/* ===========================================================
   MCPie — Homepage (hi-fi) · WARM-MINIMAL scheme
   Paper white · copper-orange accent · warm-black ink + gray.
   Grotesque + mono. Generous negative space.
   =========================================================== */

@font-face{
  font-family:'Monofonto';
  src:url('monofonto-rg.otf') format('opentype');
  font-weight:400 700;   /* single master; heavier label weights synthesize from it */
  font-style:normal;
  font-display:swap;
}

:root{
  /* scene — warm paper neutrals */
  --bg:#faf7f2;
  --bg-2:#f4efe7;
  --bg-3:#ece5da;
  --panel:#ffffff;
  --panel-2:#faf7f2;
  --hair:#e8e0d4;
  --hair-2:#d8cdbd;

  /* accent — copper orange (matches the render) */
  --ice:#e2570d;          /* primary orange (main accent) */
  --ice-bright:#ff7a2e;
  --ice-deep:#a93c06;     /* deep burnt orange */
  --spark:#ffffff;
  --navy:#1f1810;         /* warm near-black anchor */
  --navy-2:#3a2f23;
  --mesh:#7c7163;         /* neutral gray lines */

  /* ink (warm black → gray) */
  --ink:#1b150e;
  --ink-2:#5c5246;
  --ink-3:#8c8174;
  --ink-faint:#bdb3a4;

  --grot:'Figtree', system-ui, sans-serif;
  --display-font:'Archivo', 'Figtree', system-ui, sans-serif;          /* headline face — more engineered presence than Figtree */
  --mono:'Monofonto', ui-monospace, 'JetBrains Mono', monospace;       /* workhorse label mono (was Bitcount Single) */
  --mono-read:'Monofonto', ui-monospace, 'JetBrains Mono', monospace;  /* same loaded mono for all functional labels — consistent across OS */
  --mono-display:'Bitcount Single', ui-monospace, monospace;           /* dot-matrix display — reserved for large moments only */

  /* THE PAGE MEASURE — every other measure on the site derives from it (--u, --navh,
     --fig-h, the nav zone, the footer, and the side-mosaic gutters that tile outward
     from its edge). It was a flat 1180px, which made the page a fixed ribbon: at 2560
     the content was 46% of the screen with 682px of gutter EACH SIDE — more tiled
     margin than page. So it grows now, with a knee and a ceiling:
       · below ~1735px viewport  68vw < 1180  → the floor holds → NOTHING changes,
         so every laptop width keeps the exact layout it had.
       · 1735 → 2530px           68vw rules   → the grid scales with the screen.
       · above ~2530px           the 1720px ceiling holds → the module stops growing,
         for the same reason the fig stopped at eight modules: past a point a bigger
         grid is just a stretched one. The gutters take the rest, which is what they
         are for. */
  --maxw:clamp(1180px, 68vw, 1720px);
  /* header height = ONE modular unit: the nav-zone's 12-col module (zone width
     minus the 11 seam gaps, /12) plus the bar's 1px top+bottom padding — so the
     header tiles are true squares and the bar is exactly one grid row tall.
     Tracks --maxw (was a duplicated 1180) or the bar stops being one row tall the
     moment the measure moves. */
  --navh:calc(min(100vw, var(--maxw)) / 12 + 2px);
  --pad:0px;
  --glow:0 14px 50px rgba(226,87,13,.16);
}

*{box-sizing:border-box;}
html{overflow-x:hidden;}   /* clip horizontal */
/* free scrolling — no scroll-snap. scroll-margin-top keeps anchor jumps clear of the fixed header. */
.section-pad, .vcat, .footer{ scroll-margin-top:var(--navh); }
body{
  margin:0;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--grot);
  font-size:18px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
body.menu-open{overflow:hidden;}

::selection{background:rgba(226,87,13,.22); color:#fff;}

a{color:inherit;}
img{max-width:100%; display:block;}

/* subtle film grain + vignette over the whole page (+ ambient blueprint grid).
   z-index:0 → sits BELOW the render canvas so the FIG pie covers the grid cleanly */
body::after{   /* faint grain / vignette over the whole page, below the render canvas (z0) */
  content:""; position:fixed; inset:0; pointer-events:none; z-index:0;
  background:
    radial-gradient(120% 90% at 50% -10%, rgba(226,87,13,.045) 0%, transparent 45%),
    radial-gradient(130% 100% at 50% 100%, rgba(31,24,16,.035) 0%, transparent 55%);
}

/* ---------- typography ---------- */
.eyebrow{
  font:600 12px/1 var(--mono); letter-spacing:.28em; text-transform:uppercase;
  color:var(--ice); display:inline-flex; gap:10px; align-items:center;
}
.eyebrow::before{ content:""; width:22px; height:1px; background:var(--ice-deep); }
h1,h2,h3,h4{ margin:0; font-weight:500; letter-spacing:-.025em; line-height:1.02; }
.display{ font-size:clamp(38px,6.6vw,82px); font-weight:500; letter-spacing:-.035em; line-height:.98; }
.h2{ font-size:clamp(30px,4.4vw,56px); letter-spacing:-.03em; line-height:1.0; }
.h3{ font-size:clamp(21px,2.2vw,29px); letter-spacing:-.02em; }
.lede{ font-size:clamp(16px,1.5vw,20px); line-height:1.55; color:var(--ink-2); max-width:52ch; text-wrap:pretty; }   /* ~70 chars/line for readability (60ch ran ~82 in this font) */
.body{ font-size:16.5px; line-height:1.6; color:var(--ink-2); text-wrap:pretty; }
.mono{ font-family:var(--mono); }
.tag{ font:600 11px/1 var(--mono); letter-spacing:.18em; text-transform:uppercase; color:var(--ink-3); }

/* gradient ice text for select emphasis */
.ice-text{
  background:linear-gradient(180deg, var(--ice-bright), var(--ice) 60%, var(--ice-deep));
  -webkit-background-clip:text; background-clip:text; color:transparent;
}

/* ---------- layout ---------- */
.wrap{ max-width:var(--maxw); margin:0 auto; padding-left:var(--pad); padding-right:var(--pad); position:relative; z-index:2; }
section{ position:relative; z-index:2; }
.section-pad{ padding:0; }   /* gaps are filled by interstitial cornered bands instead */
.divider-row{ border-top:0; }   /* the interstitial band is the separator now */
/* interstitial cornered blocks — empty void modules filling the gaps between sections so
   the page reads as one continuous modular grid (injected by app.js). */
.interstice{ position:relative; z-index:2; }
.interstice.m-band-only{ display:none; }   /* injected divider that shows ONLY ≤1024 (desktop sections carry their own band) */
.interstice .ggrid{ grid-template-columns:repeat(12,1fr);
  /* fixed SQUARE row unit = --u (one column's width) so the mixed-span mosaic tiles cleanly.
     !important overrides the .gsec:not(.explode-section) auto-rows rule below.
     MINUS the 1px grout, so row + gap is exactly one module: n rows then measure n modules
     and the band ends on a module line. Without it every band was n modules PLUS (n-1)px,
     and that remainder is what the old bottom-padding pass paid for with a dead strip. */
  grid-auto-rows:var(--u) !important; }
/* mosaic blocks: a mix of 1×1 squares + 2×1 / 1×2 rectangles, placed by app.js makeBand() */
/* divider squares use the same rounded corner-registration brackets as every other block
   (the general .c::after rule) — no special circle. */

.seclabel{ display:flex; align-items:center; gap:14px; margin-bottom:34px; }
.seclabel .n{ font:600 12px/1 var(--mono); color:#fff; background:var(--ice); padding:5px 8px; border-radius:0; }
.seclabel .t{ font:600 12px/1.3 var(--mono); letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3); }
.seclabel .ln{ flex:1; height:1px; background:linear-gradient(90deg, var(--hair), transparent); }

/* ---------- nav — modular tile row ----------
   The bar is a full-bleed run of two-shape blocks: a seam-grey background strip
   (the bottom shape) with rounded paper tiles sitting on it (the top shapes).
   Functional elements — brand, links, readout, CTA — are FOREGROUND blocks in
   the same row; app.js fills every leftover span with ambient tiles (.nav-amb),
   so the bar reads as row zero of the page's mosaic, edge to edge. */
.nav{
  position:fixed; top:0; left:0; right:0; z-index:100;   /* above EVERYTHING — no section, FIG, or
                                                            decorative layer may paint over the bar */
  display:flex; align-items:stretch; gap:0;
  /* EXACTLY one modular unit tall (the zone's 12-col module + the 1px paddings) —
     the same datum the stage window pins to (app.js reads the live height) */
  height:var(--navh); padding:0;
  /* OPAQUE BAR BASE (supersedes the floating-zone era's transparent bar): the bar is a
     full-width grounded module row now, and the base seam guarantees that NO pixel of the
     row can show scrolling content — flex-boundary hairlines, DPR rounding slits, and any
     translucency in child tiles all land on seam instead of the page. */
  background:var(--seam);
  border-bottom:0;
}
.nav > *, .nav-zone > *{ border-radius:22px; min-width:0; }
/* content zone: a 12-column module row on the SAME grid as the sections below
   (repeat(12,1fr) + 1px gaps across --maxw) — every block spans whole modules:
   brand 2 · ambient 5 · contributing 3 · querying 2. The gutters either side
   continue the module pitch out to the screen edge. */
.nav-zone{ flex:0 1 var(--maxw); min-width:0; display:grid;
  grid-template-columns:repeat(12,1fr); gap:1px; border-radius:0;
  background:var(--seam); padding:1px; }
.nav-zone > .nav-amb{ display:grid; grid-auto-flow:column; grid-auto-columns:1fr; gap:1px; }
/* ---- corner tabs: the bar melts into the page at the zone's bottom corners ----
   The two-square element: a BACKGROUND square (the zone's seam ground continuing
   --ftab below the bar) with a FOREGROUND square whose one corner is FULLY rounded
   (radius = the whole side, matching the tiles' 22px block radius). The foreground
   square is cut as a transparent quarter-circle (radial-gradient) rather than painted
   page-color: the nav is fixed, so scrolled content passes under these corners and
   must show through the round. Left tab mirrors right. */
.nav-zone{ position:relative; --ftab:22px; }
.nav-zone::before, .nav-zone::after{
  content:""; position:absolute; top:100%; width:var(--ftab); height:var(--ftab);
  pointer-events:none; }
/* FINAL SHAPE (user): the foreground square is CUT OUT of the background square —
   only the seam fillet remains; the quarter-circle region is a true hole and the
   page shows through it. */
.nav-zone::before{ left:0;
  background:radial-gradient(circle var(--ftab) at 100% 100%, transparent calc(var(--ftab) - .5px), var(--tab-ink, var(--seam)) var(--ftab)); }
.nav-zone::after{ right:0;
  background:radial-gradient(circle var(--ftab) at 0 100%, transparent calc(var(--ftab) - .5px), var(--tab-ink, var(--seam)) var(--ftab)); }
/* the tabs recolor to the section ground passing beneath them: app.js sets html.tabs-ice
   while the VERTICALS section (orange field) is under the bar — seam fillet reads wrong
   against copper, so the fillet itself goes copper there */
html.tabs-ice .nav-zone{ --tab-ink:var(--ice); }
/* ONE action in the bar (user request): a TIGHT brand block, an empty block, then the
   socials beside the dashboard button. Row totals 12:
   brand 2 · empty 4 · Telegram 1 · X 1 · GitHub 1 · dashboard 3 */
.nav-zone > .nav-amb--1{ display:grid; grid-column:span 4; }   /* the empty paper block */
.nav-zone .brand{ grid-column:span 2; }
.nav-zone > .nav-ico{ grid-column:span 1; }   /* X · GitHub — square modules next to the CTA */
.nav .nav-zone .navcta{ display:contents; }   /* the button joins the 12-col grid directly */
.nav-zone .navcta .btn.dark{ grid-column:span 3; justify-content:center; }
/* TWO-SHAPE like the zone: the gutter carries its own seam GROUND (1px top/bottom inset,
   1px gaps showing grout) so the flanking tiles are real background+foreground modules —
   the whole bar reads as ONE continuous grounded row, zone + gutters. */
.nav-gutter{ flex:1 0 0; display:flex; gap:1px; overflow:hidden;
  background:var(--seam); padding:1px 0; }
.nav-gutter:first-child{ justify-content:flex-end; }   /* tiles march from the zone edge; the outermost clips */
/* ambient filler tiles — same paper-on-seam module as the divider bands */
.nav-amb i{ background:var(--paper-fill); border-radius:22px; }
.nav .brand{ display:flex; align-items:center; gap:11px; font-weight:600; letter-spacing:-.02em; font-size:17px; text-decoration:none;
  background:var(--paper-fill); padding:0 26px; }
.brandmark{ width:26px; height:26px; flex:none; }
/* CTA cell: transparent wrapper — the buttons ARE the blocks (full bar height).
   Primary = copper block; secondary "Start querying" = dark-ink block. */
.nav .navcta{ display:flex; gap:1px; align-items:stretch; }
.nav .navcta .btn{ align-self:stretch; border-radius:22px; box-shadow:none; padding:0 30px; }
/* the global .btn::after corner-registration brackets (dark 8px-radius ticks) read as a stray
   black stroke on the bar's 22px paper tiles — the nav CTAs are plain modules, no brackets */
.nav .navcta .btn::after{ content:none; }
/* the global CTA rev (.btn.pri.sm radius 8, later in the file) must not undercut
   the bar's block radius — copper matches the dark button exactly */
.nav .navcta .btn.pri.sm{ border-radius:22px; }
/* the two CTAs are ONE style, two colorways — kill the global .btn.pri hover
   shadow/press-transform inside the bar so copper behaves exactly like dark */
.nav .navcta .btn:hover, .nav .navcta .btn:active{ box-shadow:none; transform:none; }
.nav .navcta .btn.dark{
  background:#262626; border-color:#262626; color:#f4ede4;
  letter-spacing:.14em;
}
.nav .navcta .btn.dark:hover{ background:#1b1b1b; color:var(--ice-bright); }
/* scrolled: the CTAs de-color into the tile field (paper block, ink type) and
   only light up again under the cursor */
.nav.solid .navcta .btn{ background:var(--paper-fill); border-color:transparent; color:var(--ink); }
.nav.solid .navcta .btn.pri:hover{ background:var(--ice); border-color:var(--ice); color:#fff; }
.nav.solid .navcta .btn.dark:hover{ background:#262626; border-color:#262626; color:#f4ede4; }
/* CTAs = light paper FOREGROUND blocks (same two-shape tile as brand/ambient/
   icons) carrying only the label — no colored fill in ANY state; primary keeps
   the orange type, secondary goes ink. Hover just brightens the type. */
.nav .navcta .btn.pri, .nav .navcta .btn.dark,
.nav.solid .navcta .btn.pri, .nav.solid .navcta .btn.dark{
  background:var(--paper-fill); border-color:transparent;
}
.nav .navcta .btn.pri, .nav.solid .navcta .btn.pri{ color:var(--ice); }
.nav .navcta .btn.dark, .nav.solid .navcta .btn.dark{ color:var(--ink); }
.nav .navcta .btn.pri:hover, .nav.solid .navcta .btn.pri:hover{ background:var(--paper-fill); border-color:transparent; color:var(--ice-bright); }
.nav .navcta .btn.dark:hover, .nav.solid .navcta .btn.dark:hover{ background:var(--paper-fill); border-color:transparent; color:var(--ice); }
/* DASHBOARD CTA — built exactly like the hero's: a paper module carrying an orange
   --field-h pill (radius 12) with a white mono-uppercase label. Placed after the
   paper-block rules above so it wins at equal specificity; keeps .dark for the zone's
   grid span + the <=760 hide. */
.nav .navcta .btn.btn--dash,
.nav.solid .navcta .btn.btn--dash{
  background:var(--paper-fill); border-color:transparent;
  padding:0 clamp(10px,1vw,14px);
}
.nav .navcta .btn.btn--dash .cta-chip{
  width:100%; height:var(--field-h); border-radius:12px;
  background:var(--ice); color:#fff;
  display:flex; align-items:center; justify-content:center; gap:10px;
  font:600 clamp(10.5px,1vw,12.5px)/1 var(--mono-read);
  letter-spacing:.14em; text-transform:uppercase;
  transition:background .18s ease;
}
.nav .navcta .btn.btn--dash .cta-chip .arr{ font:600 14px/1 var(--grot); opacity:1; transition:transform .18s ease; }
.nav .navcta .btn.btn--dash:hover .cta-chip{ background:var(--ice-deep); }
.nav .navcta .btn.btn--dash:hover .cta-chip .arr{ transform:translateX(3px); }
/* square single-module icon tiles (X · GitHub) — same paper module as the
   ambient tiles, just carrying an icon (no filled button background) */
.nav .nav-zone .nav-ico{
  width:auto; height:auto; align-self:stretch; border:0; border-radius:22px;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--paper-fill); color:var(--ink-3); transition:.16s;
}
.nav .nav-zone .nav-ico svg{ width:18px; height:18px; }
.nav .nav-zone .nav-ico:hover{ color:var(--ice); }
/* "coming soon": keep the tile full-strength paper — only the icon reads muted */
.nav .nav-zone .nav-ico[aria-disabled="true"]{ opacity:1; }
/* social icon links (X · GitHub) in the header */
.nav-ico{ display:inline-flex; align-items:center; justify-content:center; width:36px; height:36px;
  color:var(--ink-2); border:1px solid var(--seam); background:rgba(255,255,255,.45); transition:.16s; }
.nav-ico svg{ width:17px; height:17px; fill:currentColor; }
.nav-ico:hover{ color:var(--ice); border-color:rgba(226,87,13,.45); background:rgba(226,87,13,.08); }

/* ---------- buttons ---------- */
.btn{
  font:600 14.5px/1 var(--grot); letter-spacing:-.01em;
  padding:13px 19px; border-radius:0; border:1px solid var(--hair-2);
  background:#fff; color:var(--ink); cursor:pointer; position:relative;
  display:inline-flex; gap:9px; align-items:center; text-decoration:none;
  transition:.18s; white-space:nowrap;
}
/* rounded corner-registration brackets at the 4 corners (same blueprint motif as the blocks) */
.btn::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  border:1px solid var(--bp-tick); border-radius:8px;
  -webkit-mask:
    linear-gradient(#000,#000) 0 0/11px 11px no-repeat,
    linear-gradient(#000,#000) 100% 0/11px 11px no-repeat,
    linear-gradient(#000,#000) 0 100%/11px 11px no-repeat,
    linear-gradient(#000,#000) 100% 100%/11px 11px no-repeat;
          mask:
    linear-gradient(#000,#000) 0 0/11px 11px no-repeat,
    linear-gradient(#000,#000) 100% 0/11px 11px no-repeat,
    linear-gradient(#000,#000) 0 100%/11px 11px no-repeat,
    linear-gradient(#000,#000) 100% 100%/11px 11px no-repeat;
}
.btn.pri::after{ border-color:rgba(255,255,255,.6); }   /* light brackets read on the copper fill */
.btn:hover{ border-color:var(--ice-deep); background:rgba(226,87,13,.07); transform:translateY(-1px); }
.btn .arr{ font:500 13px/1 var(--mono); opacity:.8; transition:transform .18s; }
.btn:hover .arr{ transform:translateX(3px); }
.btn.pri{
  background:linear-gradient(180deg, var(--ice-bright), var(--ice)); color:#fff; border-color:transparent;
  box-shadow:0 6px 22px rgba(226,87,13,.30);
}
.btn.pri:hover{ box-shadow:0 10px 28px rgba(226,87,13,.44); background:linear-gradient(180deg,#ff8a45,var(--ice)); }
.btn.sm{ padding:10px 14px; font-size:13.5px; }

/* ---------- HERO (minimal · copy left, render breathes right) ---------- */
/* full-page background canvas — the render sequence, behind all content */
/* the pie's canvas: the rounded paper TOP shape inside the #stageWin frame — CSS
   radius clips the clean 3D render, the seam ground peeks at the corners */
/* the render surface is the FACE of the FIG's two-shape, so it obeys the same rule as every
   other module on the page: one pixel smaller than its ground on all four sides, radius
   22 − 1 for the inset. Before this the canvas sat at inset:0 and the grey ground could only
   peek at the corners, which left the 3D window as the one block on the page without an
   edge. The renderer sizes itself from the canvas's own client box, so taking 2px off it
   costs nothing but the frame it draws.
   SIZE IT EXPLICITLY. `inset:1px` with width/height:auto does NOT work on a canvas: it is a
   replaced element, so auto resolves to its INTRINSIC size (the width/height attributes the
   renderer writes), the right/bottom insets are dropped as over-constrained, and the two feed
   each other — the canvas grew past 26 million pixels a side on the first measurement. */
#bgCanvas{ position:absolute; top:1px; left:1px;
  width:calc(100% - 2px); height:calc(100% - 2px); display:block;
  pointer-events:none; border-radius:21px; background:var(--bg); }
/* content sections sit above the sequence with a warm-paper tint so text stays
   crisp while the copper object still reads faintly through the whole page */
.section-pad, .footer{ position:relative; z-index:2; background:rgba(250,247,242,.85); }
/* (dead hero-section / hero-inner / hero-copy-left layout removed — the page uses the #stack grid) */
.cta-row{ display:flex; gap:13px; flex-wrap:wrap; justify-content:flex-start; margin-top:2px; }
/* W/V/Q band cell labels (Weave / Verify / Query — used in the #stack hero band) */
.hband-note{ margin:0; font-size:13.5px; line-height:1.5; color:var(--ink-2); max-width:26ch; text-wrap:pretty; }
.hband-idx{ font:600 11px/1 var(--mono); letter-spacing:.1em; color:var(--ice); }
.hband-name{ display:block; margin:9px 0 6px; font-weight:650; font-size:15.5px; letter-spacing:-.01em; color:var(--ink); }
@media (max-width:1080px){ .hband-note{ max-width:40ch; } }

/* ---------- THE STACK (normal flow; sequence plays in the page bg) ---------- */
.explode-section{ position:relative; z-index:2; }   /* padding comes from .stage */
/* (dead .explode-inner / .explode-head / .layer-labels / .layer-card removed — stack uses .ggrid cells) */
/* flywheel close-line (the old .flywheel/.flystep grid is dead — section uses a .subgrid) */
.flywheel-close{ margin-top:26px; font-size:clamp(17px,1.8vw,22px); color:var(--ink); max-width:60ch; line-height:1.4; }
.flywheel-close .ice-text{ font-weight:500; }

/* verticals lattice section */
.lattice-stage{ position:relative; border:1px solid var(--hair); border-radius:0; overflow:hidden; background:radial-gradient(95% 95% at 50% 25%, #ffffff, #f6efe5 70%, #ece2d4); box-shadow:inset 0 1px 0 #fff, 0 18px 50px rgba(31,24,16,.08); }
.lattice-stage canvas{ display:block; width:100%; height:auto; }
.lattice-floatlabel{ position:absolute; top:18px; left:20px; z-index:2; pointer-events:none; }
.lattice-readout{ position:absolute; bottom:18px; left:20px; right:20px; z-index:2; display:flex; align-items:center; gap:14px; pointer-events:none; }
.lattice-readout .nm{ font:600 15px/1 var(--mono); color:var(--ice); min-height:18px; transition:.15s; }
.lattice-readout .hint{ margin-left:auto; font:500 12px/1 var(--mono); color:var(--ink-3); }

/* footer */
.footer{ border-top:1px solid var(--hair); padding:clamp(56px,8vh,90px) 0 0; position:relative; z-index:2; }   /* no bottom padding — the page ends exactly at the panel's bottom edge */
/* one consolidated footer block with brand + 3 columns + bottom copyright row.
   Two-shape colors (background/border-radius override) live further down, after the
   general .c rule — same source-order requirement as the #stack hero override below. */
.footer-inner{ padding:clamp(44px,6vh,68px) clamp(24px,3vw,44px) var(--u); display:flex; flex-direction:column; gap:clamp(28px,4vh,44px); }   /* bottom air under the wordmark = exactly ONE module */
/* the brand row and the link columns run to the SAME left and right edges as the animated
   wordmark below them — one measure for the whole footer instead of the content sitting
   ~25px wider than the mark on each side, which read as two different boxes stacked. Both
   are width:100% under the cap, so below 1052px they simply fill the panel together. */
.footer-inner{ --wm-w:1052px; }
/* TWO CLUSTERS, not four spread columns. The three link columns were 1fr each, so they were
   strung out across the whole width with the brand — every gap the same, nothing grouped.
   They are one group now, sized to their content and pushed to the footer's right edge, with
   a TIGHTER gap between them than between them and the brand: the eye reads "who we are" on
   the left and "where to go" on the right, instead of four equal things in a row. */
.footer-top{ display:flex; justify-content:space-between; align-items:flex-start;
  gap:clamp(28px,3.4vw,60px);
  width:100%; max-width:var(--wm-w); margin-inline:auto; }
/* wide gaps between the three columns (user request). It also closes the void: the group
   grows leftward from the footer's right edge, so the tagline and the links end up nearer
   each other than they were when the columns were packed tight. */
.fcols{ display:flex; align-items:flex-start; gap:clamp(40px,5.6vw,104px); }
/* a wider measure than 38ch on purpose: the void between the tagline and the link group was
   ~250px, which is what made them read as two things that had nothing to do with each other.
   The text now reaches further right and the group still ends on the footer's right edge. */
.fbrand-block{ display:flex; flex-direction:column; max-width:52ch; }
/* the tagline reads as two rows, not one long line: 52ch is the block's width, so the
   sentence needs its own narrower measure. `balance` splits the two lines evenly instead
   of leaving a three-word orphan on the second. */
.footer .ftag{ max-width:33ch; text-wrap:balance; }
/* NO BLOCK behind the footer brand (user request): it used to be a two-shape chip — a
   #343434 ground with a rounded #262626 face — but the footer panel is already that same
   near-black, so the chip only drew a faint rectangle around the mark. The logo stands on
   the panel by itself. */
.fbrand-block .brand{ align-self:flex-start; margin-bottom:14px; }
.footer h5{ font:600 11px/1 var(--mono); letter-spacing:.16em; text-transform:uppercase; color:var(--ink-3); margin:0 0 14px; }
.footer .fcol a{ display:block; padding:7px 0; color:var(--ink-2); text-decoration:none; font-size:14.5px; transition:.15s; }
.footer .fcol a:hover{ color:var(--ice); }
.footer .fbottom{ margin:0; padding-top:clamp(20px,2.4vh,28px); border-top:1px solid var(--seam); display:flex; justify-content:space-between; gap:16px; flex-wrap:wrap; align-items:center; }
.footer .fbottom .mono{ font-size:12px; color:var(--ink-3); letter-spacing:.04em; }

/* ---------- responsive ---------- */
/* narrow: the two clusters stack, and the link columns share the width evenly between them
   (the old rules aimed at a 4-column grid that no longer exists) */
@media (max-width:1000px){
  .footer-top{ flex-direction:column; }
  .fbrand-block{ max-width:none; }
  .fcols{ width:100%; }
  .fcols .fcol{ flex:1 1 0; min-width:0; }
  /* CENTRED brand (user request). Once .footer-top stacks, the mark and the tagline sit alone
     above three link columns that all start at the same left edge — left-aligned, the brand
     block just read as a fourth column of that set rather than as the thing they hang under.
     The LINKS stay left-aligned on purpose: a centred list has no edge to run your eye down. */
  /* ONE ROW (user request): the mark on the left, the tagline beside it, instead of the mark
     stacked over a centred paragraph.
     align-self:stretch is still load-bearing. .footer-top keeps align-items:flex-start from the
     desktop rule, so once it turns into a column its children shrink to their content width and
     sit against the left edge — without the stretch the row would be as narrow as its contents
     and the tagline would have nothing to wrap into. */
  .fbrand-block{ align-self:stretch; flex-direction:row; align-items:center;
    gap:clamp(12px,3.5vw,20px); text-align:left; }
  .fbrand-block .brand{ align-self:center; margin-bottom:0; flex:none; }
  /* the ftag carries margin-top:14px as an INLINE style in the markup, which no selector can
     outrank — it was spacing the paragraph off the mark when they were stacked, and in a row it
     just pushes the text off-centre. !important is the override, not a preference. */
  /* TWO ROWS (user request). max-width:none let the paragraph take the whole rest of the row
     and unravel into five short lines. 33ch is the measure it was written to — it splits it
     as "Open protocol. Verifiable data." / "Built by the people who supply it.", which is the
     break the sentence already has — and text-wrap:balance from the base rule then evens the
     two. flex-basis stays 0 so it still shares the row; max-width caps what it takes. */
  /* smaller (user request) so the 33ch measure fits beside the mark on a phone. Both the
     margin and the size need !important: the markup carries font-size:14.5px and
     margin-top:14px as INLINE styles, which no selector outranks. The measure is in ch, so
     it shrinks with the type — 33ch at 12.5px is ~206px against ~239 at 14.5. */
  .footer .ftag{ margin:0 !important; font-size:12.5px !important; line-height:1.45 !important;
    max-width:33ch; flex:1 1 0; }
  /* the row lines its left edge up with the link columns below it rather than with the footer
     padding, so the mark and "The network" start on the same rule */
  .fbrand-block, .fcols{ box-sizing:border-box; padding-inline:clamp(14px,5vw,32px); }
  /* AIR ABOVE AND BELOW THE BRAND ROW (user request). It has to be padding on the row itself,
     not a bigger gap on .footer-top: the top of the row is the top of the footer's content, so
     a gap has nothing above it to push against and only the space UNDER the row would grow.
     .footer-inner's own top padding stays what it is — this stacks on it. */
  .fbrand-block{ padding-block:clamp(20px,5.5vw,40px); }
  /* …and the columns get their own inset ON TOP of .footer-inner's clamp(24px,3vw,44px), so the
     links sit inside the block instead of running to its edge. border-box or the 100% width
     above would push the padding outside and overflow the footer. */

}
@media (max-width:760px){
  body{ font-size:16px; }
}
@media (max-width:460px){
  /* two columns on mobile (user request). A BASIS of half the row rather than flex-grow:
     with grow the odd third column stretched across the whole second row and read as a
     different kind of thing than the two above it. */
  .fcols{ flex-wrap:wrap; gap:26px 20px; }
  .fcols .fcol{ flex:0 1 calc(50% - 10px); }
}

/* ============================================================
   BLUEPRINT / TECHNO-BRUTALIST LAYER — 2026-06-13
   Architectural-grid restyle of the warm-minimal base: visible
   blueprint grid, corner registration ticks, monospace labels,
   heavier geometric titles, flat surfaces, copper restrained to
   markers + CTAs + active states. Brand palette, content and
   functionality (3D scene, lattice, scroll) all unchanged.
   ============================================================ */
:root{
  --bp-line:rgba(31,24,16,.10);     /* ambient grid hairline — unified line color */
  --bp-edge:rgba(31,24,16,.10);     /* container / page-frame rule */
  --bp-tick:rgba(31,24,16,.10);     /* corner registration tick */
  --bp-cell:clamp(58px,6vw,96px);   /* blueprint grid module */
}


/* solid content sections get their own faint grid over the paper tint, so the
   blueprint reads continuously while text stays crisp; seams = modular blocks */
.section-pad, .footer, .explode-section{
  background:
    repeating-linear-gradient(90deg, var(--bp-line) 0 1px, transparent 1px var(--bp-cell)),
    repeating-linear-gradient(0deg,  var(--bp-line) 0 1px, transparent 1px var(--bp-cell)),
    rgba(250,247,242,.9);
}

/* 3 — flat surfaces: strip the glossy gradients, soft shadows and lift hovers;
   brutalist state changes are flat fills / border swaps, not elevation */
.btn{ font:600 12px/1 var(--mono-read); text-transform:uppercase; letter-spacing:.1em; padding:14px 18px; }
.btn .arr{ font-family:var(--mono-read); opacity:1; }
.btn:hover{ transform:none; }
.btn.pri{ background:var(--ice); border-color:var(--ice); box-shadow:none; }
.btn.pri:hover{ background:var(--ice-deep); box-shadow:none; }
.btn.sm{ font-size:11px; padding:11px 14px; }

/* 4 — heavier, engineered geometric titles + monospace UI chrome */
.display{ font-weight:700; letter-spacing:-.045em; }
.h2{ font-weight:700; letter-spacing:-.035em; }
.h3{ font-weight:650; letter-spacing:-.02em; }
/* headline face (Archivo) — body, lede and labels stay Figtree / Monofonto */
.display, .h2, .h3{ font-family:var(--display-font); }
/* ===== header — instrument rail ===== */
/* brand — framed mark + mono wordmark, centered in its merged block */
/* the wordmark is the DOT-MATRIX display face, the same one the footer logo uses — the
   brand should read as one thing in both places, and the functional label mono (--mono-read)
   made the header's MCPIE look like a UI label rather than the mark it sits next to */
.nav .brand{ font:400 17px/1 var(--mono-display); letter-spacing:.05em; text-transform:uppercase; color:var(--ink); gap:9px;
  justify-content:center; }
/* bare mark, no chip frame (user request) — just the woven pie, larger */
.brandmark{ width:34px; height:34px; padding:0; border:0; background:transparent; box-sizing:content-box; }

/* live section readout — its own paper block mid-row (ambient tiles flex around it).
   app.js keeps its text on the current section (SEC.0X — NAME), so the bar
   participates in the scroll narrative instead of sitting above it. */
.nav-rec{
  flex:none; display:flex; align-items:center; gap:9px; pointer-events:none;
  background:var(--paper-fill); padding:0 26px;
  font:600 10.5px/1 var(--mono-read); letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3);
}
.nav-rec .dot{ width:7px; height:7px; background:var(--ice); flex:none; animation:recpulse 2.4s ease-in-out infinite; }
@keyframes recpulse{ 0%,100%{ opacity:.45; } 50%{ opacity:1; } }
@media (prefers-reduced-motion:reduce){ .nav-rec .dot{ animation:none; } }
/* below 1180 the readout block steps out — ambient tiles take its span */
@media (max-width:1180px){ .nav-rec{ display:none; } }
/* mobile rail: links live in the footer below 760; the bar condenses to brand + CTA */
@media (max-width:760px){ .nav-links{ display:none; } }
@media (max-width:760px){
  /* the dark button IS the dashboard CTA now — the bar's only action, so it must NOT be
     hidden here (this rule used to drop the secondary of a two-CTA pair). Icons stay too. */
  /* narrow: drop the 12-col module row for a simple flex rail (brand · tiles · CTA) */
  .nav-zone{ display:flex; }
  .nav-zone > .nav-amb{ display:flex; flex:1 1 0; }
  .nav-zone > .nav-amb i{ flex:1 1 0; }
  .nav .nav-zone .navcta{ display:flex; }
  /* SQUARE link tiles (user request). On the 12-col grid the icons span one module and the bar is
     one module tall, so they came out square for free. This flex rail has no modules: the icons are
     stretched flex items, so their HEIGHT is the bar's content height while their width was content
     -sized — 18px of svg plus centring, a tall sliver. aspect-ratio turns that definite height into
     an equal width, which keeps them square at whatever height the bar is (65px at 760, 62px under
     the 64px rule below) instead of pinning a number that would go stale. flex-shrink stays at 1:
     on a very narrow phone something has to give, and a slightly-off tile beats an overflowing bar. */
  .nav .nav-zone .nav-ico{ flex:0 1 auto; aspect-ratio:1; }
  /* THE BUTTON EATS THE GAP TILE (user request). .nav-amb--gap exists to keep the DESKTOP row
     at a full 12 columns — brand 2 + filler 4 + 2 icons + this 1 + CTA 3 — and it still does,
     on the grid above 760. Down here there are no columns to make up, so the tile is pure
     spacing between the GitHub tile and the bar's only action. Hiding it frees that width, and
     growing the CTA is what claims it: .navcta is the flex ITEM of the rail (it is display
     :contents on the grid, so this only applies here) and the button has to grow with it or it
     would sit content-sized inside a stretched wrapper. The remaining filler keeps flex:1 1 0,
     so the two now split the free space instead of the two fillers splitting it. */
  .nav-zone > .nav-amb--gap{ display:none; }
  .nav .nav-zone .navcta{ flex:1 1 auto; }
  .nav .nav-zone .navcta .btn{ flex:1 1 auto; }
}
@media (max-width:640px){
  .nav{ height:64px; }
  .nav > *, .nav-zone > *, .nav-amb i, .nav .navcta .btn, .nav .navcta .btn.pri.sm{ border-radius:16px; }
  .nav-zone{ border-radius:0; --ftab:16px; }   /* corner tabs track the 16px tile radius tier */
  .nav .brand{ padding:0 16px; }
  .nav .navcta .btn.pri.sm{ padding:0 16px; letter-spacing:.08em; }
}
@media (max-width:400px){ .nav .brand{ font-size:0; gap:0; } }

/* 5 — flat copper section index marker (square, not rounded) + sharper rule */
.seclabel .n{ border:1px solid var(--ice); }
.seclabel .ln{ background:linear-gradient(90deg, var(--bp-edge), transparent); }

/* 6 — brand moment: full-bleed segmented wordmark closing the footer */
/* MCPIE wordmark — built from the same two-shape module as the divider tiles: a 5x7
   dot-matrix letterform where each "on" pixel is a background square (seam/orange) with
   an inset rounded square (orange/dark-gray) on top. Colored by ROW: the upper 3 rows
   are orange-on-light-gray, the lower 4 rows flip to dark-gray-on-orange — a playful
   two-tone split rather than a single flat color. */
.footer-wordmark{
  margin-top:clamp(40px,7vh,90px); padding:0 var(--pad);
  overflow:hidden; user-select:none; position:relative;
  display:flex; justify-content:center;
}
.footer-wordmark .wm-svg{ width:100%; max-width:var(--wm-w, 1052px); height:auto; display:block; }   /* the measure the footer-top matches */
/* letters (wm-a/wm-b): strict 2-color palette — dark grey (#262626, footer bg) and bright
   orange (--ice-bright). wm-a's light-grey background (paired with orange fg) became dark
   grey, so wm-a (dark bg / orange fg) and wm-b (orange bg / dark fg) are exact inverses. */
.footer-wordmark .wm-a rect{ fill:#262626; }
.footer-wordmark .wm-a rect.fg{ fill:var(--ice-bright); }
.footer-wordmark .wm-b rect{ fill:var(--ice-bright); }
.footer-wordmark .wm-b rect.fg{ fill:#262626; }
/* ambient filler blocks scattered around the letters (empty grid cells, randomly chosen) —
   kept on the original 3-color palette (light grey + dark grey); out of scope for the
   letters-only 2-color request above. */
.footer-wordmark .wm-void rect{ fill:#343434; }
/* letter pixels LOOP THROUGH THE MODULE TYPES — every pixel is one module that
   transitions to the next in the family and back around:
   orange square → orange dot → inverted (dark-on-orange) square → inverted dot.
   Both layers of the two-shape pixel are driven together (the bottom rect flips
   its ground, the top rect flips its ink and rounds/squares its corners); app.js
   phases each pixel by grid position, with wm-b pixels half a cycle ahead so the
   familiar mixed two-tone look is the resting texture. */
/* palette-driven so ONE keyframe pair serves both the dark footer and the light
   hero: --wm-ground/--wm-ink resolve at element scope. The footer sets neither, so
   the fallbacks (#262626 / --ice-bright) keep it byte-identical; the hero overrides
   them with a paper-safe pair (see .hero-wordmark) so the two-tone flip survives on
   a light ground without strobing near-black. */
@keyframes wmCycleBg{
  0%, 40%{ fill:var(--wm-ground,#262626); }
  50%, 90%{ fill:var(--wm-ink,var(--ice-bright)); }
  100%{ fill:var(--wm-ground,#262626); }
}
@keyframes wmCycleFg{
  0%, 15%{ fill:var(--wm-ink,var(--ice-bright)); rx:2px; }     /* module 1: ink square */
  25%, 40%{ fill:var(--wm-ink,var(--ice-bright)); rx:4.8px; }  /* module 2: ink dot */
  50%, 65%{ fill:var(--wm-ground,#262626); rx:2px; }          /* module 3: inverted square */
  75%, 90%{ fill:var(--wm-ground,#262626); rx:4.8px; }        /* module 4: inverted dot */
  100%{ fill:var(--wm-ink,var(--ice-bright)); rx:2px; }
}
/* dot-phase variant (the SAME 4-module cycle rotated -25% so its 0% frame is a FILLED
   ink DOT instead of a filled square). Paired with wmCycleFg via cell parity so the
   entrance holds a checkerboard of filled circles + filled rounded cubes (backwards
   fill-mode shows this 0% frame until each cell's staggered delay elapses), then both
   roll into the identical breathing loop. */
@keyframes wmCycleFg2{
  0%, 15%{ fill:var(--wm-ink,var(--ice-bright)); rx:4.8px; }   /* ink dot (held at start) */
  25%, 40%{ fill:var(--wm-ground,#262626); rx:2px; }          /* inverted square */
  50%, 65%{ fill:var(--wm-ground,#262626); rx:4.8px; }        /* inverted dot */
  75%, 90%{ fill:var(--wm-ink,var(--ice-bright)); rx:2px; }    /* ink square */
  100%{ fill:var(--wm-ink,var(--ice-bright)); rx:4.8px; }
}
@keyframes wmCycleBg2{
  0%, 15%{ fill:var(--wm-ground,#262626); }
  25%, 65%{ fill:var(--wm-ink,var(--ice-bright)); }
  75%, 100%{ fill:var(--wm-ground,#262626); }
}
.footer-wordmark .wm-void rect.fg{ fill:#262626; }

/* 7 — keep the corner ticks from being clipped where it matters */
.lattice-stage{ overflow:visible; }

/* ============================================================
   GRID COMPOSITION SYSTEM — 2026-06-13 (whole-page blueprint rebuild)
   The page is BUILT as a 12-col modular grid: content placed into
   explicit bordered cells, asymmetric composition, empty cells as
   measured negative space, the live 3D pie shows through transparent
   "window" cells. Supersedes the ambient-grid pass above.
   ============================================================ */
:root{
  /* row module = the divider's minimal block: one 12-col square (= one column's
     width). Single source of truth — the dividers, the hero's 12-col grid, and any
     fixed-row grid all snap to this same unit. */
  --u:calc((min(100vw, var(--maxw)) - 2 * var(--pad)) / 12);
  --paper-fill:rgba(250,247,242,.93);
  --seam:#e1dbd1;   /* unified line + corner-tab color — warm light grey, in paper family */
}

.section-pad, .footer, .explode-section{ background:transparent; }
/* continuous 12-col vertical ruling behind everything (canvas 0 < ruling 1 < content 2) */
body::before{
  content:""; position:fixed; z-index:1; pointer-events:none; top:0; bottom:0;
  left:50%; transform:translateX(-50%);
  width:calc(min(100%, var(--maxw)) - 2 * var(--pad));
  background:repeating-linear-gradient(90deg, var(--bp-edge) 0 1px, transparent 1px calc(100%/12));
  display:none;   /* background grid hidden for now — remove this line to restore */
}
@media (max-width:860px){ body::before{ display:none; } }

/* the grid container, reused per section */
.gsec{ position:relative; z-index:2; }
.gwrap{ max-width:var(--maxw); margin:0 auto; padding:0 var(--pad); position:relative; z-index:2;
  container-type:inline-size; }   /* query container so --u below tracks the REAL content width */
.ggrid{
  /* row module = TRUE column width. cqw is 1% of the wrapper's content box (excludes the
     scrollbar, unlike 100vw). NO grout to subtract any more: the seam belongs to each block
     now (see the outline rule below), the grid itself has no gaps, so a column is exactly a
     twelfth and grid-auto-rows == column width — a real square, with no remainder to carry
     through every span, band and card calculation on the page. */
  --u:calc(100cqw / 12);
  display:grid; grid-template-columns:repeat(12,1fr); grid-auto-rows:var(--u);
  gap:0;
}
/* STROKES ONLY ON BLOCKS — no section/grid frame anywhere. Every block draws its OWN full
   border (the hero included); cell interiors stay transparent where the live 3D shows through. */
.ggrid > *{ border:1px solid var(--seam); }
.explode-section .ggrid > .c--void{ border:0; }   /* the hero scroll-runway void stays invisible */

/* the grid keeps the seam GROUND even though it no longer has gaps. With gap:0 the only
   place it can still show is behind each block's rounded CORNERS — which is precisely the
   grey the gutter tiles have (their SVG is a square seam rect under a rounded paper one),
   and what the page lost when the gaps went: the little concave wedges where four modules
   meet. Blocks touch, so the ground never reads as grout; the 1px outline does that. */
.gsec:not(.explode-section) .ggrid{ grid-auto-rows:auto; gap:0; background:var(--grout, var(--seam)); }
/* TWO-SHAPE, PER BLOCK. The seam used to be the grid's background showing through 1px gaps,
   which meant a block only had an edge where it had a NEIGHBOUR — the top and bottom of every
   band came out unframed, and two stacked sections met with no line at all. Now every block
   carries its own: the face is one pixel smaller than the block on all four sides, so the
   edge exists whether or not anything is next to it. Between neighbours the two edges add up
   to 2px, at the outside 1px — which is exactly the rhythm the gutter tiles have always had,
   so the content grid and the gutters finally read as one grid.
   OUTLINE, not border or inset shadow: it paints above the content, so blocks whose child
   fills them edge to edge (the terminal, the 3D window, the catalog panels) keep their edge
   instead of having it covered. outline-offset:-1px pulls it inside, and modern engines
   follow border-radius with it. */
.gsec:not(.explode-section) .ggrid > *{ border:0; }
.gsec:not(.explode-section) .c,
.gsec:not(.explode-section) .sc{
  outline:1px solid var(--grout, var(--seam)); outline-offset:-1px;
}
/* dark or coloured groups name their own grout instead of wearing the paper seam */
#catalog{ --grout:var(--ice); }
/* the footer panel already paints its own ground + rounded face, and a paper-seam hairline
   on it would read as a scratch. Specificity has to beat .gsec:not(.explode-section) .c —
   :not() carries its argument's weight, so that selector is (0,3,0), not (0,2,0). */
.footer.gsec:not(.explode-section) .footer-inner{ outline:0; }
/* auto-injected divider void cells were transparent (page paper); on the gray grid
   they must carry an explicit paper fill so they read white, not gray. */
.interstice .ggrid > .c{ background:var(--bg); }
/* #vision — MODULAR outer grid: rows are the square unit (--u) so every block is a
   whole number of 83.5px modules. Cell spans (band 2 · manifesto 6 · right 8) keep
   the section 8 squares tall. Overrides the .gsec auto-rows via #id specificity. */
#vision .ggrid{ grid-auto-rows:var(--u); }   /* rows are whole modules now — no grout to give back */

/* extend the mosaic into the side gutters at wide viewports — dividers and light merged-band
   sections stop dead at the 1180px .gwrap edge, leaving blank paper either side once the
   viewport exceeds maxw + padding. A repeating tile (paper-fill rounded square on seam gray,
   scaled to --u so it reads as the same module) fills that gutter as background texture.
   Decorative only — no hover/interactivity, unlike the real .c tiles inside the grid.
   NOTE: colors are baked into the SVG (can't reference custom properties in a static image);
   keep them in sync with --seam / --paper-fill by hand if those tokens ever change. */
/* container query, not 100vw: vw includes the scrollbar, so it overshoots the real gwrap
   edge (which centers against the scrollbar-excluded width) by ~7px — same bug as --u had. */
.side-mosaic{ position:relative; container-type:inline-size; }
.side-mosaic::before, .side-mosaic::after{
  content:""; position:absolute; top:0; height:100%; z-index:0; pointer-events:none;
  width:max(0px, calc((100cqw - var(--maxw)) / 2));
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Crect width='100' height='100' fill='%23e1dbd1'/%3E%3Crect x='1' y='1' width='98' height='98' rx='26' fill='%23faf7f2' fill-opacity='.93'/%3E%3C/svg%3E");
  /* tiles are TRUE SQUARES, one module each. Nothing is ever clipped because app.js
     quantises every mosaic section's HEIGHT to a whole number of modules instead — the
     grid stays canon and the sections conform to it, not the other way round. */
  background-size:var(--u) var(--u); background-repeat:repeat;
  /* PHASE-LOCKED SCROLL (was background-attachment:fixed). Fixed attachment kept the
     tiling continuous across section boundaries, but it cannot be composited: the whole
     repeating pattern repaints on EVERY scroll frame, which is the classic macOS
     trackpad stutter (high event rate x Retina DPR x full-height tiles). The layer now
     scrolls normally — cheap — and app.js sizes the rows per section (--mosaic-th). */
  /* phase-lock the tile grid to the REAL 12-col grid's own column lines, not to the
     viewport edge. Without this, the gutter tiles use the same --u module but a
     different origin, so their seams never land where the real grid's column lines
     do — two grids that are the same size but not the same grid. Anchoring the tile
     origin to gwrap's own left edge (same formula .gwrap centers with) means every
     tile boundary in the gutter is exactly --u away from a real column line — the
     whole page (gutter + 12 columns + gutter) reads as one continuous modular grid. */
  background-position:max(0px, calc((100cqw - var(--maxw)) / 2)) 0;
}
/* Both gutters tile OUTWARD FROM THE CONTENT EDGE, so the module grid continues off the
   content column in both directions. The shared background-position above puts a tile
   boundary at the left gutter's RIGHT edge (= the content edge) — correct there. The right
   gutter is mirrored, so it needs its origin at its own LEFT edge instead; sharing the
   left gutter's value anchored it to the page edge and the two sides disagreed. */
.side-mosaic::before{ left:0; }
.side-mosaic::after{ right:0; background-position-x:0; }
/* #catalog gutters + field: same mosaic module, INVERTED palette — orange
   background shape, dark-grey (section fill) rounded foreground */
#catalog.side-mosaic::before, #catalog.side-mosaic::after{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Crect width='100' height='100' fill='%23e2570d'/%3E%3Crect x='1' y='1' width='98' height='98' rx='26' fill='%23262626'/%3E%3C/svg%3E");
}
/* footer rails: #343434 ground + #262626 tile faces (swapped per user) — the lighter
   grout peeks around the dark faces, matching the wordmark void pixels' construction */
footer.footer.side-mosaic::before, footer.footer.side-mosaic::after{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Crect width='100' height='100' fill='%23343434'/%3E%3Crect x='1' y='1' width='98' height='98' rx='26' fill='%23262626'/%3E%3C/svg%3E");
}
/* BEHIND THE CONTENT: one solid block spanning the content column, left to right (user
   request). The tile pattern belongs to the side gutters only — running it behind the
   content put a second, unrelated grid under blocks that already sit on the real 12-col
   grid, which is what made the light->dark boundary read as a cut through half a tile. */
#catalog .vcat-wrap{ background-color:#262626; }
@media (max-width:1024px){ .side-mosaic::before, .side-mosaic::after{ display:none; } }
/* sections clip their rising blocks; the divider instead RESERVES one block-height below its
   squares (≈ 100%/7 of width, the square height) so the full squares rise within that reserved
   band and push the next section down — no clipping, no overlap. */
.section-pad .ggrid, .footer .ggrid{ overflow:hidden; }
/* divider = two rows of blocks (14 cells) that fade in (opacity) as they cross the screen edge.
   Sections themselves are static (no entrance animation). Dividers are desktop-only. */
/* mobile keeps ONE row of four divider squares — the rhythm survives, scaled to a
   4-module band (two-shape look: paper squares on seam, same as desktop) */
@media (max-width:1024px){
  .interstice .ggrid{ grid-template-columns:repeat(4,1fr); grid-auto-rows:auto !important;
    gap:1px; background:var(--seam); border:1px solid var(--seam); }
  .interstice .ggrid .c{ grid-area:auto !important; aspect-ratio:1; display:block; border:0 !important; margin:0 !important; }
  .interstice .ggrid .c:nth-child(n+5){ display:none !important; }
  .interstice.m-band-only{ display:block; }   /* the injected divider above HOW IT WORKS is mobile-only */
}

/* cells — NEW two-shape model: a white (paper) ROUNDED rect sitting on the grid's
   gray (seam) background. The gray shows through the 1px gaps and, thanks to the
   corner rounding, at every junction (same sparkle as the old corner tabs). */
.c{ background:var(--paper-fill); padding:clamp(15px,1.3vw,21px); position:relative;
  min-width:0; display:flex; flex-direction:column; justify-content:flex-start; overflow:hidden;
  border-radius:22px; }
.c, .sc, .vcard{ border-radius:22px; }
/* hover: scale every divider-style mosaic block — the ones in the .interstice dividers
   AND the same empty blocks reused inside sections — but never content blocks or cards.
   :empty = a block with no children/text, i.e. the decorative mosaic tiles. */
.c:empty{ transition:transform .2s cubic-bezier(.2,.7,.2,1); }
/* mouse-only: on touch there is no hover — gating prevents sticky ripple states */
@media (hover:hover) and (pointer:fine){
  .c:empty:hover{ transform:scale(0.96); z-index:6; }
}
@media (prefers-reduced-motion:reduce){ .c:empty:hover{ transform:none; } }
/* on hover, the dot-matrix (ASCII) texture BUBBLES out from the cursor — a circular
   clip-path reveal expanding from (--mx,--my, set by app.js) while the dots scale up.
   Collapses on leave, re-spawns from the cursor on the next block. Same copper dot
   grid used on the 3D render. */
.c:empty::before{ content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background-image:radial-gradient(circle, var(--ice) 0 0.9px, transparent 1.5px);
  background-size:8px 8px; opacity:.6;
  clip-path:circle(0px at var(--mx,50%) var(--my,50%));
  transform:scale(.82); transform-origin:var(--mx,50%) var(--my,50%);
  /* leave = quick clean collapse so the old tile clears as the next one blooms */
  transition:clip-path .3s cubic-bezier(.4,0,.2,1), transform .3s cubic-bezier(.4,0,.2,1); }
/* enter = slower, smoothly-eased bubble so the tile-to-tile hand-off feels fluid */
@media (hover:hover) and (pointer:fine){
  .c:empty:hover::before{ clip-path:circle(150% at var(--mx,50%) var(--my,50%)); transform:scale(1);
    transition:clip-path .55s cubic-bezier(.22,.61,.36,1), transform .55s cubic-bezier(.22,.61,.36,1); }
}
@media (prefers-reduced-motion:reduce){
  .c:empty::before{ clip-path:none; transform:none; opacity:0; transition:opacity .2s; }
  .c:empty:hover::before{ opacity:.6; } }
.c--win{ background:transparent; padding:0; }     /* 3D window: pie shows through */
.c--void{ background:transparent; }               /* measured negative space */
.c--mid{ justify-content:center; }
.c--end{ justify-content:flex-end; }

/* two-shape for HERO content cells. The section grids get their gray backing
   from the grid itself (background:var(--seam)); the hero grid can't — the pie
   shows through its right half, so a gray grid would grey the pie out. Instead
   each hero content block carries its OWN two-shape: the element box is the gray
   (seam) bottom shape (square, so gray reaches the corners), a ::before paints
   the white (paper) rounded top shape, and the content lifts above it. Same
   mechanism as a mosaic tile (white border-radius on gray), self-contained. */
#stack .ggrid > .c:not(.c--void):not(.c--win){ background:var(--seam); border-radius:0; }
/* NOT .stkblk: it's a multi-MODULE container (orange heading + its own grey/paper layer rows), so it
   stays a bare seam-grey ground — a full paper face here would show white behind the heading's rounded
   corners (the modules provide their own faces). Same principle as the hero-cover block. */
#stack .ggrid > .c:not(.c--void):not(.c--win):not(.stkblk)::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:var(--paper-fill); border-radius:22px; }
/* :not(.m-explode) — the sticky exploded slice sets its OWN position:sticky; this generic
   "lift card children above the ::before ground" rule would otherwise force it back to relative. */
#stack .ggrid > .c:not(.c--void):not(.c--win) > *:not(.m-explode){ position:relative; z-index:1; }

/* footer-inner — same two-shape technique, inverted for the dark footer: the block's
   own background is the lighter grey (--ink-2), a ::before paints the actual near-black
   content panel as a rounded rect on top, so grey peeks through only at the corners. */
.footer-inner{ background:#343434; border-radius:0; position:relative; }   /* ground behind the #262626 face — same two-tone as the rails/wordmark */
/* the generic section grid paints light seam grout (1px gaps on --seam) — on the dark
   footer that rimmed the panel as a light stroke; the footer grid has no grout
   (footer.footer.gsec outranks the .gsec:not(.explode-section) .ggrid generic) */
footer.footer.gsec .ggrid{ background:transparent; gap:0; }
.footer-inner::before{ content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:#262626; border-radius:22px; }
.footer-inner > *{ position:relative; z-index:1; }

/* corner inverted-arc tabs — 28x28 square at each corner with the inner
   edge curving TOWARD the block's outer corner (concave inner edge instead
   of convex). 3 vertices: right angle at the outer corner + 2 arc endpoints.
   The fill area is the small wedge between the block's outer corner edges
   and the inward-bulging arc. */
/* old 4-corner fillet tabs replaced by border-radius (two-shape model) */
.c::after{ content:none; }
/* (.c--datum used to suppress the corner tabs via background:none — removed
   so section-title cells and .hero-cover now carry the same corner fillets
   as every other content block.) */

.sc::after,
.vcard::after{ content:none; }
/* hero scroll runway is the ONLY void where the corner tabs were popping
   in disruptively during the scroll-driven explode animation. Divider voids
   keep their tabs; only the runway (inside .explode-section) loses them. */
.explode-section .c--void::after{ background:none; }

/* window bezel: FIG tag label on the render field */
.c--win .ftag{ position:absolute; top:12px; left:12px; z-index:4;
  font:500 10.5px/1 var(--mono-read); letter-spacing:.16em; text-transform:uppercase; color:var(--ink-3); }

/* chrome strip — tag + index header bar on a panel */
.chrome{ display:flex; align-items:center; justify-content:space-between; gap:10px;
  margin:-2px -4px 12px; padding-bottom:9px; border-bottom:1px solid var(--seam); }
.chrome .ct{ font:500 10.5px/1 var(--mono-read); letter-spacing:.16em; text-transform:uppercase; color:var(--ink-3); }
.chrome .ci{ font:600 10.5px/1 var(--mono-read); letter-spacing:.08em; color:var(--ink-3); }

/* dimension line for empty cells: ink rule + end-ticks + centered mono value */
.dim{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; }
.dim::before{ content:""; position:absolute; left:16px; right:16px; top:50%; height:1px; background:var(--bp-tick); }
.dim span{ position:relative; z-index:1; padding:4px 10px; background:var(--paper-fill);
  font:500 10.5px/1 var(--mono-read); letter-spacing:.18em; text-transform:uppercase; color:var(--ink-3); }
.dim.on span{ color:var(--ice); }

/* per-section title block, pinned bottom-right of its cell */
.tblock{ position:absolute; right:12px; bottom:11px; z-index:4; text-align:right;
  font:500 9.5px/1.5 var(--mono-read); letter-spacing:.1em; text-transform:uppercase; color:var(--ink-faint); }

/* key/value readout rows inside instrument cards */
.kv{ display:flex; gap:10px; font:500 11px/1.5 var(--mono-read); letter-spacing:.06em; color:var(--ink-3); }
.kv .k{ color:var(--ink-faint); text-transform:uppercase; letter-spacing:.12em; min-width:46px; }
.kv .v{ color:var(--ink-2); }

/* segmented index number */
.idxnum{ font:400 clamp(22px,2.2vw,30px)/1 var(--mono-display); color:var(--ink); }

/* section-scoped overrides so the old card chrome doesn't double up */
.gsec .seclabel{ margin-bottom:0; }
.gsec .btn{ align-self:flex-start; }

/* nested sub-grids inside a cell (stat quadrants, 5-up flywheel, roadmap rows) */
.c--bare{ padding:0; }
.subgrid{ display:grid; gap:0; background:var(--grout, var(--seam)); height:100%; }   /* ground behind the cards' corners — same reason as .ggrid */
.sc{ background:var(--paper-fill); padding:clamp(14px,1.2vw,19px); position:relative;
  display:flex; flex-direction:column; gap:7px; min-width:0; justify-content:flex-start; }
.sc .k{ font:500 10px/1.3 var(--mono-read); letter-spacing:.14em; text-transform:uppercase; color:var(--ink-faint); }
.sc h4{ font-size:16px; letter-spacing:-.01em; }
.sc p{ margin:0; font-size:13px; line-height:1.5; color:var(--ink-2); }
.sc .idxnum{ color:var(--ink); }
.sc .prog{ margin-top:auto; height:2px; background:var(--ice); }

/* side rail of little stacked bordered boxes (lattice legend / carousel) */
.rail{ display:flex; flex-direction:column; gap:1px; background:var(--seam); padding:0; }
.railbox{ background:var(--paper-fill); padding:13px 15px; display:flex; align-items:center; gap:9px;
  font:600 11px/1 var(--mono-read); letter-spacing:.06em; color:var(--ink-2); }
.railbox .sw{ width:9px; height:9px; background:var(--ice); flex:none; }
.railbox.ghost{ color:var(--ink-3); border-left:2px dashed var(--hair-2); }
.railbox.live{ margin-top:auto; color:var(--ice); }

/* door card internals */
.doorcard{ gap:14px; }
.doorcard .kick{ font:600 10.5px/1 var(--mono-read); letter-spacing:.18em; text-transform:uppercase; color:var(--ink-3); }
.doorcard h3{ font-size:clamp(22px,2.4vw,30px); letter-spacing:-.025em; max-width:15ch; }
.doorcard p{ color:var(--ink-2); font-size:15px; line-height:1.55; margin:0; flex:1; }

/* sections wrap their content exactly — no extra padding (dividers fill the gaps) */
.gsec.section-pad{ padding-block:0; }
.gsec.divider-row{ border-top:0; }   /* the grid border replaces the section rule */
/* the explode section stays PINNED through its linger; the next section's grid-top divider
   would scroll up and draw a stray line ACROSS the still-fixed FIG frame — drop it here. */
.explode-section + .gsec .ggrid{ border-top:0; }
.hero-section.gsec{ min-height:100vh; display:block; padding-top:var(--navh); }
.hero-section .display{ font-size:clamp(32px,4.3vw,56px); line-height:.98; }

/* the pinned stage: the 3D render field sticks on screen while the left-column
   beats (hero copy -> protocol loop -> the stack layer cards) scroll past it */
/* horizontal clipping lives on <html> (the root) so position:sticky still pins; body stays visible */
/* no bottom padding: the grid bottom (where the sticky stack rests) must equal
   the section bottom (where the fig rides to and the divider begins) so the
   stack + fig both land flush on the divider — any bottom pad reopens the gap. */
.stage{ padding-block:var(--navh) 0; }

/* the hero, unified into ONE section: eyebrow marker at the top, the full
   headline below it — one cell, no internal seam, sized so nothing clips */
.hero-cell{ justify-content:flex-start; gap:clamp(16px,2.6vh,34px); }
.hero-mark{ display:flex; align-items:baseline; justify-content:space-between; gap:14px; flex-wrap:wrap; }
.hero-mark .eyebrow{ margin:0; }
.hero-coord{ font:500 10px/1 var(--mono-read); letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink-faint); white-space:nowrap; }
.hero-cell .display{ margin:0; font-size:clamp(30px,4.4vw,57px); }   /* sized so the longer v2 headline's lines fit the cell (no clip into the lede) */

/* compact heading for the one-slice panel (narrower right column) */
.oneslice-h{ font-size:clamp(24px,2.4vw,34px); }

/* the render field: a FIXED rect app.js drives (left/top/width/height) as you scroll. */
/* two-shape FIG window: the stage-win is the BOTTOM shape (the modules' seam
   grey, square), and the #bgCanvas child is the rounded TOP shape — the pie
   renders straight onto it; its radius lets the grey peek at the corners. */
.stage-win{ position:fixed; left:50%; top:var(--navh); width:38vw;
  height:var(--fig-h); z-index:2; pointer-events:none;
  background:var(--seam); box-shadow:none;
  /* app.js drives the end-of-section ride-up with translate3d — keep this pane on its own
     compositor layer so that movement never costs a repaint (macOS trackpad smoothness) */
  will-change:transform; }
.stage-win.is-hidden{ display:none; }
.stage-win::after{ content:none; }

/* ── STACK ↔ 3D LAYER HOVER SYNC — pointing at a slice layer on the 3D lights the matching
   L1/L2/L3 card here (.is-hot, toggled by app.js from gl-scene's onLayerHover); hovering a
   card fans the pie + glows that layer (gl.setLayerHover). The hot card flips to the full-
   orange module with white text — the same active language as the site's orange blocks. */
.stkrow, .stkrow::before{ transition:background .16s ease; }
.stkrow .stk-n, .stkrow h4, .stkrow p{ transition:color .16s ease; }
.stkrow .stk-pos rect{ transition:fill .16s ease; }
.stkrow.is-hot::before{ background:var(--ice); }   /* foreground face only — the seam ground stays */
.stkrow.is-hot .stk-n{ color:#fff; }
.stkrow.is-hot h4{ color:#fff; }
.stkrow.is-hot p{ color:rgba(255,255,255,.88); }
.stkrow.is-hot .stk-pos rect{ fill:rgba(255,255,255,.38); }
.stkrow.is-hot .stk-pos rect.on{ fill:#fff; }
/* the hover highlight is a desktop-only feature (app.js also gates the trigger to desktop). On
   mobile a tap used to synthesize a mouseenter that left a card stuck orange — so here a stray
   .is-hot is reverted to the resting card. Self-contained @media so it can never leak to desktop. */
@media (max-width:1024px){
  #stack .stkrow.is-hot::before{ background:var(--bg); }
  #stack .stkrow.is-hot .stk-n{ color:var(--ice); }
  #stack .stkrow.is-hot h4{ color:var(--ink); }
  #stack .stkrow.is-hot p{ color:var(--ink-2); }
  #stack .stkrow.is-hot .stk-pos rect{ fill:var(--seam); }
  #stack .stkrow.is-hot .stk-pos rect.on{ fill:var(--ice); }
}

/* STACK COLUMN — a FIXED column app.js sticks to the fig's TOP border (cols 8-12) during the
   explode. The layer blocks accumulate one under another as the slice fans into its three
   layers: hidden blocks collapse to zero height, so the column grows as each is revealed and
   the fig's height is matched to it. */
/* STACK COLUMN — a FIXED column of SEPARATED cards beside the fig. Each card keeps its slot
   (so the others don't shift) and RISES UP from below as the scroll passes its threshold; the
   column holds in place while the pie explodes, so it doesn't scroll away the moment L3 lands. */
/* Stack block — in-flow grid cell that fills its 8-row track so its visible
   bottom lines up with the 3D pie's pinned bottom. Content sits at the top
   of the cell (flex-column / justify-content:flex-start from .c); the rest
   of the cell is paper fill within the same bordered block. */
.stkblk{
  display:flex; flex-direction:column;
}

/* ── Hero + stack ↔ fig height lock (desktop) ─────────────────────────────
   The fig frame (.stage-win) is JS-positioned at top:90 with
   height = innerHeight − 90 − BOTTOM_GAP, where BOTTOM_GAP = clamp(16,3.5vh,40).
   --fig-h mirrors that exact formula in pure CSS so the left column tracks it
   with no JS. The hero sits at the top (= fig top→bottom) and the stack sits
   directly below it (gap 0); the stack is sticky so it PINS the moment its top
   reaches the fig's top border (90) and its bottom lands on the fig's bottom. */
/* FULL-HEIGHT HERO (user request): no bottom gap — the hero/FIG run from the nav's
   bottom edge to the bottom of the screen…
   …UP TO A CEILING OF EIGHT MODULES. The content grid stops at --maxw, so on a big
   display the width is fixed while the height kept growing with the viewport: at 4K the
   frame was a ~590px column stretched past 2000px, and the hero copy and supply column,
   which both track --fig-h, stretched with it. The module is the natural limit — capped
   here the FIG is exactly 6 modules wide by 8 tall, a whole block of the same grid as
   everything else, and short viewports still get vh − nav as before. app.js caps the
   frame it positions with the same 8 × column rule; the two must not disagree. */
:root{ --fig-h: min(calc(100vh - var(--navh)), calc(var(--u) * 8)); }
/* small-viewport units where supported — the iOS address bar collapsing must not
   re-measure the pinned geometry mid-gesture (vh breathes, svh doesn't) */
@supports (height:100svh){
  :root{ --fig-h: min(calc(100svh - var(--navh)), calc(var(--u) * 8)); }
}
@media (min-width:1025px){
  /* the stage's scroll runway (was an inline height on the .ggrid — desktop-only,
     so phones get natural auto height instead of ~2.1k px of stretched cells).
     360vh floor: at 2164px the whole 240-frame story rode on ~1.25 screens of scroll
     (~5px/frame) — one touchpad momentum flick skipped most of it. 360vh ≈ doubles
     the travel (hero screen + ~2.6 screens of story) so scrubbing has a watchable pace. */
  #stack .ggrid{ height:max(2164px, 360vh); }
  #stack .ggrid{ grid-auto-rows:auto; align-content:start; }
  #stack .hero-cover{ height:var(--fig-h); align-self:start; }
  /* rows HUG the blocks (auto rows + explicit block heights) — the sized-template variant
     left stretched rows around the fixed-height strips, which read as gaps between blocks.
     ½-module strips · 2-module wordmark · 1-module buttons · 2-module intro, 2px grout only. */
  /* the LAST row flexes to absorb the leftover fig-height and the closing strip rides its
     bottom edge — pinned to the hero's bottom (user request); rows 1-7 hug their blocks */
  /* row 8 is the FLEX row: the strip between the buttons and the foot strip stretches to
     consume all leftover fig-height (user request) — the foot stays a half-module at the bottom */
  /* --hu = the hero's module unit: the site module (--u) whenever the stack fits the
     cover, CAPPED so it always does. The fixed rows are 7 units (½ strip · 1 kicker ·
     2 wordmark · 2 intro · 1 buttons · ½ foot) plus ½ for the fill's floor and ~20px of
     grout, so hu can never exceed (fig-h − 20) / 7.5. Without the cap, short viewports
     overflowed the cover (747px of blocks in a 601px cover at 700px tall) and the sticky
     supply block collided with the buttons row. Slack always lands in the 1fr fill row. */
  #stack .hero-modgrid{
    --hu:min(var(--u), (var(--fig-h) - 20px) / 7.5);
    /* 8 rows, not 9: the closing foot strip merged into the fill block, so the flexible row
       IS the last one. Its height grows by exactly what the foot strip used to take. */
    grid-template-rows:repeat(7, min-content) minmax(0, 1fr); align-content:start; }
  #stack .hero-modgrid .hmod--kicker.hmod--fill{ height:auto; min-height:calc(var(--hu) / 2); align-self:stretch; }
  #stack .hero-modgrid .hmod--kicker{ height:calc(var(--hu) / 2); min-height:0; }
  /* the TEXT kicker is one full module; the empty strips stay half-module. Excludes
     .hmod--fill explicitly — that strip lost its aria-hidden when it took the "how to
     connect" link, which silently pinned it to a full module and killed its stretch. */
  #stack .hero-modgrid .hmod--kicker:not([aria-hidden]):not(.hmod--fill){ height:var(--hu); }
  #stack .hero-modgrid .hmod--wm, #stack .hero-modgrid .hmod--intro{ height:calc(var(--hu) * 2 + 2px); min-height:0; }
  #stack .hero-modgrid .hmod--cta, #stack .hero-modgrid .hmod--connect{ height:var(--hu); min-height:0; }
  /* the hero occupies the full first screen (nav + fig-h + bottom gap = 100vh); the hero's
     bottom edge sits one BOTTOM_GAP above the fold, so give the supply-layer block that same
     gap below the hero — it then begins exactly at the fold and only rises in on scroll. */
  #stack .stkblk{ height:var(--fig-h); align-self:start; position:sticky; top:var(--navh);
    margin-top:clamp(16px, 3.5vh, 40px); }
  /* NO transition on margin-top: it animated a LAYOUT property, and the class that fires
     it flips the instant scrolling begins — so every frame of that .28s relaid out the
     sticky column right when the trackpad's event storm starts. Collapsing instantly is
     imperceptible and costs nothing. */
  /* the fold-spacer above exists ONLY so the supply block starts exactly at the fold at rest —
     once scrolling begins (html.stage-scrolled, set by app.js) it collapses and the block rides
     up flush with the hero, so no white slit travels between the two seam grounds mid-scroll. */
  html.stage-scrolled #stack .stkblk{ margin-top:0; }
}
/* STACK = exploded cross-section legend. Header on top, then three layer rows
   that SHARE all remaining height (the block is fig-height on desktop — the
   rows absorb it, no dead paper), and a read-me footline pinning the bottom
   edge. Each row: position glyph + big dot-matrix numeral | text | material
   swatch built from the same two-shape pixel cells as the dividers/wordmark. */
/* the stack cell is FOUR sibling blocks — the title and the three layers —
   each with its OWN two-shape fill: seam-grey ground square + rounded paper
   foreground (2px inset, ground peeks at the corners), separated by real gaps */
.stkblk{ background:transparent; padding:0; gap:0;
  border-radius:0; overflow:visible; }
/* ONE clean orange module. The two-shape (seam-ground square + inset rounded face) works on the
   tight layer-row grid, but on this wide standalone heading it read as a distracting grey frame
   around an inner block ("two blocks nested"). So the heading is a single rounded orange block,
   contained by its 2px margin so the fill doesn't bleed past the block into the surroundings. */
.stkblk .oneslice-h{ position:relative; background:var(--ice); border-radius:22px;
  margin:2px; padding:clamp(18px,2.2vw,28px); border-bottom:0;
  /* same text voice as the MISSION title (.vis-bigtitle): dot-matrix display, white on
     copper, uppercase — sized for a full-sentence line rather than a one-word title */
  font-family:var(--mono-display); font-weight:400;
  font-size:clamp(32px, 3.5vw, 56px); line-height:.9;
  letter-spacing:.04em; text-transform:uppercase; color:#fff; }
.stkblk .oneslice-h::before{ content:none; }
.stk-layers{ flex:1; min-height:0; display:flex; flex-direction:column;
  gap:0; margin-top:0; }
.stkrow{ flex:1; display:grid; grid-template-columns:58px minmax(0,1fr);
  gap:0 clamp(14px,1.8vw,26px); align-items:center; align-content:center;
  padding:clamp(12px,1.6vh,18px) clamp(16px,1.7vw,26px);
  position:relative; background:var(--seam); }
.stkrow::before{ content:""; position:absolute; inset:2px;
  border-radius:20px; background:var(--bg); }   /* concentric with the 22px block family (22 − 2px inset) */
/* SUPPLY-IN-THREE-PARTS ON THE MODULAR CANON (desktop): one seam ground for the whole
   column (same construction as the hero cover) — 2px padding, 2px grout between the four
   faces, every face at radius 20 (22 − 2px inset). Before this, the heading sat at the
   full-module 22 radius despite its 2px margin, and each row carried its OWN ground with
   a 2px inset, so touching rows read a 4px face-to-face grout — twice the modular scale.
   Mobile keeps the base rules (the card deck uses these classes with its own system). */
/* ON THE PAGE'S CURRENT CANON: the column is one seam ground and every face — the heading
   and the three layer rows — is inset ONE pixel from its own box, radius 22 − 1. That is
   the same construction as a divider module: 1px of ground at the outside, and where two
   faces touch, their two insets add to 2px. It used to be built at the 2px scale (2px
   padding, 2px grout, radius 20), which was the convention before the seam moved onto the
   blocks themselves — it read a size heavier than every module beside it. */
@media (min-width:1025px){
  #stack .stkblk{ background:var(--seam); padding:0; }
  #stack .stkblk .oneslice-h{ margin:1px; border-radius:21px;
    /* exactly ONE module tall (user request) — same unit as the hero blocks / nav tiles;
       text centred both axes */
    height:var(--u); min-height:0; display:flex; align-items:center; justify-content:center;
    text-align:center; padding-top:0; padding-bottom:0; }
  #stack .stk-layers{ gap:0; }

  /* ── THE COLUMN IS THE CROSS-SECTION ────────────────────────────────────────
     Each row was one card with a small glyph rail inside it — a shape any site
     could have. It is two modules now: a one-module SPINE carrying the layer's
     numeral, and the copy beside it. The three spines stack down the left edge
     and are toned by depth — paper at the surface, seam grey in the middle,
     near-black at the base — so the left edge reads as the strata the 3D beside
     it is showing, and "which layer" is answered by the material rather than by
     a label. Both faces keep the page's 1px canon, so the pair reads as two
     modules of the same grid, not as a card with a sidebar. */
  /* align-CONTENT too, not just align-items: the base rule centres the single row track
     inside the row box, so the two faces stretched to the TRACK (their own content height)
     and left the rest of the card as bare ground under them */
  #stack .stkrow{ background:transparent; padding:0; align-items:stretch; align-content:stretch;
    grid-template-columns:var(--u) minmax(0,1fr); gap:0; }
  #stack .stkrow::before{ content:none; }        /* the row is no longer one face */
  #stack .stk-ix, #stack .stkrow .stk-tx{ margin:1px; border-radius:21px; }
  #stack .stkrow .stk-tx{ background:var(--bg); display:flex; flex-direction:column; justify-content:center;
    padding:clamp(16px,2.2vh,26px) clamp(20px,2vw,30px); }
  /* ONE spine, three times (user request). The tone ladder that ran paper → seam → near-black
     down the column is gone: all three spines are the dark face L3 had, with the copper
     numeral. Depth is read from the numeral and its position glyph, and the three dark blocks
     stack into a single column that holds the left edge instead of fading upward. */
  #stack .stk-ix{ background:#262626; flex-direction:column; align-items:center; justify-content:center; gap:12px; }
  #stack .stk-n{ color:var(--ice-bright); }
  #stack .stk-pos rect{ fill:#3f3a34; }
  #stack .stk-pos rect.on{ fill:var(--ice-bright); }

  /* hover sync with the 3D: the SPINE answers — flooding the whole row in copper
     took the copy with it, and the copy is what you were reading */
  #stack .stkrow.is-hot .stk-ix{ background:var(--ice); }
  #stack .stkrow.is-hot .stk-n{ color:#fff; }
  #stack .stkrow.is-hot .stk-pos rect{ fill:rgba(255,255,255,.34); }
  #stack .stkrow.is-hot .stk-pos rect.on{ fill:#fff; }
  #stack .stkrow.is-hot .stk-tx{ background:var(--bg); }
  #stack .stkrow.is-hot h4{ color:var(--ice); }
  #stack .stkrow.is-hot p, #stack .stkrow.is-hot .stk-meta{ color:var(--ink-2); }
}
.stkrow > *{ position:relative; z-index:1; }
.stk-ix{ display:flex; flex-direction:column; gap:9px; }
.stk-pos{ width:26px; height:auto; display:block; }
.stk-pos rect{ fill:var(--seam); }
.stk-pos rect.on{ fill:var(--ice); }
.stk-n{ font:400 clamp(30px,3vw,42px)/1 var(--mono-display); letter-spacing:.03em; color:var(--ice); }
.stk-tx{ display:flex; flex-direction:column; justify-content:center;
  gap:clamp(10px,2.2vh,22px); }
.stkrow h4{ margin:0; font-size:clamp(19px,1.9vw,27px);
  line-height:1.12; letter-spacing:-.015em; }
/* max-width, not the cell's full width: past ~46 characters the eye loses the line
   return, and these rows are wide enough to run well past it */
.stkrow p{ margin:0; font-size:clamp(14px,1.25vw,16.5px); line-height:1.6; max-width:38ch;
  color:var(--ink-2); text-wrap:pretty; }
.stk-swatch{ width:100%; max-width:118px; height:auto; display:block; justify-self:end; }
.stk-swatch rect{ fill:var(--seam); }
.stk-swatch rect.f-p{ fill:var(--bg); }
.stk-swatch rect.f-o{ fill:var(--ice-bright); }
.stk-swatch rect.f-d{ fill:#262626; }
.stk-foot{ border-top:1px solid var(--seam); padding-top:11px; display:flex; justify-content:space-between; gap:12px;
  font:500 10px/1.5 var(--mono-read); letter-spacing:.14em; text-transform:uppercase; color:var(--ink-3); }
@media (max-width:640px){
  .stkrow{ grid-template-columns:46px minmax(0,1fr); }
  .stk-swatch{ grid-column:2; justify-self:start; margin-top:10px; max-width:104px; }
  .stk-n{ font-size:24px; }
}
/* desktop: the pin wrapper is transparent — the fig is JS-fixed as before, so the wrapper must
   not introduce a layout box (display:contents makes its children act as direct #stack children). */
.m-pinwrap{ display:contents; }
.m-explode{ display:none; }   /* the 2nd exploded-slice 3D is mobile-only */
.stk-strip{ display:none; }   /* …and so is the empty module that caps the stack */

@media (max-width:1024px){
  /* SUPPLY-LAYER SCROLLYTELLING (card deck): the exploded slice PINS (sticky, portrait) with a card
     band under it; the three L1/L2/L3 cards are ALSO sticky at that band's top, so each new card
     slides up OVER the previous one. app.js (syncSupplyLayers) glows the layer of the top card via
     explodeGL.setLayerHover. After the third card lands, the section simply scrolls away.
     Geometry (all svh so the iOS bar collapse can't reflow it):
       slice top = navh+8, slice height = --m3d-h, card line = slice bottom + 10. */
  /* one-viewport composition: nav + FULL-WIDTH 3D + one full-band card = 100svh, nothing else
     visible. --mcard-h sizes every card identically to the leftover band — IDENTICAL heights are
     load-bearing: a sticky card releases when its BOTTOM hits the container end, so equal heights
     mean all three release at the SAME scroll point and ride out as one stack (no card sliding
     out from behind another). */
  /* --mcard-h is a FLOOR, not a stretch: full-band cards read as mostly blank paper (the layer
     text is ~120px tall). Cards hug their content, floored at 24svh so all three stay EQUAL
     height (the floor binds for all) — equal heights + equal margins = shared release point. */
  /* AIRTIGHT stack (user choice): nav → 3D → card with ZERO seams. The slice pins flush to the
     nav's bottom edge and the card line IS the slice's bottom edge — no 8/10px module gaps. */
  #stack{ --m3d-h:46svh; --mcard-top:calc(var(--navh-px, 64px) + var(--m3d-h));
    --mcard-h:24svh; }
  #stack .stkblk{ display:block; }                    /* was flex column — let the sticky slice + deck flow */
  /* the cap module. .stkblk's ground is seam here and its children are paper tiles held off it by
     2px margins (see .oneslice-h just below) — this is that same tile with nothing in it, at the
     60px the hero's empty modules use. It sits ABOVE the sticky slice, so the slice now pins to the
     nav after this block has scrolled past rather than the instant the section arrives. */
  #stack .stk-strip{ display:block; min-height:60px; margin:2px 0;
    background:var(--paper-fill); border-radius:22px; }
  #stack .m-explode{ display:block; position:sticky; top:var(--navh-px, 64px);
    height:var(--m3d-h); width:100%; margin:0; z-index:1;
    background:#262626; border-radius:22px; overflow:hidden; }   /* module family radius — content tiles stay 22 on mobile */
  #stack .m-explode canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }
  /* CARD DECK: every card is sticky at the SAME line under the slice, so a later card scrolls up
     and covers the earlier one (opaque two-shape fill). Short cards — they fit inside the band
     below the 3D. No selection ring / dim states: the top card IS the selection. */
  /* NO landing-runway spacer (user choice — it read as a grey gap after the deck): the moment the
     third card reaches the line the stack releases and the page scrolls on. If dwell is ever wanted
     back, it must be an ::after SPACER child here — NOT margin on the last card (a sticky box is
     constrained to the container inset by its own margins — net zero) and NOT container padding
     (the sticky containing block is the CONTENT box, which padding sits outside of). */
  #stack .stk-layers{ display:block; margin-top:0; }
  /* NO between-card spacing (user choice): cards sit flush in flow, so the next card is queued
     directly under the current one and slides over after --mcard-h of scroll. If spacing ever
     returns, it MUST be identical on all three cards — the sticky constraint is inset by the
     card's OWN bottom margin, and equal margins + equal heights = the one shared release point
     that lets the stack ride out as a single unit. */
  #stack .stkrow{ position:sticky; top:var(--mcard-top); height:auto; min-height:var(--mcard-h);
    align-content:center; margin-bottom:0; }
  /* MOBILE STAGE (user choice): the 3D square sits at the TOP; as you scroll it stays PINNED (a JS
     translateY cancels the scroll) while the HERO rises UP and OVER it — SLOWLY. The hero (opaque,
     higher-z) is held back to 1/SLOW of the scroll speed, so it takes a runway of SQ×SLOW to climb
     one square-height and cover the pie — and the weave→slice animation scrubs on the same scroll,
     ending exactly as the hero finishes covering. The stack is transformed by the same amount as
     the hero (glued, so nothing gaps or overlaps), and app.js adds (R−SQ) bottom padding to #stack
     so the next section still lines up once everything freezes. All transforms are set in app.js. */
  /* .stage pads its top by --navh (~32px grid formula) but the real mobile bar is 64px — pad by the
     REAL nav height so the square (the first block) clears the fixed bar. */
  .stage{ padding-top:var(--navh-px, 64px); }
  .m-pinwrap{ display:block; position:relative; height:auto; z-index:1; }
  /* 4:3, not a square (user choice). The square was 390px tall on a 390-wide phone and pushed
     the hero copy most of the way off the fold. 4:3 is the tightest ratio that still frames the
     WHOLE pie: resize() fits the ortho span to the tall axis and then clamps the width to what
     onScreen() actually generated, so a wider box zooms IN rather than opening a gap at the sides
     — 4:3 lands on ortho 18.0 with the half-width at exactly HALF_H. Going wider starts cropping
     (3:2 loses 0.3% of the pie's blocks, 16:9 loses 2.9%). */
  .stage-win{ position:relative; left:auto; top:auto; width:auto; height:auto;
    aspect-ratio:4/3; margin:0; will-change:transform; }
  /* the square is a .ggrid child (between hero & stack): drop the generic .ggrid>* seam
     border + margin-top:-1px so it doesn't box the square or nudge it. */
  .ggrid > .m-pinwrap{ border:0; margin:0; }
  /* hero sits above (z) and opaque. NO will-change:transform anymore — the mobile pin was removed,
     and a transform hint on .stkblk (the sticky slice's parent) breaks position:sticky on the slice. */
  #stack .hero-cover{ position:relative; z-index:2; background:var(--seam); }
}

/* lattice cell holds the SVG + readout sibling (lattice.js contract) */
.latcell{ padding:0; }
.latcell .lattice-stage{ border:0; box-shadow:none; background:transparent; }
.latcell .lattice-stage::after{ display:none; }

/* responsive: the 12-col blocks are desktop-only — every cell uses hard 12-col line
   numbers (grid-column:1/8 …). Below 1024 we stack to ONE readable column instead of
   remapping every inline span. This removes the old mid-width (640–1024) collapse where
   12-col spans fell into collapsed implicit columns and squished text to ~108px. */
@media (max-width:1024px){
  /* --u (the divider square) needs no mobile override: its formula tracks 100vw, and
     mobile grids are single-column/auto-row anyway so the value is only nominal here. */
  .ggrid{ grid-template-columns:1fr; grid-auto-rows:auto; gap:0; background:transparent; border:0; }
  #vision .ggrid{ grid-auto-rows:auto; }   /* the desktop #id modular-row rule would clip stacked cells */
  .ggrid > *{ grid-column:1 / -1 !important; grid-row:auto !important;
    border:1px solid var(--seam); margin-top:-1px; min-height:0; }
  /* decorative spacer voids disappear; .c--band voids CARRY content (Mission,
     HOW IT WORKS, orbit, GET STARTED) and flatten to stacked blocks instead */
  .c--void:not(.c--band){ display:none; }
  .c--band{ display:block !important; padding:0; }
  .c--band > div{ display:block !important; height:auto !important; background:transparent !important; }
  .c--band .c:empty{ display:none !important; }
  .subgrid{ grid-template-columns:1fr !important; grid-template-rows:auto !important; }
  /* comfortable tap targets */
  .footer .fcol a{ padding:11px 0; }
}

/* ── Verticals — click-driven carousel showing 3 cards on a dark stage (section 6) ──── */
.vcat-dark{ background:#0c0907; color:#f4ede4; }
.vcat-wrap{ max-width:var(--maxw); margin:0 auto; padding:clamp(64px,9vh,120px) var(--pad); }

/* Verticals header: title panel, explanatory panel, then a narrow marker cell. */
.vcat-head{ display:grid; grid-template-columns: 7fr 5fr; gap:0;
  margin-bottom:0; }
.vcat-head > .c{ background:rgba(255,255,255,.02); border:1px solid var(--seam);
  margin:-0.5px; padding:clamp(22px,2.6vw,34px); color:#f4ede4; }
.vcat-head-title{ min-height:clamp(250px,26vw,360px); display:flex; flex-direction:column; justify-content:center; align-items:center; }
.vcat-head-title .seclabel{ margin:0; }
.vcat-head-title .seclabel .n{ background:var(--ice); color:#fff; padding:8px 10px; }
.vcat-head-title .seclabel .t{ color:rgba(244,237,228,.6); }
.vcat-head-title .seclabel .ln{ background:rgba(244,237,228,.18); }
.vcat-head-intro{ display:flex; flex-direction:column; justify-content:center; }
.vcat-head-title .vmark{ font:400 clamp(54px,8.4vw,112px)/.8 var(--mono-display); letter-spacing:.035em; color:#f4ede4; }
.vcat-head-spark{ display:grid; place-items:center; padding:0 !important; font:clamp(42px,5vw,70px)/1 var(--grot); color:var(--ice); }

.vcat-intro{ font-size:clamp(17px,1.7vw,22px); line-height:1.34; letter-spacing:-.012em;
  color:#f4ede4; max-width:46ch; text-wrap:pretty; margin:0; }
.vcat-intro strong{ color:var(--ice-bright); font-weight:650; }
.vcat-note{ display:flex; align-items:center; gap:9px; margin:14px 0 0;
  font:500 10.5px/1.4 var(--mono-read); letter-spacing:.1em; text-transform:uppercase; color:rgba(244,237,228,.5); }
.vcat-note .rec{ width:7px; height:7px; background:var(--ice); flex:none; }

/* The rail and carousel occupy one shared row. They use the same top/bottom
   edges and orange seams, so the control reads as part of the Verticals field. */
:root{
  --vcat-unit: calc((min(100vw, var(--maxw)) - 2 * var(--pad)) / 12);
}
.vcat-body{ display:grid; grid-template-columns:minmax(0,1fr) var(--vcat-unit);
  gap:0; align-items:stretch; padding-top:0; min-height:0; }
  /* min-height was 252px — sized for the old 3-module cards, it left ~54px of dead dark
     strip under them once the cards shrank to fit their copy. The row hugs the cards now. */
/* matches the sparkle ✦ block in the section header: subtle white tint,
   1px --seam border, corner-tab fillets via ::after — no rounded radius.
   order:2 moves it to the RIGHT column (grid-template-columns above puts the
   wide carousel column first); the omitted border edge flips to match. */
.vnav-stack{ display:flex; flex-direction:column; gap:0;
  background:var(--ice); order:2;
  border:1px solid var(--seam); border-left:0;
  width:auto; height:100%; align-self:stretch;
  position:relative; overflow:visible; }
.vnav-stack{ border-radius:22px; }
.vnav-stack::after{ content:none; }
.vnav-btn{ width:100%; flex:1 1 50%; min-height:0;
  display:flex; align-items:center; justify-content:center;
  background:transparent; border:0; padding:0; color:#f4ede4;
  font:400 32px/1 var(--grot); cursor:pointer;
  transition:color .18s, transform .18s, opacity .18s; }
.vnav-btn:hover{ color:var(--ice-bright); transform:scale(1.12); }
.vnav-btn:disabled{ opacity:.28; cursor:default; transform:none; }
.vnav-btn:focus-visible{ outline:2px solid var(--ice); outline-offset:-6px; }

.vcarousel{ overflow:hidden; padding:0 !important; margin:0 !important; min-width:0; height:100%; }
.vstripe{ display:flex; align-items:stretch; height:100%;
  gap:0 !important; column-gap:0 !important; row-gap:0 !important;
  padding:0 !important; margin:0 !important;
  will-change:transform; transition:transform .5s cubic-bezier(.4,.75,.2,1); }
/* hard pin with !important so no inline style or override can widen/space the cards */
.vstripe .vcard{
  flex:0 0 calc(var(--vcat-unit) * 4) !important;
  width:calc(var(--vcat-unit) * 4) !important;
  min-width:calc(var(--vcat-unit) * 4) !important;
  max-width:calc(var(--vcat-unit) * 4) !important;
  flex-shrink:0 !important; flex-grow:0 !important;
  margin:0 !important;
  box-sizing:border-box !important;
  border-right-width:0;
}
.vstripe .vcard:last-of-type{ border-right-width:1px; }

/* readability pass (user request): larger body with taller leading and stronger contrast,
   clearer title/subtitle steps, roomier padding — dark ground needs the extra light. */
.vcard{ position:relative; display:flex; flex-direction:column; gap:14px;
  background:rgba(255,255,255,.035); border:1px solid rgba(255,255,255,.10);
  padding:26px 24px 20px; text-decoration:none; color:inherit;
  transition:background .18s, border-color .18s, transform .18s; }
/* hover effect removed per user request — cards stay static */
.vcard:focus-visible{ outline:2px solid var(--ice); outline-offset:-2px; }
.vcard-h{ padding-right:24px; }
.vcard-h .vt{ display:block; font:600 15px/1.25 var(--mono); letter-spacing:.1em; text-transform:uppercase; color:var(--ice-bright); }
.vcard-h .vsub{ display:block; margin-top:8px; font:500 12px/1.5 var(--mono-read); letter-spacing:.02em; color:rgba(244,237,228,.66); }
.vcard > p{ margin:0; flex:1; font-size:14.5px; line-height:1.65; color:rgba(244,237,228,.85); max-width:52ch; text-wrap:pretty; }

/* area status chip — replaces live metrics until real numbers exist */
.vstatus{ margin-top:auto; align-self:flex-start; display:inline-flex; align-items:center; gap:8px;
  font:600 10px/1 var(--mono-read); letter-spacing:.1em; text-transform:uppercase;
  padding:9px 12px; border:1px solid rgba(255,255,255,.14); color:rgba(244,237,228,.62); }
.vstatus::before{ content:""; width:7px; height:7px; background:rgba(244,237,228,.32); flex:none; }
.vstatus.forming{ color:var(--ice-bright); border-color:rgba(226,87,13,.5); }
.vstatus.forming::before{ background:var(--ice); box-shadow:0 0 7px rgba(226,87,13,.7); }

.vcard-arr{ position:absolute; top:22px; right:20px; font:600 16px/1 var(--grot);
  color:rgba(244,237,228,.4); transition:color .18s, transform .18s; }
/* arrow hover also removed */

.vcard-propose{ align-items:center; justify-content:center; text-align:center; gap:11px;
  background:repeating-linear-gradient(135deg, transparent 0 9px, rgba(255,255,255,.03) 9px 18px); }
.vcard-propose .vp-plus{ font:300 38px/1 var(--grot); color:var(--ice-bright); }
.vcard-propose .vp-t{ font:600 13px/1.2 var(--mono); letter-spacing:.14em; text-transform:uppercase; color:#f4ede4; }
.vcard-propose .vp-sub{ font-size:12.5px; line-height:1.5; color:rgba(244,237,228,.6); max-width:32ch; }

/* mobile: drop the carousel, stack as a normal vertical list (matches the 1024
   site-wide mobile line — the 761-1024 mid-range cropped the third card) */
@media (max-width:1024px){
  .vcat-head{ grid-template-columns:1fr; }
  .vcat-head-title{ min-height:180px; }
  .vcat-head-spark{ display:none; }
  .vnav-stack{ display:none; }
  .vcarousel{ overflow:visible; }
  .vstripe{ flex-direction:column; transform:none !important; gap:1px; background:rgba(255,255,255,.1); }
  /* un-pin the hard desktop card width (it's !important-locked above) — stacked cards go full width */
  .vstripe .vcard{ flex:none !important; width:auto !important; min-width:0 !important; max-width:none !important; border-right-width:1px; }
}

/* #catalog — two-shape block model on the dark Verticals section:
   ORANGE is the background (bottom) shape — it shows through the 1px seams and
   at every rounded corner — and the blocks themselves are dark-grey (the
   section's own fill) rounded rects sitting on top. No borders. */
#catalog .vcat-head{ gap:1px; background:var(--ice); }
/* foreground blocks scaled from the CENTRE: 99.5% wide / 99% tall (user-tuned) —
   a hairline orange reveal at the sides, slightly deeper above and below */
#catalog .vcat-head > .c{ border:0; margin:0; background:#262626; transform:scale(.995, .99); }
#catalog .vcarousel{ background:var(--ice); }
#catalog .vstripe{ gap:1px !important; background:var(--ice); }
/* Height is a WHOLE number of modules, chosen per viewport by app.js (fitVerticalCards):
   the module shrinks with the viewport but the copy does not, so a hard-coded count either
   left a module of dead space at wide widths or clipped the text at narrow ones. The value
   below is only the pre-JS fallback. */
#catalog .vcard{ border:0; background:#262626; transform:scale(.995);
  height:calc(var(--u) * 2 + 2px); overflow:hidden;
  padding:20px 22px 18px; gap:10px; }
/* nav arrows: ONE vertical two-shape block — orange ground, two dark-grey
   button modules stacked on it */
/* SOLID ground behind the carousel + arrow column (desktop AND mobile): the wrap's
   background-attachment:FIXED mosaic doesn't align with the layout, so it showed through
   the rounded corners as randomly-clipped tile fragments ("rubbish" behind the arrows).
   The body sits on plain section dark; the pattern stays in the decorative frame only. */
#catalog .vcat-body{ background:#262626; }
/* GROUND copper, FACES dark — the two-shape, not the other way round. The copper is the
   block's background and shows as the frame around each dark arrow face; recolouring the
   faces instead turned the controls into two orange slabs. */
/* SAME AS A CARD (user request), which the arrows were not. .vnav-stack carried
   border-radius:22px from the rule up at the generic tier, so its orange was a rounded PILL
   with the dark page showing outside its own corners, and the tiles sat inside it at 21. A
   card is built the other way round: the orange is a square, full-bleed FIELD and the dark
   tile lies ON it at 22, so the ground fills the corners solid instead of reading as a ring.
       #catalog .vstripe { background:var(--ice); gap:1px }   <- field, no radius
       #catalog .vcard   { background:#262626; border-radius:22px }
   Square here too — and .vcarousel around it is orange as well, so the stack's field now runs
   straight into the stripe's instead of floating in front of it. */
#catalog .vnav-stack{ border:0; background:var(--ice); gap:1px; padding:1px; overflow:hidden;
  border-radius:0; }
#catalog .vnav-btn{ background:#262626; border-radius:22px; color:#f4ede4; }
#catalog .vnav-btn:hover{ color:var(--ice-bright); transform:none; background:#1b1b1b; }
/* disabled: keep the dark module at full strength — only the arrow goes quiet */
#catalog .vnav-btn:disabled{ opacity:1; background:#262626; color:rgba(244,237,228,.28); }
#catalog .c::after,
#catalog .vcard::after{ content:none; }

/* ============================================================
   RELEASE POLISH — 2026-06-20
   A11y baseline + honest placeholder states + reduced-motion.
   Pure additive; no token changes, no layout reflow.
   ============================================================ */

/* ----- skip link (visually hidden until focused) ----- */
.skip-link{
  position:fixed; top:8px; left:8px; z-index:200;
  padding:10px 14px; background:var(--ice); color:#fff;
  font:600 12px/1 var(--mono-read); letter-spacing:.1em; text-transform:uppercase;
  text-decoration:none; border:1px solid var(--ice-deep);
  transform:translateY(-150%); transition:transform .18s;
}
.skip-link:focus{ transform:translateY(0); outline:2px solid #fff; outline-offset:2px; }

/* ----- focus rings on every interactive surface ----- */
/* WCAG 2.4.7 + 2.4.13: 2px copper ring at 2px offset, ≥3:1 against paper bg */
.btn:focus-visible,
.nav-ico:focus-visible,
.nav-links a:focus-visible,
.nav .brand:focus-visible,
.fcol a:focus-visible,
.footer .fbottom a:focus-visible,
.skip-link:focus-visible{
  outline:2px solid var(--ice);
  outline-offset:2px;
}
/* primary copper button — invert ring so it reads on the orange fill */
.btn.pri:focus-visible{ outline-color:var(--navy); outline-offset:3px; }

/* ----- selected text — readable on copper tint ----- */
/* original kept white-on-22%-copper which fades on cream paper; ink stays legible */
::selection{ background:rgba(226,87,13,.28); color:var(--ink); }

/* ----- honest "coming soon" state for placeholder links ----- */
/* applied to dead href="#" and bare platform-homepage placeholders */
.btn[aria-disabled="true"],
a[aria-disabled="true"]{
  cursor:not-allowed; opacity:.55;
}
.btn[aria-disabled="true"]:hover,
a[aria-disabled="true"]:hover{
  transform:none; background:initial; border-color:var(--hair-2); box-shadow:none;
}
.btn.pri[aria-disabled="true"]{
  background:var(--ink-faint); border-color:var(--ink-faint); color:#fff; box-shadow:none;
}
.btn.pri[aria-disabled="true"]:hover{ background:var(--ink-faint); }
/* small "soon" tag pinned to the disabled CTA */
.soon-tag{
  font:600 9.5px/1 var(--mono-read); letter-spacing:.16em; text-transform:uppercase;
  padding:4px 7px; border:1px solid currentColor; color:var(--ink-3);
  margin-left:9px;
}
.btn.pri .soon-tag{ color:rgba(255,255,255,.85); border-color:rgba(255,255,255,.4); }

/* nav social — when disabled, dim and disable interaction but keep keyboard focus */
.nav-ico[aria-disabled="true"]{
  opacity:.45; cursor:not-allowed; pointer-events:none;
}

/* ----- reduced-motion baseline ----- */
/* WCAG 2.3.3 (AAA) — translate / transform / scroll animations stripped;
   opacity crossfades stay so state changes still register. */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration:0.01ms !important;
    animation-iteration-count:1 !important;
    scroll-behavior:auto !important;
  }
  .vstripe{ transition:none !important; }
  .btn, .btn .arr, .vcard, .vcard-arr, .nav-ico,
  .vnav-btn, .vstatus, .skip-link{
    transition:opacity .18s, background-color .18s, color .18s, border-color .18s !important;
    transform:none !important;
  }
  .btn:hover, .vcard:hover{ transform:none !important; }
}

/* ============================================================
   TYPE-FIX PASS — 2026-06-20
   Mono-as-display commit: Monofonto carries h1/h2/h3 + labels;
   Figtree stays as body voice. Hierarchy comes from size+weight,
   not by stretching to 80px. Lede sizes unified to one scale.
   ============================================================ */

:root{
  --display-font:'Monofonto', ui-monospace, 'JetBrains Mono', monospace;
}

/* H1 (display) — mono runs ~30% wider per glyph than humanist sans,
   so the ramp lands lower: H1 ≤ 48px, H2 ≤ 32px. Felt step from hero
   to section is reasserted by SIZE+WEIGHT and the unified type voice. */
.display{
  font-family:var(--display-font);
  font-size:clamp(28px, 3.8vw, 48px);
  font-weight:600;
  letter-spacing:-0.01em;
  line-height:1.05;
}
.hero-cell .display{
  font-size:clamp(26px, 3.5vw, 44px);
}

.h2{
  font-family:var(--display-font);
  font-size:clamp(20px, 2.4vw, 30px);   /* nudged down: caps read ~7% bigger */
  font-weight:600;
  letter-spacing:0.04em;                 /* mono caps need open tracking */
  text-transform:uppercase;
  line-height:1.12;
}
.oneslice-h{
  text-transform:uppercase;
  letter-spacing:0.04em;
  font-size:clamp(16px, 1.9vw, 24px);
}
/* Areas manifesto h2 — long sentence on dark, stays mixed-case Figtree */
.vcat-intro{ text-transform:none; letter-spacing:-0.015em; }

.h3{
  font-family:var(--display-font);
  font-size:clamp(18px, 1.7vw, 22px);
  font-weight:600;
  letter-spacing:0;
  line-height:1.15;
}

.doorcard h3{
  font-family:var(--display-font);
  font-size:clamp(20px, 2vw, 26px);
  font-weight:600;
  letter-spacing:0;
  max-width:20ch;
}

.oneslice-h{
  font-family:var(--display-font);
  font-size:clamp(18px, 2.1vw, 26px);
  font-weight:600;
  letter-spacing:-0.005em;
}

/* sc h4 — kept in body sans (16px mono reads cramped); bump weight so
   it actually leads its own card */
.sc h4{
  font-family:var(--grot);
  font-size:17px;
  font-weight:650;
  letter-spacing:-0.005em;
  line-height:1.25;
}

/* vcat-intro (h2 in dark Areas section) — keep Figtree; a 27px mono
   manifesto sentence on dark would be too heavy and slow to read */
.vcat-intro{ font-family:var(--grot); font-weight:400; }

/* LEDE — unified scale, replaces ad-hoc inline font-size overrides */
.lede{ font-size:clamp(16px, 1.4vw, 18px); line-height:1.55; }
.lede.sm{ font-size:14.5px; line-height:1.5; }

/* hero coordinate — slightly more tracking now that it sits beside a mono H1 */
.hero-coord{ letter-spacing:.14em; }

/* ============================================================
   VISION — 2026-06-21
   Header band (mirrors Verticals head: title / intro / mark)
   + V3 diptych manifesto body (orange + short side).
   ============================================================ */
/* ----- right-column header cell (sits above .vision-side) */
.vis-head-title{
  display:flex; flex-direction:column; gap:18px; justify-content:space-between;
  padding:clamp(22px,2.4vw,32px);
}
.vis-bigtitle{
  font-family:var(--mono-display); font-weight:400;
  font-size:clamp(54px,8vw,118px); line-height:.82;
  letter-spacing:.04em; text-transform:uppercase; color:var(--ink);
  display:block;
}

/* ── MISSION · THE INSTRUMENT ────────────────────────────────────────────────
   Replaces the lede paragraph beside the manifesto: the section's claim shown
   instead of told. A copper hand sweeps the dot field like a radar; every dot it
   passes flares and then cools back to grey — sources touched and let go. A few
   dots do not cool: those are the records that came back verified, and they stay
   copper, so the pie fills in over a few turns and keeps what it earned. Nothing
   assembles and falls apart on a loop; the thing simply RUNS.

   Why it reads as a pie: the lit dots are a disc, the hand is the cut, and the
   wedge trailing it is the slice — the same figure as the logo and the 3D scene,
   drawn by a machine rather than posed.

   The vocabulary is the page's own module (rounded square, grid, grout) at dot
   scale, so it reads as an instrument inside the paper block rather than as more
   of the page's mosaic.

   The whole thing is ONE rotating layer plus an opacity pulse on the two dozen
   verified dots: no JS runs per frame, nothing can reflow, and nothing repaints.
   Their delay is derived from each dot's ANGLE, which is what keeps the pulse
   under the hand — see visionField() in app.js. app.js runs it only while the
   block is on screen. */
.vis-field{ position:relative; flex:1; min-height:0; display:grid;
  --vf-dur:6.5s;
  grid-template-columns:repeat(var(--vf-n,16), 1fr); grid-auto-rows:1fr; gap:3px; }
/* THE DOTS CARRY THE SWEEP — the hand and its wedge are gone, so what travels round the
   disc is the dots lighting and cooling in turn.
   OPACITY, NEVER COLOUR. The first version animated background-color on 165 dots; colour
   cannot be composited, so every frame repainted the whole square, on the same threads that
   carry the WebGL scene and the fixed 3D frame — and it started exactly as the mission
   section scrolled in, which is where the 3D frame was reported to judder. Each disc dot is
   copper at rest and simply brightens, which the compositor can do without repainting
   anything. Same wave, none of the paint. */
.vis-field .vf{ border-radius:3px; background:var(--seam); opacity:.5; }
.vis-field .vf.vf-out{ opacity:.13; }                          /* outside the disc: the paper it sits on */
.vis-field .vf.vf-in{ background:var(--ice); opacity:.18; }    /* the pie itself, at rest */
.vis-field .vf.vf-keep{ opacity:.6; }                          /* the records that came back verified */

/* NO SWEEP. There was a rotating hand with a trailing wedge over the dots; it is gone at
   the user's request, and with it the last thing here that moved across the field. What
   remains is the field itself: the disc reads from which dots are lit at all, and the
   verified ones breathe. */

/* the wave. Each dot's delay comes from its ANGLE (see visionField in app.js), so the
   brightening runs round the disc like a hand that is no longer drawn: a dot flares as its
   turn comes and cools over the next third of the cycle. */
.vis-field.is-live .vf.vf-in{
  animation:vfWave var(--vf-dur) linear infinite both;
  animation-delay:calc(var(--a,0) * -1ms);
}
@keyframes vfWave{
  0%   { opacity:1;   transform:scale(1.25); }
  8%   { opacity:.92; transform:scale(1); }
  40%  { opacity:.18; transform:scale(1); }
  100% { opacity:.18; transform:scale(1); }
}
/* the verified ones never go all the way down — the pie keeps what it confirmed */
.vis-field.is-live .vf.vf-keep{ animation-name:vfWaveKeep; }
@keyframes vfWaveKeep{
  0%   { opacity:1; transform:scale(1.25); }
  9%   { opacity:1; transform:scale(1); }
  45%  { opacity:.6; transform:scale(1); }
  100% { opacity:.6; transform:scale(1); }
}
/* the still frame for anyone who asked not to be moved */
@media (prefers-reduced-motion:reduce){
  .vis-field .vf{ animation:none !important; }
  .vis-field .vf.vf-keep{ opacity:1; }
}

/* ----- diptych body (restored) ----- */
.vision-mani{
  background:var(--paper-fill); color:var(--ice);
  padding:clamp(36px,4.5vw,72px);
  display:flex; flex-direction:column; justify-content:center; align-items:center;
  text-align:center;
  gap:clamp(28px,4vh,48px);
  /* NO min-height. It was clamp(360px,42vw,520px), which at desktop widths came out taller
     than the rows the block spans — so the block quietly stretched its grid rows and the
     mosaic bands above and below it came out ~9% short of a module. The row span is the
     height; on mobile, where the grid is one column, the copy sets it. */
  min-height:0;
}
.vision-mani .lab{
  font:600 11px/1 var(--mono-read); letter-spacing:.22em; text-transform:uppercase;
  color:rgba(255,255,255,.7);
}
.vision-mani h2{
  font-family:var(--display-font);
  /* 55px ceiling = the former inline value on the h2, now responsive below it */
  font-size:clamp(32px,4.4vw,55px); font-weight:600;
  letter-spacing:-.01em; text-transform:uppercase; line-height:1.0;
  margin:0;
}
/* emphasis: the narrative anchors (subject · problem · solution) flip to heavy INK
   against the orange body, so the manifesto scans in two tones */
.vision-mani h2 strong{ color:var(--ink); font-weight:700; }
.vision-mani .sig{
  font:600 10.5px/1 var(--mono-read); letter-spacing:.18em; text-transform:uppercase;
  color:rgba(255,255,255,.55);
  align-self:flex-end;
}
.vision-side{
  display:flex; flex-direction:column; gap:18px;
  padding:clamp(26px,3vw,40px);
}
.vision-side .seclabel{ margin:0; }
.vision-side .lede{
  margin:0; font-size:14.5px; line-height:1.55; color:var(--ink-2);
}
.vision-side .pullquote{
  margin-top:auto; padding-top:18px; border-top:1px solid var(--seam);
  font:500 13.5px/1.5 var(--mono-read); letter-spacing:.04em; color:var(--ink-3);
}
.vision-side .pullquote .ice{
  color:var(--ice); text-transform:uppercase; letter-spacing:.08em;
}
@media (max-width:1000px){
  .vision-mani h2{ font-size:clamp(28px,5vw,40px); }
  .vis-bigtitle{ font-size:clamp(36px,9vw,64px); }
}

/* ============================================================
   HERO — 2026-06-20 (V4 editorial title page, merged block)
   Single .hero-cover block sized to match the 3D pie height.
   Internal layout: centered .hero-body + bottom-pinned strip.
   ============================================================ */
.hero-cover{
  padding:32px 22px;
  display:flex; flex-direction:column; gap:0;
  justify-content:space-between; align-items:stretch;
  text-align:center;
}
.hero-body{
  flex:1 1 auto;
  display:flex; flex-direction:column; gap:18px;
  justify-content:center; align-items:center;
}
.hero-cover .kicker{
  font:600 clamp(13px, 1.15vw, 16px)/1.3 var(--mono-read); letter-spacing:.28em; text-transform:uppercase;
  color:var(--ice);
}
.hero-cover .mark{
  font-family:var(--mono-display);
  font-size:clamp(54px, 7.4vw, 96px);
  line-height:.85; letter-spacing:.04em;
  color:var(--ink);
  margin:6px 0 4px;
}
.hero-cover .tag{
  font-family:var(--display-font);
  font-size:clamp(15px, 1.7vw, 21px); font-weight:600;
  letter-spacing:.01em; text-transform:uppercase;
  color:var(--ink); max-width:30ch;
}
.hero-cover .rule{
  width:30px; height:1px; background:var(--ice); margin:8px 0;
}
.hero-cover .lede{
  margin:0; max-width:50ch; text-align:center; font-size:clamp(16px, 1.35vw, 18.5px);
}
.hero-cover .cta-row{ margin-top:8px; justify-content:center; }

/* bottom-pinned strip inside the merged cover block — separated by a hairline
   rule, no separate block frame so no corner-tab overlap with the cover */
.hero-strip-inline{
  margin-top:18px; padding-top:18px;
  border-top:1px solid var(--seam);
  display:flex; align-items:center; gap:16px; justify-content:center;
  font:600 10.5px/1 var(--mono-read); letter-spacing:.18em; text-transform:uppercase;
  color:var(--ink-3); flex:none;
}
.hero-strip-inline .dot{ width:6px; height:6px; background:var(--ice); flex:none; }

/* ── HERO reworked onto the MODULAR GRID (2026-07-07) ─────────────────────────
   The editorial cover is now a seam-ground grid of rounded paper module faces —
   kicker · MCPIE · tag · lede · orange CTA · 01·02·03 strip + decorative voids —
   matching the two-shape modular system used in the header / VISION / stack. */
/* seam-grey SQUARE ground behind the rounded modgrid, so the hero block's outer corners
   read as grey fillets (like every other grid cell) instead of showing white through the
   modgrid's 18px rounded corners. */
.hero-cover{ padding:0 !important; display:block; text-align:left; overflow:hidden; background:var(--seam); border-radius:0; }
.hero-modgrid{
  height:100%;
  display:grid;
  grid-template-columns:repeat(6,1fr);
  grid-template-rows:repeat(6,minmax(0,1fr));   /* 6 rows since the 01·02·03 step row was removed */
  gap:2px; padding:2px;
  background:var(--seam);
  border-radius:18px;
}
.hero-modgrid .hmod{
  min-width:0; min-height:0; overflow:hidden;
  background:var(--bg); border-radius:20px;   /* 22 − 2px gap on the seam ground */
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:6px; padding:clamp(8px,1.3vw,16px); text-align:center;
}
.hmod--mark .mark{ margin:0; font-size:clamp(46px,6.6vw,86px); line-height:.85; }
.hmod--kicker .kicker{ color:var(--ice); }
.hmod--tag .tag{ max-width:none; }
.hmod--lede .lede{ margin:0; max-width:none; text-align:center;
  font-size:clamp(12px,1.05vw,13.5px); line-height:1.5; }
/* merged tagline + lede in one module face */
.hmod--intro{ gap:clamp(10px,1.4vw,18px); }
.hmod--intro .tag{ max-width:none; }
.hmod--intro .lede{ margin:0; max-width:none; text-align:center;
  font-size:clamp(12px,1.05vw,13.5px); line-height:1.5; }
/* CTA as a STRUCTURED action module: display-type label, orange arrow-chip module
   anchored bottom-right (same module-in-module language as the vnav buttons). The
   label centres vertically — the eyebrow above it was removed per user request.
   White ground, ink label. */
/* the CTA is a white module holding ONE horizontal orange button that spans its width
   and carries the label + arrow (user request — the label moved inside the button) */
/* ONE shared control height for every dashboard-style control — the hero button, the
   endpoint field AND the nav's dashboard pill are pinned to it, so they can never drift
   out of alignment (defined on :root so the bar can read it too) */
:root{ --field-h:30px; }
.hmod--connect .cn-row{ height:var(--field-h); }
.hero-modgrid .hmod--cta{
  text-decoration:none;
  /* ground inherits .hmod's var(--bg) — a pure-white block read as a bright patch next to
     the paper modules once the label moved inside the orange pill */
  color:var(--ink);
  flex-direction:row; align-items:center; justify-content:stretch;
  padding:11px clamp(12px,1.3vw,18px);
}
.hmod--cta .cta-chip{
  flex:1; min-width:0; height:var(--field-h); border-radius:12px;
  background:var(--ice); color:#fff;
  display:flex; align-items:center; justify-content:center; gap:10px;
  font:600 clamp(10.5px,1vw,12.5px)/1 var(--mono-read);
  letter-spacing:.14em; text-transform:uppercase;
  transition:background .18s ease;
}
.hmod--cta .cta-chip .arr{ font:600 14px/1 var(--grot); transition:transform .18s ease; }
.hmod--cta:hover .cta-chip{ background:var(--ice-deep); }
.hmod--cta:hover .cta-chip .arr{ transform:translateX(3px); }

/* ── HERO · connect-your-agent module (2026-07-14) ───────────────────────────
   Fills the old decorative void beside the CTA, so the hero reads as two doors:
   CONTRIBUTE (left, orange) / CONNECT YOUR AGENT (right, paper). The face shows
   only the endpoint — the module is ~86px tall, so a 5-line JSON block does not
   fit and does not need to: the CLIPBOARD carries the full mcpServers config. */
.hero-modgrid .hmod--connect{
  align-items:stretch; justify-content:center; text-align:left;
  gap:5px; padding:10px clamp(10px,1.1vw,14px);
}
/* endpoint as a TERMINAL FIELD: dark input module with the copy chip flush inside its
   right edge — echoes the HIW terminal instead of loose code floating on paper */
.hmod--connect .cn-row{ display:flex; align-items:center; gap:8px;
  background:#262626; border-radius:10px; padding:4px 4px 4px 12px; }
.hmod--connect .cn-ep{
  flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font:500 clamp(10px,.95vw,12px)/1 var(--mono-read); color:#f4ede4;
}
.hmod--connect .cn-copy{
  flex:none; cursor:pointer; border:0; border-radius:7px;
  background:var(--ice); color:#fff; padding:6px 10px;
  font:600 clamp(8.5px,.75vw,9.5px)/1 var(--mono-read); letter-spacing:.12em;
  text-transform:uppercase; transition:background .2s ease;
}
.hmod--connect .cn-copy:hover{ background:var(--ice-deep); }
.hmod--connect .cn-copy.is-done{ background:#343434; }   /* copied — quiet dark, still visible inside the dark field */
/* the link now lives in its OWN filler strip below the buttons (was inside the connect
   block), so it is scoped to the hero grid and sized for a standalone line */
.hero-modgrid .cn-how{
  font:600 clamp(10px,.9vw,12px)/1 var(--mono-read); letter-spacing:.14em;
  text-transform:uppercase; color:var(--ice); text-decoration:none;
}
.hero-modgrid .cn-how:hover{ text-decoration:underline; }
.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; }
/* ── HERO WORDMARK — the footer's animated module-matrix MCPIE, reused in the mark
   module and re-tinted for the PAPER ground (2026-07-07). Same 29x7 two-shape SVG,
   same wmCycle keyframes + app.js builder, so the page opens and closes on the same
   breathing wordmark — a bookend. Palette is the SITE's own module pair (seam ground
   + orange ink), so it also reads as one system with the hmod grid around it. */
.hmod--wm{ padding:clamp(12px,1.9vw,24px); overflow:hidden; }
.hero-wordmark{
  --wm-ground:var(--bg);               /* paper (= the hmod face) — letters flip paper <-> copper,
                                          so the ground blends in and the mark reads as clean orange */
  --wm-ink:var(--ice);                 /* copper — the letter/accent tone */
  width:100%; height:100%; min-height:0;
  display:flex; align-items:center; justify-content:center;
}
.hero-wordmark .wm-svg{ width:100%; max-width:86%; height:auto; max-height:100%; display:block; margin:0 auto; }
/* resting / reduced-motion two-tone (wm-b is the inverse; the builder offsets it half
   a cycle so at any instant the matrix shows the mixed two-tone) */
.hero-wordmark .wm-a rect{ fill:var(--wm-ground); }
.hero-wordmark .wm-a rect.fg{ fill:var(--wm-ink); }
.hero-wordmark .wm-b rect{ fill:var(--wm-ink); }
.hero-wordmark .wm-b rect.fg{ fill:var(--wm-ground); }
/* ambient scatter around the letters — light-grey ground + paper (background) rounded
   foreground, i.e. the site's own two-shape module (grey ground peeking at the corners
   of a paper face). Static; only the wm-a/wm-b letters cycle. */
.hero-wordmark .wm-void rect{ fill:#ddd4c6; }
.hero-wordmark .wm-void rect.fg{ fill:var(--bg); }

/* visually-hidden accessible brand name (the animated SVG is aria-hidden) */
.wm-sr{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0,0,0,0); clip-path:inset(50%); white-space:nowrap; border:0; }

/* the 01·02·03 pipeline — one module block per step (Weave / Verify / Query),
   number over label, orange numeral accent */
.hmod--step{ gap:5px; }
.hmod--step .step-n{ font:600 clamp(13px,1.25vw,16px)/1 var(--mono-read); letter-spacing:.06em; color:var(--ice); }
.hmod--step .step-l{ font:600 clamp(9px,0.9vw,11px)/1 var(--mono-read); letter-spacing:.18em; text-transform:uppercase; color:var(--ink-3); }

/* mobile: the .c--band rule flattens .hero-modgrid to block flow — keep the seam
   ground, stack the paper faces with 2px seam channels, drop the decorative voids */
@media (max-width:1024px){
  .c--band > .hero-modgrid{ background:var(--seam) !important; padding:2px !important; border-radius:0; }
  .hmod--void{ display:none !important; }
  .hero-modgrid .hmod{ margin:2px 0; min-height:60px; padding:16px 18px; border-radius:20px; }
  .hmod--mark{ min-height:118px; }
  .hmod--mark .mark{ font-size:clamp(52px,12vw,84px); }
  /* the animated wordmark: natural aspect from width (height:100% would collapse to 0
     against the c--band auto-height cell), capped so it stays a legible mark not a band */
  .hmod--mark.hmod--wm{ min-height:0; padding:20px 18px; }
  .hero-wordmark{ height:auto; }
  .hero-wordmark .wm-svg{ width:100%; height:auto; max-height:none; max-width:440px; }
}

/* ============================================================
   CTA — 2026-06-20 (rev)
   Solid copper, no inset stroke, rounded corners. Keeps the
   printed-depth bottom edge and warm halo for weight.
   Applied system-wide to .btn.pri (nav .sm and hero/invite/door).
   ============================================================ */
.btn.pri{
  background:var(--ice);
  border-color:var(--ice);
  color:#fff;
  letter-spacing:.14em;
  padding:15px 22px;
  border-radius:10px;
  box-shadow:
    0 2px 0 var(--ice-deep),
    0 10px 24px rgba(226,87,13,.28);
  transition: background .18s, box-shadow .18s, transform .12s;
}
.btn.pri.sm{
  padding:12px 16px;
  border-radius:8px;
  letter-spacing:.14em;
}
.btn.pri:hover{
  background:var(--ice-deep);
  box-shadow:
    0 2px 0 #7d2c04,
    0 12px 28px rgba(226,87,13,.38);
}
.btn.pri:active{
  transform:translateY(1px);
  box-shadow:
    0 0 0 var(--ice-deep),
    0 4px 14px rgba(226,87,13,.28);
}
.btn.pri::after{ display:none; }   /* no corner brackets — rounded radius carries it */
.btn.pri .arr{ font-weight:600; opacity:1; }

/* ============================================================
   MOBILE TYPE & ACCESSIBILITY TIER — 2026-07-14 audit
   Measured at 375px: 30 of 51 text groups rendered under 14px, several
   informative labels sat at 1.5–3.8:1 contrast, and three tap targets were
   under 24px. This tier lifts the MOBILE floors only (everything scoped
   ≤1024 — desktop is untouched) and remaps the failing colors to in-palette
   passes: --ink-faint/--ink-3 → --ink-2 (7.1:1 on paper), --ice → --ice-deep
   (6.3:1) for small labels, CTA fills → #c2410b (5.1:1 under white — the same
   copper the 3D glass uses for attenuation, so it stays in family).
   WCAG: 1.4.3 contrast AA (4.5:1 below 18.66px bold) · 2.5.8 target ≥24px.
   Inline-styled blocks (#how terminal, #getstarted gs-panes, footer h5) are
   overridden with id-prefixed selectors — they win on SPECIFICITY, since the
   inline <style> comes after this sheet in source order.
   ============================================================ */
@media (max-width:1024px){
  /* ---- body copy floors (16px = Apple HIG / Material body size; secondary 15px) ---- */
  .hero-cover .lede, .hmod--intro .lede{ font-size:16px; line-height:1.55; }
  .stkrow p{ font-size:15px; line-height:1.55; }
  .vcard p{ font-size:15px; line-height:1.55; }
  #how .sc p{ font-size:15px; line-height:1.55; }
  .hband-note{ font-size:15px; line-height:1.55; }
  #getstarted .gs-body{ font-size:16px; line-height:1.55; }   /* inline-styled → id wins */
  /* card titles keep a clear step above their body copy */
  .stkrow h4{ font-size:17px; }
  #how .sc h4{ font-size:17px; }
  .vcard .vt{ font-size:17px; }

  /* ---- micro-caps labels: 11px floor + AA colors ---- */
  .hmod--kicker .kicker{ font-size:12px; color:var(--ice-deep); }
  .hmod--step .step-n{ font-size:15px; color:var(--ice-deep); }
  .hmod--step .step-l{ font-size:11px; color:var(--ink-2); }
  .vstatus{ font-size:11px; }
  .vsub{ font-size:12.5px; }
  #getstarted .gs-eyebrow{ font-size:11px; }
  #how .sc .k{ font-size:11px; color:var(--ink-2); }

  /* ---- hero connect module (was 8px type at 1.9:1 + an 8px-tall link) ---- */
  /* touch targets are bigger here (copy button min-height 32) — grow the shared control
     height to match so the field still contains it and the button stays aligned */
  .hero-modgrid{ --field-h:44px; }
  .hero-modgrid .hmod--connect{ gap:8px; padding:14px 16px; }
  .hmod--connect .cn-ep{ font-size:13.5px; }
  .hmod--connect .cn-copy{ font-size:11px; padding:9px 14px; min-height:32px; background:var(--ice-deep); }
  .hmod--connect .cn-copy:hover{ background:#c2410b; }
  .hero-modgrid .cn-how{ font-size:12px; color:var(--ice-deep);
    display:inline-flex; align-items:center; min-height:32px; }

  /* ---- CTAs: readable labels + AA fills ---- */
  /* no block fill: the orange pill inside carries the label now, so the old burnt-orange
     ground (from when the label sat directly on the block) read as orange-on-orange */
  .hero-modgrid .hmod--cta{ font-size:13.5px; }
  /* the header CTA is a PAPER TILE with orange text by design (.nav .navcta .btn.pri,
     specificity 0,3,1 — it beat this tier's earlier class-only attempt); keep the chip,
     deepen the label: --ice 3.5:1 → --ice-deep 6.3:1 on paper */
  .navcta .btn.pri{ font-size:12.5px; }
  .nav .navcta .btn.pri, .nav.solid .navcta .btn.pri{ color:var(--ice-deep); }
  /* skip-link: white on --ice was 3.75 — deepen the fill */
  .skip-link{ background:var(--ice-deep); }
  /* footer h5 category headers sit on the footer cell's #5c5246 ground: orange
     rgb(249,115,22) = 2.7:1 there. Pale peach keeps the warm hierarchy at 5.8:1. */
  .footer .fcol h5{ color:#ffd9c2; }
  #getstarted .gs-cta{ font-size:13px; min-height:44px; color:var(--ice-deep); border-color:var(--ice-deep); }
  #getstarted .gs-cta.is-pri{ font-size:13px; color:#1a1a1a; }   /* dark-on-bright passes at 6.7:1 */

  /* ---- #how terminal chrome ---- */
  /* the orbit is a direct div child of a .c--band, so the mobile flatten rule
     `.c--band > div{ background:transparent }` blanks its panel — restore the PC dark-grey ground */
  #how .hiw-orbit{ background:#262626 !important; }
  #how .term{ font-size:13px; }
  #how .term-title{ font-size:11px; }
  #how .term-status{ font-size:11px; }
  #how .hiw-cap{ font-size:12px; }

  /* ---- footer: lift the dark-ground small text ---- */
  .footer .fcol h5{ font-size:12px; }
  .footer .fbottom .mono{ font-size:13px; color:rgba(244,237,228,.66); }

  /* ---- verticals: 3 cards + read-more (markup ships .v-collapsed; the rule
          lives only in this tier, so the desktop carousel always sees all cards) ---- */
  #vstripe.v-collapsed > a:nth-of-type(n+4){ display:none; }
  /* full-width, edge-for-edge with the stacked cards (left 1 / right 374 = the vcard extent,
     the 1px each side is the vstripe seam). Was inset 16px — user wanted it card-width. */
  /* THE BUTTON'S GROUND. Unlike the cards above it, this button is NOT inside #vstripe — it
     sits at .vcat-wrap level, and that wrap is #262626. So it had no orange under it at all,
     and had been faking the seam with a transparent border + border-box gradient. On a dark
     wrap that does not read as a shape under a shape, it reads as an outline with dark on
     both sides of it, and the wrap shows through outside the rounded corners as dark notches.
     Give it the real construction instead, and specifically the CARD's, not a rounded box of
     its own: look at how a .vcard is made and the ground is not a shape at all, it is a FIELD.
         #catalog .vstripe { background:var(--ice); gap:1px }   square, full-bleed, no radius
         #catalog .vcard   { background:#262626; border-radius:22px }
     The orange runs edge to edge behind everything and the dark tiles sit ON it at radius 22,
     so the ground fills the corners solid instead of being visible as a ring. My first go gave
     this ground border-radius:22 and side margins, which turns it into a floating orange pill
     with the dark wrap showing outside its corners — the exact thing that looked wrong. Square
     and flush, and the seam is the 1px padding, matching the stripe's 1px inter-card gap. */
  .v-more-ground{ background:var(--ice); padding:1px; margin:0; }
  .v-more{ display:flex; align-items:center; justify-content:center; gap:8px;
    width:100%; min-height:60px; margin:0; cursor:pointer;
    /* TWO-SHAPE, like every other module on the page (user request). I had flattened this to
       one solid orange plate, which threw the structure away — the family is always a
       BACKGROUND shape carrying a rounded FOREGROUND shape on top, and the ground reads
       through as a thin frame plus the four corner fillets:
           .nav-zone   ground seam, 1px padding, faces at radius 22
           .stkblk     ground seam, children held off it by 2px margins
           .wm-a/.wm-b ground rect + inset rounded rect, exact colour inverses
       Here the GROUND is orange and the FOREGROUND is the dark grey. border-box paints the
       ground, padding-box paints the face, and the transparent border is the gap between
       them — 3px rather than the 2 it used to be, because at 2 the ground read as a hairline
       border on a dark button instead of as the shape under it. The radii stay concentric on
       their own: the padding box inherits 22 - 3 = 19. */
    border:0; border-radius:22px; background:#262626; color:#f4ede4;   /* the card's own radius */
    font:600 12.5px/1 var(--mono-read); letter-spacing:.14em; text-transform:uppercase; }
  .v-more:focus-visible{ outline:2px solid var(--ice-bright); outline-offset:3px; }
  /* T1 — hide-on-mobile utility (user: don't display that block on mobile). The 2nd selector beats
     the #vision .c--band .c:not(:empty) flatten rule (which would otherwise force display:flex). */
  .hide-on-mobile,
  #vision.gsec .c--band .c.hide-on-mobile{ display:none !important; }
  /* T2 — verticals cards fit their content: the desktop carousel locks a fixed 3-module height +
     overflow:hidden (equal-height cards); on mobile they stack, so let them grow so text isn't clipped */
  #catalog .vcard{ height:auto !important; min-height:200px; overflow:visible; }
  /* T4 — HOW IT WORKS steps as a 2×2 grid on mobile (was one stacked column) */
  #how .subgrid{ grid-template-columns:1fr 1fr !important; }
}
/* the read-more button exists only below the carousel breakpoint */
@media (min-width:1025px){ .v-more, .v-more-ground{ display:none; } }

/* ============================================================
   MOBILE LAYOUT PASS — 2026-07-14 (user punch-list)
   Measured before: header blocks were 32 / 56 / 61 / 179px tall and MISSION was
   display:none; 01·02·03 stacked full-width; vcards 342px wide (a phantom
   nav-column in the .vcat-body grid ate 31px); L-rows 137px.
   ============================================================ */
@media (max-width:1024px){

  /* --- 3 · 01/02/03 as three blocks in a ROW ---------------------------------
     .c--band > div{display:block} flattens the hero modgrid, so the steps stacked.
     Re-flex it: every module claims a full row except the three steps, which are
     consecutive siblings and therefore share the last line. */
  .c--band > .hero-modgrid{ display:flex !important; flex-wrap:wrap; gap:2px;
    align-content:flex-start; }
  .hero-modgrid > .hmod{ flex:0 0 100%; margin:0 !important; }
  .hero-modgrid > .hmod--step{ flex:1 1 0; min-width:0; padding:14px 8px; }
  .hmod--step .step-n{ font-size:17px; }
  /* …and the decorative half-strip goes with them. On DESKTOP it is grid-row 1: a blank
     half-module ABOVE the kicker, capping the top of the hero group. The flex above lays
     out on DOM ORDER, and in the DOM it comes AFTER the kicker — so on mobile it landed
     BELOW it instead, a 60px blank paper block wedged between the kicker and the wordmark,
     capping nothing. It is the hero's only empty .hmod, and [aria-hidden] is already how
     this grid marks a strip decorative — the rule that sizes the strips CARRYING text
     keys off :not([aria-hidden]) for exactly the same reason. */
  .hero-modgrid > .hmod--kicker[aria-hidden="true"]{ display:none; }

  /* --- 7 · every section header block the same height ------------------------
     The titles are inline-styled cells inside .c--band voids (mosaic siblings are
     truly :empty and already hidden), plus the stack's own orange h2 and the dark
     VERTICALS mark. One height for all four. */
  :root{ --sechead-h:104px; }
  #vision .c--band .c:not(:empty),
  #how .c--band .c:not(:empty),
  #getstarted .c--band .c:not(:empty){
    min-height:var(--sechead-h) !important; display:flex !important;
    align-items:center !important; justify-content:center !important;
    text-align:center; padding:14px 18px !important; }
  #stack .stkblk .oneslice-h{ min-height:var(--sechead-h); display:flex;
    align-items:center; justify-content:center; text-align:center; margin:2px 0; }
  .vcat-head-title{ min-height:var(--sechead-h) !important; }
  /* titles scaled to the shared block (inline `font:` shorthand → needs !important) */
  #how .c--band .c:not(:empty) span,
  #getstarted .c--band .c:not(:empty) span{ font-size:30px !important; line-height:1.05 !important; }
  #vision .c--band .c:not(:empty) span{ font-size:40px !important; line-height:1 !important; }
  .vcat-head-title .vmark{ font-size:40px !important; }
  #stack .stkblk .oneslice-h{ font-size:22px; line-height:1.15; }

  /* --- 6 · HOW IT WORKS on ONE line ------------------------------------------
     markup carries `HOW IT<br class="mbr"> WORKS`; the space after the <br> is
     collapsed at the start of a wrapped line on desktop, and becomes the word gap
     here once the break is switched off. */
  #how .mbr{ display:none; }

  /* --- 4 + 5 · verticals cards: FULL width + taller --------------------------
     .vcat-body is a 2-col grid (carousel + nav unit). .vnav-stack is display:none
     on mobile but its GRID COLUMN survived, stealing 31px — that's why the cards
     were 342px, not 375px. Collapse to one column. */
  .vcat-body{ grid-template-columns:1fr !important; }
  .vcard{ min-height:200px; padding:22px 20px; }
  .vcard-propose{ min-height:200px; }

  /* --- 8 · supply-layer cards: content-hugging with the shared --mcard-h floor (IDENTICAL
     heights for all three — release-together depends on it) ------ */
  #stack .stkrow{ min-height:var(--mcard-h); padding:20px 18px; }

  /* --- MISSION above the manifesto (user "move this above") -------------------
     Source order stacks manifesto → MISSION → lede. The MISSION title + lede live
     together in the right band, so ordering the manifesto last lifts that whole
     block above it: MISSION → lede → manifesto. Desktop is grid-row-placed, so
     `order` is a no-op there. */
  #vision .vision-mani{ order:1; }
