/* ============================================================================
   Data Workers — design system (v6 · "clean light, enterprise")
   Token-driven: type · color (themeable) · spacing · elevation · radii.
   Every marketing page consumes this file; refine here, lift the whole site.
   ========================================================================== */

/* ---- self-hosted brand fonts ---- */
@font-face{font-family:'Geist';src:url(fonts/Geist-Regular.woff2) format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Instrument Serif';src:url(fonts/InstrumentSerif-Regular.woff2) format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Instrument Serif';src:url(fonts/InstrumentSerif-Italic.woff2) format('woff2');font-weight:400;font-style:italic;font-display:swap}
@font-face{font-family:'JetBrains Mono';src:url(fonts/JetBrainsMono-Regular.woff2) format('woff2');font-weight:400;font-style:normal;font-display:swap}

:root{
  /* ---- type ---- */
  --sans:'Geist','Hanken Grotesk',system-ui,-apple-system,'Segoe UI',sans-serif; /* @kind font */
  --serif:'Instrument Serif',Georgia,serif; /* @kind font */ /* sparing italic accent only */
  --mono:'JetBrains Mono',ui-monospace,'SFMono-Regular',monospace; /* @kind font */

  /* ---- neutral foreground/background (cool-warm balanced, low saturation) ---- */
  --bg:#FBFAF8;            /* page — warm cream (original) */
  --bg-2:#F4F2EC;          /* recessed wells */
  --card:#FFFFFF;
  --ink:#11201A;           /* primary text */
  --ink-2:#4C5A54;         /* secondary text */
  --ink-3:#8B958F;         /* tertiary / labels */
  --line:#E9E8E3;          /* hairline */
  --line-2:#DCDBD4;        /* stronger divider */
  --dark:#0B100D;          /* dark surfaces */
  --dark-2:#141C18;

  /* ---- brand green family (vivid emerald — original brand green) ---- */
  --dw:oklch(0.58 0.14 156);           /* primary */
  --dw-deep:oklch(0.42 0.105 158);     /* deepest — headlines on light */
  --dw-bright:oklch(0.70 0.16 154);    /* accent / data viz */
  --dw-tint:oklch(0.95 0.035 158);     /* soft fill */
  --dw-tint-2:oklch(0.975 0.018 158);  /* faintest fill */
  --dw-ring:oklch(0.58 0.14 156 / .35);
  --on-dw:#FFFFFF;                     /* text on primary */

  /* ---- flagship gradient (deep-green feature surfaces: hero/feature tiles, hub center) ---- */
  --grad-flag:linear-gradient(160deg, var(--dw-deep), oklch(0.36 0.085 158));

  /* ---- spacing scale (4pt base) ---- */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px;
  --s7:32px; --s8:48px; --s9:64px; --s10:96px; --s11:128px;

  /* ---- radii ---- */
  --r-xs:8px; --r-sm:12px; --r:16px; --r-lg:22px; --r-xl:28px; --r-pill:999px;

  /* ---- elevation (neutral ink, layered, low-opacity) ---- */
  --sh-sm:0 1px 2px rgba(17,32,26,.05), 0 1px 1px rgba(17,32,26,.03);
  --sh-md:0 1px 2px rgba(17,32,26,.04), 0 6px 14px rgba(17,32,26,.06);
  --sh-lg:0 2px 4px rgba(17,32,26,.04), 0 14px 32px rgba(17,32,26,.08);
  --sh-xl:0 6px 14px rgba(17,32,26,.05), 0 30px 64px rgba(17,32,26,.11);

  --maxw:1180px;
  --r:16px; /* legacy alias retained */
}

*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:17px;line-height:1.62;
  font-feature-settings:"ss01","cv01","cv11";-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
a{color:inherit;text-decoration:none}
img{max-width:100%}
::selection{background:var(--dw-tint);color:var(--dw-deep)}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 var(--s8)}
@media(max-width:640px){.wrap{padding:0 var(--s6)}}

/* ============================ nav ============================ */
/* Header blends into the page: same oyster/cream shade as the body, no divider
   line. Pages with a dark hero override this with `.nav.clear` (transparent)
   until the hero scrolls past — that transparent treatment is intentional. */
.nav{position:sticky;top:0;z-index:50;background:var(--bg)}
.nav-in{max-width:var(--maxw);margin:0 auto;padding:0 var(--s8);height:56px;display:flex;align-items:center;gap:var(--s7)}
/* logo + links flush to the viewport's left edge (v6 header treatment, all pages) */
.nav .nav-in{max-width:none;margin:0;padding-left:12px}
@media(max-width:640px){.nav-in{padding:0 var(--s6)}}
/* Wordmark — Geist 700, 15px, sentence-case, matches nav type. */
.logo{font-family:var(--sans);font-size:15px;letter-spacing:.02em;text-transform:none;font-weight:700;
  display:inline-flex;align-items:center;gap:9px;color:var(--ink)}
.logo b{color:var(--ink);font-weight:700}
.logo span{color:var(--ink)!important;font-weight:700}
.logo span b{color:var(--ink);font-weight:700}
.logo-mark{display:block;border-radius:6px;flex:none}
/* One TEXT scale across the whole bar - founder 2026-08-12, option C in the lab
   bar. The 2026-08-11 nav bump took these links to 15.5px but left .nav-cta
   behind at 14.5px, so the right half of the header rendered a size smaller
   than the left. 15.5px/500 now covers .nav-links AND .nav-cta .pilot-link -
   change one, change the other, or the split comes back.
   Book a Demo is deliberately EXCLUDED: as a filled pill it keeps the smaller,
   heavier button type (.btn, 14.5px/550) so its label holds up against the
   green fill. That difference is intentional, not the old bug. */
.nav-links{display:flex;gap:var(--s6);align-items:center;font-size:15.5px;color:var(--ink-2);font-weight:500}
.nav-links a{transition:color .15s;white-space:nowrap}
/* .ddt is the Read More trigger: a disclosure, not a destination, so it is not
   an <a>. It must look and space exactly like its anchor siblings. */
.nav-links .ddt{transition:color .15s;white-space:nowrap;cursor:pointer;font:inherit;
  color:inherit;background:none;border:0;padding:0}
.nav-links a:hover{color:var(--ink)}
.nav-links .on{color:var(--dw);font-weight:600}
.dd{position:relative}
.dd>a{transition:color .18s}
/* The un-hovered rule is what plays on CLOSE, so its .5s delay is the menu's
   grace period (founder 2026-08-12): the panel stays put for half a second
   after the pointer leaves, instead of vanishing the instant the cursor clips a
   corner. Opening stays instant - the .dd:hover rule below carries no delay.
   Keep the >* delay in step, or the contents fade out while the panel lingers. */
.dd-menu{position:absolute;top:calc(100% + 12px);left:-14px;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-sm);box-shadow:var(--sh-lg);padding:7px;min-width:312px;z-index:60;
  opacity:0;visibility:hidden;transform:translateY(-6px) scale(.985);transform-origin:top center;
  transition:opacity .18s ease .5s,transform .2s cubic-bezier(.4,0,.2,1) .5s,visibility 0s linear .7s}
/* Twin of the nav-preload guard in index.html: until the load event, the closed
   panel must simply BE closed rather than animate its way there, or the .5s
   close delay above turns a late stylesheet into a half-second of every
   dropdown hanging open. Inert in the static mockups, which carry no script. */
.nav-preload .dd-menu,.nav-preload .dd-menu>*{transition:none}
.dd-menu::before{content:"";position:absolute;top:-14px;left:0;right:0;height:14px}
.dd:hover .dd-menu{opacity:1;visibility:visible;transform:none;
  transition:opacity .18s ease,transform .2s cubic-bezier(.4,0,.2,1),visibility 0s}
.dd-menu a{display:block;padding:10px 12px;border-radius:var(--r-xs);transition:background .14s}
.dd-menu a:hover{background:var(--bg-2)}
.dd-menu .t{font-weight:600;color:var(--ink);font-size:18.5px;letter-spacing:-.01em}
.dd-menu .s{display:block;font-size:16.5px;color:var(--ink-3);line-height:1.42;margin-top:2px}
.dd-menu>*{opacity:0;transform:translateY(7px);transition:opacity .18s ease .5s,transform .18s ease .5s}
.dd:hover .dd-menu>*{opacity:1;transform:none}
.dd:hover .dd-menu>*:nth-child(1){transition-delay:.04s}
.dd:hover .dd-menu>*:nth-child(2){transition-delay:.07s}
.dd:hover .dd-menu>*:nth-child(3){transition-delay:.10s}
.dd:hover .dd-menu>*:nth-child(4){transition-delay:.13s}
.dd:hover .dd-menu>*:nth-child(5){transition-delay:.16s}
.dd:hover .dd-menu>*:nth-child(6){transition-delay:.19s}
.dd-mega{left:50%;right:auto;min-width:680px;padding:18px 16px;display:grid;grid-template-columns:1.6fr 1fr 1.1fr;gap:6px 26px;
  transform:translateX(-50%) translateY(-6px) scale(.985)}
.dd:hover .dd-mega{transform:translateX(-50%)}
/* Agents mega: lifecycle sections (founder 2026-07-19) - full-width Autopilot
   strip (flagship Conductor featured green) over Understand&Build / Operate&Govern. */
.dd-agents-mega{left:0;right:auto;width:min(1440px,calc(100vw - 380px));min-width:0;grid-template-columns:repeat(5,1fr);transform:translateX(0) translateY(-6px) scale(.985)}
.dd:hover .dd-agents-mega{transform:translateX(0)}
.dd-agents-mega .ds-all{grid-column:1/-1}
/* Founder 2026-08-09: give the Agents mega menu a section title, matching the
   Products menu's "Agentic Platform". grid-column:1/-1 because this menu is a
   5-column grid and a plain child would take one cell - the same reason .ds-all
   above already spans. .dd-mega .ds-h gives it the mono uppercase green, and the
   founder's 2026-08-09 spacing block at the end of this section trims its air.
   Spans correctly at the 3- and 2-column breakpoints too. */
.dd-agents-mega > .ds-mega-title{grid-column:1/-1}
.dd-agents-mega a{white-space:normal}
.dd-mega .ds-h{font-family:var(--mono);font-size:14px;letter-spacing:.16em;text-transform:uppercase;color:var(--dw);
  padding:4px 8px 8px;margin-bottom:4px;border-bottom:1px solid var(--line)}
.dd-mega a{padding:6px 8px;font-size:17px;color:var(--ink-2);line-height:1.3;border-radius:6px}
.dd-mega a:hover{background:var(--bg-2);color:var(--ink)}
.dd-mega .ds-all{color:var(--dw);font-weight:600;margin-top:6px}
.dd-mega .ds-soon{cursor:default;color:var(--ink-2)}
.dd-mega .ds-soon:hover{background:transparent}
.dd-mega .ds-soon .s{display:block;font-size:11px;color:var(--ink-3);margin-top:2px}
/* Products dropdown: same type system as the Agents / Read More megas — mono green
   section head over a hairline, 12.5px links — so all three nav menus read as one
   family instead of the Products one being a size larger (founder, 2026-07-23). */
.dd-products{padding:18px 14px 14px;min-width:346px}
.dd-products .ds-h{font-family:var(--mono);font-size:14px;letter-spacing:.16em;text-transform:uppercase;color:var(--dw);
  padding:0 8px 8px;margin-bottom:6px;border-bottom:1px solid var(--line)}
.dd-products a{padding:7px 8px;border-radius:6px}
.dd-products .t{font-size:18.5px;font-weight:600;color:var(--ink);letter-spacing:0;line-height:1.3}
.dd-products .s{display:block;font-size:16.5px;color:var(--ink-3);line-height:1.55;margin-top:7px}
/* Read More mega: 4 columns (Our Thinking / Free, Open-Source Core / Resources / Plans).
   The Plans column was added 2026-07-23; the grid was widened from 860px/3-col so the
   three content columns keep their line lengths instead of being squeezed. */
.dd-read-mega{left:-14px;right:auto;width:min(1420px,calc(100vw - 440px));min-width:0;grid-template-columns:1.02fr 1.06fr 1fr .74fr;transform:translateY(-6px) scale(.985)}
.dd:hover .dd-read-mega{transform:none}
.dd-read-mega a{white-space:normal}

/* ── Nav menu decisions locked 2026-08-08 ────────────────────────────────────
   Previewed in the lab bar and chosen there: Read More carries Our Thesis and
   Enterprise stacked as a left rail, all three menus card their sections, the
   Products menu is two columns with the product name in brand green, and the
   "Our Blog" call to action sits under Our Thinking. */

/* Read More: four tracks, the first being the rail rather than a column. */
.dd-read-mega{grid-template-columns:minmax(0,321fr) minmax(0,339fr) minmax(0,286fr) minmax(0,355fr)}
.ds-rail{display:flex;flex-direction:column;gap:18px;min-width:0}

/* Carded sections, across all three menus. `--bg-2` is also `.dd-menu a:hover`,
   so a card painted that shade would have an invisible hover: the links inside a
   card hover to the green tint instead. */
.dd-read-mega > .ds-col,.ds-rail > .ds-col,
.dd-agents-mega > .ds-col{background:var(--bg-2);border-radius:10px;padding:10px 8px 8px}
.dd-read-mega .ds-h,.dd-agents-mega .ds-h{border-bottom-color:transparent;padding-bottom:4px}
.dd-read-mega a:hover,.dd-agents-mega a:hover{background:var(--dw-tint)}
/* the agents mega's "All agents →" spans the row, so it is not a card */
.dd-agents-mega > .ds-all{background:none}

/* Products: two columns, carded, product name in brand green.
   `.nav-links a{white-space:nowrap}` above inherits into every dropdown item and
   both megas already undo it; Products needs the same reset now that it wraps,
   or a long tagline holds one line and pushes its column past the panel edge. */
/* `grid-auto-rows:1fr` sized EVERY row equally, including the heading row, so
   the Agentic Platform label was stretched to a full card's height and left a
   gap above its own divider. Only the two CARD rows should be equal. */
.dd-products{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:auto 1fr 1fr;gap:8px 12px;
  width:min(648px,calc(100vw - 370px));min-width:0}
.dd-products .ds-h{align-self:start}
.dd-products .ds-h{grid-column:1/-1}
/* All four cards the same size, per the founder: `grid-auto-rows:1fr` equalises
   the two rows, and the anchors must fill their cell or a one-line card floats at
   the top of a row sized by the two-line Conductor tagline. */
.dd-products a{white-space:normal;background:var(--bg-2);border-radius:10px;padding:14px 16px;
  display:flex;flex-direction:column;justify-content:flex-start;height:100%;box-sizing:border-box}
.dd-products a:hover{background:var(--dw-tint)}
/* The product pages carry `.nav.clear .dd-menu a,.nav.clear .dd-menu .t{color:var(--ink)}`
   in their own <style> to keep dropdown items dark on a white panel under a clear
   nav. That is (0,3,0) and beats a plain `.dd-products .t` at (0,2,0), which is why
   the product names stayed near-black on every page with a clear nav while the
   homepage looked right. Match its weight instead of reaching for !important. */
.dd-menu.dd-products .t,.nav.clear .dd-menu.dd-products .t{color:var(--dw)}

/* Founder 2026-08-09: no hairline under "Agentic Platform", and the section
   title in both menus sits in equal air. The two panels are not interchangeable
   - Products pads 14px with an 8px row gap, the Agents mega pads 18px with a 6px
   one - so a shared `.ds-h` value cannot land both. Each title's own padding and
   margin absorb its panel's numbers instead, and both read 19px above and below:
     Products  1 border + 14 pad + 4 = 19  |  5 pad + 6 margin + 8 gap = 19
     Agents    1 border + 18 pad + 0 = 19  |  4 pad + 1 transparent border
                                              + 8 margin + 6 gap = 19
   The agents rule is scoped `>` so it moves the mega's own title and leaves the
   column heads inside the cards alone. */
.dd-products .ds-h{border-bottom:0;padding:4px 8px 5px;margin-bottom:6px}
.dd-agents-mega > .ds-mega-title{padding-top:0;margin-bottom:8px}

/* Narrow desktop. The panels are sized `calc(100vw - 370px)`, so at 901px the Read
   More mega is only 531px: four tracks give ~110px each and "Read our thesis ->"
   cannot fit, which pushed 4px past the viewport. Four tracks need roughly 800px of
   panel, i.e. a ~1170px window, so below that drop to two. Same reasoning for the
   agents mega's five tracks. */
@media(max-width:1169px){
  .dd-read-mega{grid-template-columns:1fr 1fr}
  .dd-agents-mega{grid-template-columns:repeat(3,1fr)}
}
@media(max-width:1000px){
  .dd-agents-mega{grid-template-columns:repeat(2,1fr)}
}

@media(max-width:900px){
  /* the megas are hidden behind the drawer below this width; keep the grid
     declarations from fighting the drawer's own layout */
  .dd-products{display:block;width:auto}
}
/* Narrow desktops (901-1150px): tighten only the read-mega column gap so the 4th
   (Plans) column does not squeeze the content columns into ragged wrapping. */
@media(max-width:1150px){.dd-read-mega{gap:6px 14px}}
.nav-cta{margin-left:auto;display:flex;gap:10px;align-items:center}
/* text-only secondary CTA in the nav (replaces the old bordered "Start a pilot" button) */
.nav-cta .pilot-link{font-size:15.5px;font-weight:500;color:var(--ink-2);transition:color .2s}
.nav-cta .pilot-link:hover{color:var(--ink)}

/* ============================ buttons ============================ */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;white-space:nowrap;
  padding:11px 19px;border-radius:var(--r-pill);font-family:var(--sans);font-size:14.5px;font-weight:550;
  letter-spacing:-.01em;border:1px solid var(--line-2);color:var(--ink);background:var(--card);cursor:pointer;
  transition:transform .16s cubic-bezier(.3,.7,.3,1),box-shadow .16s,background .16s,border-color .16s,color .16s}
.btn:hover{transform:translateY(-1px)}
.btn-solid{background:var(--ink);color:#fff;border-color:var(--ink)}
.btn-solid:hover{background:var(--dw-deep);border-color:var(--dw-deep)}
.btn-green{background:var(--dw);border-color:var(--dw);color:var(--on-dw);box-shadow:var(--sh-sm);padding-block:8px}
.btn-green:hover{background:var(--dw-deep);border-color:var(--dw-deep);box-shadow:var(--sh-md)}
.btn-ghost{border-color:var(--line-2);color:var(--ink);background:var(--card)}
.btn-ghost:hover{border-color:var(--ink);box-shadow:var(--sh-sm)}
.btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--dw-ring)}

/* ============================ type ============================ */
.eyebrow{font-family:var(--mono);font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--dw);
  font-weight:600;display:inline-flex;align-items:center;gap:10px;white-space:nowrap}
.eyebrow::before{content:"";width:18px;height:1.5px;background:var(--dw);opacity:.6}
.eyebrow.center{justify-content:center}
.eyebrow.center::after{content:"";width:18px;height:1.5px;background:var(--dw);opacity:.6}
h1{font-family:var(--sans);font-weight:600;font-size:clamp(42px,5.6vw,72px);line-height:1.02;letter-spacing:-.035em;color:var(--ink)}
h1 em,h2 em,h3 em{font-family:var(--serif);font-weight:400;font-style:italic;color:var(--dw);letter-spacing:0}
h2{font-family:var(--sans);font-weight:600;font-size:clamp(30px,3.6vw,46px);line-height:1.08;letter-spacing:-.028em;color:var(--ink)}
h3{font-size:19px;font-weight:600;line-height:1.32;letter-spacing:-.018em}
.sub{font-size:18px;color:var(--ink-2);max-width:600px;line-height:1.55;font-weight:420}
.center{text-align:center}
.center .sub{margin-left:auto;margin-right:auto}
.byline{font-family:var(--serif);font-style:italic;font-size:19px;color:var(--dw-deep);line-height:1.35}

/* ============================ section rhythm ============================ */
.hero{padding-top:var(--s10);padding-bottom:var(--s9);text-align:center}
.hero h1{max-width:14ch;margin:var(--s5) auto var(--s5)}
/* Mobile-only break in the hero headline. MUST stay above the 640px mobile
   layer that flips it to `inline`: the two rules have identical specificity,
   so whichever comes last in this file wins. Desktop keeps the founder's
   two-line composition ("Your data platform," / "run by an agentic
   workforce."); only the phone gets the third line. */
.hero h1 .br-m{display:none}
.hero .sub{margin:0 auto var(--s7);max-width:620px}
.hero-ctas{display:flex;gap:12px;justify-content:center;margin-bottom:var(--s9)}

/* ---- opt-in photographic hero backgrounds (full-bleed, fade to page) ---- */
body{overflow-x:hidden}
.hero[data-hbg]{position:relative}
.hero[data-hbg]::before{content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);
  width:100vw;height:100%;z-index:0;pointer-events:none;background-repeat:no-repeat}
.hero[data-hbg] > *{position:relative;z-index:1}
.hero[data-hbg="tree"]::before{
  background-image:linear-gradient(180deg,rgba(8,13,9,.66) 0%,rgba(8,13,9,.40) 40%,rgba(8,13,9,.50) 95%,var(--bg) 100%),url(assets/bg/tree.png);
  background-size:cover,cover;background-position:center 26%,center 26%}
/* per-product final hero photography.
   Overlay mid-stop is .52 on ALL FOUR (founder: all pages or none). Measured
   against the lime em: at .40 the conductor photo left 8.4% of the background
   behind the em under WCAG 3:1 (min 2.50); at .52 every one of the four sits at
   0.0% under, so no scrim panel is needed on any of them. */
.hero[data-hbg="swarm"]::before{
  background-image:linear-gradient(180deg,rgba(8,13,9,.66) 0%,rgba(8,13,9,.52) 40%,rgba(8,13,9,.50) 95%,var(--bg) 100%),url(assets/bg/hero-swarm.png);
  background-size:cover,cover;background-position:center 32%,center 32%}
/* ---- the agent overlay's backdrop ----
   Founder: "when the agent card is opened I want to add a background. I want to add
   this background which is the above product landing page image thats blurred."
   That is hero-swarm.png, the photograph behind this page's own hero.

   Until now the overlay relied on backdrop-filter, which blurs WHATEVER IS BEHIND
   IT. Open a card from the top of the page and you get the hero photo blurred, which
   is what he screenshotted and liked; open one after scrolling to the benchmark
   section and you get a blurred white band. Same code, different result depending on
   scroll position. This paints the hero explicitly so the overlay looks the same
   wherever it is opened from.

   THIS RULE HAS TO LIVE IN shared.css, NOT in the mockup's inline <style>. Relative
   url() resolves against the STYLESHEET, so assets/bg/... works from /v6/shared.css
   on the devserver and from the mockup directory when the page is opened standalone.
   In an inline style block it would resolve against the document, and 404 on
   /product/. Same trap documented further down this file for the .hero rules.

   No extra page weight: the hero on this page already loads this image, so the
   overlay hits the cache.

   scale(1.06) hides the soft edge a 22px blur leaves at the viewport boundary. The
   flat gradient is blurred too, which costs nothing and keeps the tint above the
   photograph rather than under it. */
.agent-modal::before{content:"";position:fixed;inset:0;z-index:0;
  background:linear-gradient(rgba(9,19,14,.58),rgba(9,19,14,.58)),url(assets/bg/hero-swarm.png) center 32%/cover no-repeat;
  filter:blur(22px) saturate(.94);transform:scale(1.06)}
.agent-modal > *{position:relative;z-index:1}
@media(prefers-reduced-motion:reduce){.agent-modal::before{transform:none}}

.hero[data-hbg="conductor"]::before{
  background-image:linear-gradient(180deg,rgba(8,13,9,.66) 0%,rgba(8,13,9,.52) 40%,rgba(8,13,9,.50) 95%,var(--bg) 100%),url(assets/bg/hero-conductor.png);
  background-size:cover,cover;background-position:center 34%,center 34%}
.hero[data-hbg="context"]::before{
  background-image:linear-gradient(180deg,rgba(8,13,9,.66) 0%,rgba(8,13,9,.52) 40%,rgba(8,13,9,.50) 95%,var(--bg) 100%),url(assets/bg/hero-context.png);
  background-size:cover,cover;background-position:center 30%,center 30%}
.hero[data-hbg="console"]::before{
  background-image:linear-gradient(180deg,rgba(8,13,9,.66) 0%,rgba(8,13,9,.52) 40%,rgba(8,13,9,.50) 95%,var(--bg) 100%),url(assets/bg/hero-console.png);
  background-size:cover,cover;background-position:center 30%,center 30%}
/* dark fallback backdrop so white hero text stays legible even before the
   photographic hero image is dropped in (the loaded photo covers this) */
.hero.dark-hero{background:linear-gradient(165deg,var(--dw-deep) 0%,var(--dark) 78%)}
/* light-text treatment over a photographic hero */
.hero.dark-hero h1{color:#fff;text-shadow:0 2px 28px rgba(0,0,0,.5)}
/* The <em> line was the ONE piece of hero text with no shadow, in the lightest mint —
   which is why the product-page subheads read as harder over busy artwork (measured:
   conductor 20% bright pixels behind it > swarm 17% > console 13% > context 6%).
   Deepen it a step and give it the same shadow the rest of the hero text has. */
.hero.dark-hero h1 em{color:oklch(0.74 0.13 162);text-shadow:0 2px 22px rgba(0,0,0,.6)}
.hero.dark-hero .sub{color:#fff !important;text-shadow:0 1px 20px rgba(0,0,0,.55)}
.hero.dark-hero .eyebrow{color:#fff}
.hero.dark-hero .eyebrow::before,.hero.dark-hero .eyebrow::after{background:rgba(255,255,255,.8)}
.hero.dark-hero .stat .n{color:#fff}
.hero.dark-hero .stat .l{color:rgba(255,255,255,.78)}
/* Clear boxed stats on the dark hero: translucent white outline, no fill, so
   the green (and the tree photo) reads straight through each box. */
.hero.dark-hero #swarm-stats .stat{border-color:rgba(255,255,255,.28)!important;background:transparent!important}
.hero.dark-hero .btn-ghost{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.55);color:#fff}
.hero.dark-hero .btn-ghost:hover{border-color:#fff;background:rgba(255,255,255,.2)}

.sec{padding-top:var(--s10);padding-bottom:var(--s10)}
/* Tweak: remove side padding on the platform + CTA sections */
body.pf-flush #platform{padding-left:0;padding-right:0}
body.pf-flush .wrap:has(#homeCta){padding-left:0;padding-right:0}
/* Tweak: flush ALL boxed sections — consistent zero side padding (Customers stays padded) */
body.pf-flush-all #platform,
body.pf-flush-all .bic.wrap,
body.pf-flush-all .btn-sec,
body.pf-flush-all .wrap:has(#homeCta){padding-left:0;padding-right:0}
/* Tweak: flush boxed sections on product pages (meet2/bench2 cards + CTA) */
body.pf-flush-all .meet2,body.pf-flush-all .bench2{margin-left:calc(var(--s8)*-1);margin-right:calc(var(--s8)*-1)}
body.pf-flush-all .wrap:has(.cta){padding-left:0;padding-right:0}
@media(max-width:640px){body.pf-flush-all .meet2,body.pf-flush-all .bench2{margin-left:calc(var(--s6)*-1);margin-right:calc(var(--s6)*-1)}}
.sec-head{margin-bottom:var(--s8);display:flex;flex-direction:column;gap:var(--s4)}
.num{font-family:var(--mono);font-size:13px;color:var(--ink-3)}

/* ============================ stat strip ============================ */
.stats{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);border-radius:var(--r);
  overflow:hidden;background:var(--card);box-shadow:var(--sh-sm)}
.stat{padding:var(--s7) var(--s5);text-align:center;border-right:1px solid var(--line)}
.stat:last-child{border-right:0}
.stat .n{font-family:var(--sans);font-weight:600;font-size:44px;line-height:1;color:var(--dw-deep);letter-spacing:-.03em;
  font-variant-numeric:tabular-nums}
.stat .l{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);margin-top:10px}

/* ============================ cards ============================ */
.cards3{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s5)}
.cards2{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s5)}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:var(--s7);
  box-shadow:var(--sh-sm);transition:transform .2s cubic-bezier(.3,.7,.3,1),box-shadow .2s,border-color .2s}
a.card:hover{border-color:var(--line-2);box-shadow:var(--sh-lg);transform:translateY(-3px)}
.card .k{font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--dw)}
.card h3{margin:12px 0 7px;letter-spacing:-.02em}
.card p{font-size:15px;color:var(--ink-2);line-height:1.55}

/* ============================ product-mock browser frame ============================ */
.frame{background:var(--card);border:1px solid var(--line);border-radius:var(--r);overflow:hidden;box-shadow:var(--sh-xl)}
.frame-bar{height:40px;background:var(--bg-2);border-bottom:1px solid var(--line);display:flex;align-items:center;gap:7px;padding:0 15px}
.frame-bar i{width:9px;height:9px;border-radius:50%;background:var(--line-2);display:block}
.frame-bar .url{font-family:var(--mono);font-size:11px;color:var(--ink-3);background:var(--card);border:1px solid var(--line);
  border-radius:7px;padding:4px 12px;margin-left:10px;letter-spacing:-.01em}
.frame-body{padding:22px;display:flex;gap:18px;text-align:left}
.mock-side{width:148px;flex:none;display:flex;flex-direction:column;gap:8px}
.mock-side .it{font-family:var(--mono);font-size:10.5px;letter-spacing:.04em;color:var(--ink-3);padding:7px 10px;border-radius:8px}
.mock-side .it.on{background:var(--dw-tint);color:var(--dw-deep);font-weight:700}
.mock-main{flex:1;display:flex;flex-direction:column;gap:12px;min-width:0}
.bar{height:9px;border-radius:5px;background:var(--bg-2)}
.bar.g{background:var(--dw-tint)}
.chip{display:inline-flex;align-items:center;font-family:var(--mono);font-size:10.5px;letter-spacing:.04em;padding:5px 11px;
  border-radius:var(--r-pill);border:1px solid var(--line);color:var(--ink-2);background:var(--card)}
.chip.g{background:var(--dw-tint);border-color:transparent;color:var(--dw-deep);font-weight:700}
.chip.a{background:#FBF1DF;border-color:transparent;color:#8A5A12;font-weight:700}
.panel{border:1px solid var(--line);border-radius:var(--r-sm);padding:14px;background:var(--card)}
.panel .ttl{font-size:13px;font-weight:650;margin-bottom:8px;letter-spacing:-.01em}
.mono-line{font-family:var(--mono);font-size:11.5px;color:var(--ink-2);line-height:1.7;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mono-line b{color:var(--dw);font-weight:600}

/* ============================ feature rows ============================ */
.feat{display:grid;grid-template-columns:5fr 7fr;gap:var(--s10);align-items:center;padding:var(--s10) 0;border-top:1px solid var(--line)}
.feat:first-of-type{border-top:0}
.feat.flip{grid-template-columns:7fr 5fr}
.feat.flip .feat-copy{order:2}
.feat.flip .feat-mock{order:1}
.feat-copy h3{font-family:var(--sans);font-weight:600;font-size:clamp(24px,2.6vw,32px);line-height:1.12;
  letter-spacing:-.028em;margin:14px 0 12px}
.feat-copy p{color:var(--ink-2);font-size:16px;max-width:440px;line-height:1.6}
.feat-copy .pts{margin-top:var(--s6);display:flex;flex-direction:column;gap:10px}
.pt{display:flex;gap:10px;font-size:14.5px;color:var(--ink-2);line-height:1.45}
.pt::before{content:"";flex:none;width:18px;height:18px;margin-top:1px;border-radius:5px;background:var(--dw-tint);
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23157a4a' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>");
  background-size:13px;background-repeat:no-repeat;background-position:center}

/* ============================ agent grid ============================ */
.agents{display:grid;grid-template-columns:repeat(6,1fr);gap:10px}
.agent{background:var(--card);border:1px solid var(--line);border-radius:var(--r-sm);padding:13px 12px;cursor:pointer;
  position:relative;transition:transform .18s,box-shadow .18s,border-color .18s}
.agent:hover{border-color:var(--line-2);transform:translateY(-3px);box-shadow:var(--sh-md);z-index:2}
.agent .dot{width:6px;height:6px;border-radius:50%;background:var(--dw-bright);margin-bottom:8px}
.agent .nm{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.01em}
.agent .ds{font-size:11px;color:var(--ink-3);line-height:1.35;margin-top:3px}

/* ============================ diff table ============================ */
.dtable{width:100%;border-collapse:separate;border-spacing:0;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden;font-size:14px;box-shadow:var(--sh-sm)}
.dtable th{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;text-align:left;
  padding:15px 18px;background:var(--bg-2);color:var(--ink-3);border-bottom:1px solid var(--line);font-weight:700}
.dtable th.us{background:var(--dw-deep);color:#fff}
.dtable td{padding:16px 18px;border-bottom:1px solid var(--line);vertical-align:top;color:var(--ink-2);line-height:1.5}
.dtable tr:last-child td{border-bottom:0}
.dtable td.us{background:var(--dw-tint-2);color:var(--ink)}
.dtable td b{color:var(--ink)}

/* ============================ CTA + footer ============================ */
.cta{background:linear-gradient(to top,rgba(7,11,8,.92) 0%,rgba(7,11,8,.60) 38%,rgba(7,11,8,.16) 70%,rgba(7,11,8,0) 100%),
  url(assets/bg/cta-tree.webp) center bottom/cover no-repeat,var(--dark);color:#fff;border-radius:var(--r-xl); /* webp: 326KB vs the 2.9MB png that was starving the hero image on cold loads */
  padding:clamp(36px,5vw,72px);text-align:left;margin:var(--s9) 0;min-height:clamp(400px,44vw,540px);
  display:flex;flex-direction:column;justify-content:flex-end;box-shadow:var(--sh-xl)}
.cta h2{color:#fff;margin:0;max-width:18ch;font-size:clamp(34px,4.6vw,60px);line-height:1.04;letter-spacing:-.03em}
.cta p{color:#C2CDC7;margin:18px 0 28px;max-width:560px;font-size:17px;line-height:1.55}
.cta-actions{display:flex;gap:12px;flex-wrap:wrap}
footer{background:var(--dark);color:#fff;margin-top:var(--s11)}
/* Founder 2026-08-30, on a phone: "after the bottom CTA on the mobile version
   there's way too much white space between it and the footer." --s11 is 128px,
   set for a desktop page where that gap is one section break among many. On a
   390px screen it is a third of the viewport of nothing, and it reads as the
   page having ended early. Measured 112px of visible gap between the card and
   the footer; 48 keeps the separation without the dead screen. Desktop is
   untouched. */
@media(max-width:640px){footer{margin-top:48px}}
.foot-grid{max-width:1600px;margin:0 auto;padding:var(--s9) var(--s6);display:grid;
  grid-template-columns:.5fr 1fr 1fr minmax(0,max-content) 1fr 1fr;gap:24px;font-size:13px}
.foot-grid .col-agents .agents-cols{display:flex;gap:var(--agents-gap,28px)}
.foot-grid .col-agents .agents-c{flex:0 1 auto;min-width:0;display:flex;flex-direction:column}
/* Tight link rhythm, all widths (founder 2026-07-24: the phone fix landed but
   the gaps persisted on desktop). The agent list carried a 5px vertical margin
   on top of its line-height; the other columns carried 3px of padding. Both are
   now zero and the spacing comes from line-height alone. */
.foot-grid .col-agents .agents-c a{position:relative;padding:0 0 0 16px;line-height:1.5;font-size:var(--foot-fs,13.5px);margin:0}
.foot-grid.no-bullets .col-agents .agents-c a::before{display:none}
.foot-grid.no-bullets .col-agents .agents-c a{padding-left:0}
.foot-grid .col-agents .agents-c a::before{content:"";position:absolute;left:0;top:7px;width:5px;height:5px;border-radius:50%;background:var(--dw-bright)}
.foot-grid .col .h{font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:#76847C;margin-bottom:12px}
.foot-grid .col a{display:block;color:#C3CDC7;padding:0;line-height:1.5;font-size:var(--foot-fs,13.5px);transition:color .14s}
.foot-grid .col a:hover{color:#fff}
/* footer links not yet live */
footer a.soon{cursor:default;opacity:.55}
footer a.soon::after{content:" soon";font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;opacity:.85}
.foot-head{max-width:1600px;margin:0 auto;padding:var(--s7) var(--s6) var(--s9)}
.foot-head p{font-family:var(--sans);font-weight:500;font-size:clamp(42px,5.85vw,98px);line-height:1.03;letter-spacing:-.035em;color:#fff;margin:0;max-width:none;text-wrap:balance}
.foot-head p b{color:var(--dw-bright);font-weight:500}
.foot-base{max-width:1600px;margin:0 auto;padding:0 var(--s6) var(--s8);display:flex;justify-content:flex-end;gap:20px;
  color:#76847C;font-size:8.5px;font-family:var(--mono);padding-top:var(--s6)}
.foot-base a{color:#76847C;text-decoration:underline;text-underline-offset:2px;transition:color .14s}
.foot-base a:hover{color:#fff}

/* ============================ reveal ============================ */
.rv{opacity:0;transform:translateY(16px);transition:opacity .6s ease,transform .6s cubic-bezier(.3,.7,.3,1)}
.rv.in{opacity:1;transform:none}
/* Staggered reveal — heading slides up, body + actions follow (matches live-site CTA motion) */
.rv-up{transform:translateY(38px);transition:opacity .95s ease,transform .95s cubic-bezier(.22,1,.36,1)}
/* Homepage waterfall ladder (.rv-w1..): when a whole section's .rv items are revealed
   TOGETHER (by the section-pager on arrival, or the .wfall fallback observer), these
   1s-step delays play them one-per-second in DOM order. The load-order opacity flash
   that would otherwise desync this is neutralised by an inline .rv{opacity:0} guard
   injected at first paint (extractV6Body). Deck panels need --rvd (their base
   transition clobbers transition-delay) - see index.html. */
/* Ladder steps were 1s apart, so the sixth item landed SIX seconds after the
   group armed - long after a normal scroll has carried you past it, which read
   as items appearing out of nowhere and out of order (founder 2026-07-24).
   Now ~0.13s apart: the cascade is still legibly one-after-another in DOM
   order, but the whole group has resolved inside a second. */
.rv-w1{transition-delay:.13s}
.rv-w2{transition-delay:.26s}
.rv-w3{transition-delay:.39s}
.rv-w4{transition-delay:.52s}
.rv-w5{transition-delay:.65s}
.rv-w6{transition-delay:.78s}
.rv-d1{transition-delay:.14s}
.rv-d2{transition-delay:.3s}
.rv-d3{transition-delay:.45s}
.rv-d4{transition-delay:.6s}
.rv-d5{transition-delay:.75s}
.rv-d6{transition-delay:.9s}
@media(prefers-reduced-motion:reduce){.rv,.rv-up{opacity:1;transform:none;transition:none}}

/* ==================== hero entry (.hv) ====================
   Port of the live (main-branch) site's hero mount cascade - the "clean float into
   screen" the founder asked to keep (2026-08-08). Measured off dataworkers.io in
   headless Chrome at 390x844: the h1 goes opacity 0->1 and translateY(60px)->0 over
   1.2s on cubic-bezier(.22,1,.36,1), with the eyebrow/sub/CTA on a delay ladder
   around it. Source of truth was src/app/components/Hero.tsx on `main` (framer-motion
   `initial`/`animate`, imported from "motion/react" - grepping for "framer-motion"
   finds nothing). It is NOT mobile-only there and is not gated here either; mobile is
   just where a single column makes the ladder legible.

   Fires ON MOUNT, not on scroll: the hero is above the fold, so an IntersectionObserver
   would fire instantly anyway and only adds a race with .rv. Armed by adding .hero-in to
   the .hero element (mockups: inline <script>; React: useV6Page).

   Delays are compressed vs the live site's 2.2s ladder - founder 2026-07-24 on the .rv-w*
   waterfall: reveals landing seconds late read as "out of nowhere and out of order".
   Last item here lands at ~1.4s.

   FOSC: the hidden state MUST exist at first paint. Static mockups get that from this
   render-blocking <link>. The React port attaches shared.css AFTER first paint, so .hv
   is duplicated into the inline revealGuard <style> that extractV6Body prepends to the
   injected body - if you add a .hv* rule that affects the resting state, mirror it there.

   SPECIFICITY, and why the ladder is written .hv.hv2 rather than .hv2: the React port's
   guard is an inline <style> INSIDE the injected body, i.e. later in document order than
   this file's <link>. A bare .hv2 (0,1,0) ties the guard's .hv and loses the tie on order,
   so every step silently collapsed to the guard's resting value - measured: the h1 rose
   24px instead of 60px. Doubling the class to .hv.hv2 (0,2,0) makes the ladder win on
   specificity, independent of load order. Keep the resting states here property-for-
   property identical to the guard, and never let the guard declare anything the ladder
   needs to override with equal specificity. (Related: transform deliberately does NOT use
   var() - a var() inside transform resolved to its fallback here even once the custom
   property was set, which is what sent us chasing this in the first place.)

   THE TRANSITION IS DECLARED ONLY IN THE ARMED STATE, never at rest. This is the
   subtle one and it cost a debugging round. In the React port the resting offset
   necessarily CHANGES once shared.css lands: the guard says 24px, .hv.hv2 says 60px. If
   .hv carried the transition at rest, that 24px->60px correction is itself a transitioned
   change - so the h1 was still ramping DOWNWARD-from-24 when .hero-in flipped it, and it
   never travelled more than 24px no matter what the ladder said (measured: peak 24px
   against an intended 60px). With the transition scoped to .hero-in, resting corrections
   apply instantly and invisibly at opacity 0, and the only animated change is the armed
   one. Transitions are driven by the AFTER-change style, so declaring them here is what
   makes the entry animate at all. */
.hv{opacity:0;transform:translateY(24px)}
.hv.hv1{transform:translateY(20px)} /* .eyebrow */
.hv.hv2{transform:translateY(60px)} /* h1 - the signature 60px rise */
.hv.hv3{transform:translateY(34px)} /* p.sub */
.hv.hv4{transform:translateY(24px)} /* .hero-ctas */
.hv.hv5{transform:translateY(20px)} /* .stats / .dwconsole */
/* 60px is calibrated to a single-column mobile hero. On wide screens the h1 is far
   larger and that drop reads heavy - ease it back without branching the JS. 861px is
   already this file's hero breakpoint (see .hero padding override below). */
@media(min-width:861px){.hv.hv2{transform:translateY(44px)}}
/* Armed state (0,3,0) + its per-step timings (0,4,0). */
.hero.hero-in .hv{opacity:1;transform:none;
    transition:opacity .8s cubic-bezier(.22,1,.36,1),
               transform .8s cubic-bezier(.22,1,.36,1)}
.hero.hero-in .hv.hv1{transition-duration:.75s;transition-delay:.05s}
.hero.hero-in .hv.hv2{transition-duration:1.2s;transition-delay:.12s}
.hero.hero-in .hv.hv3{transition-duration:.95s;transition-delay:.3s}
.hero.hero-in .hv.hv4{transition-duration:.8s;transition-delay:.46s}
.hero.hero-in .hv.hv5{transition-duration:.8s;transition-delay:.6s}
/* Enumerated: a bare .hv here (0,1,0) would lose to the .hv.hvN resting ladder (0,2,0). */
@media(prefers-reduced-motion:reduce){
  .hv,.hv.hv1,.hv.hv2,.hv.hv3,.hv.hv4,.hv.hv5{opacity:1;transform:none}
  .hero.hero-in .hv{transition:none}
}

/* ============================ responsive ============================ */
@media(max-width:900px){
  .cards3,.cards2,.stats{grid-template-columns:1fr 1fr}
  .agents{grid-template-columns:repeat(3,1fr)}
  .feat,.feat.flip{grid-template-columns:1fr;gap:var(--s7);padding:var(--s9) 0}
  .feat.flip .feat-copy{order:1}.feat.flip .feat-mock{order:2}
  .nav-links{display:none}
  .foot-grid{grid-template-columns:1fr 1fr}
  .stat{padding:var(--s6) var(--s4)}
}
@media(max-width:560px){
  .stats{grid-template-columns:1fr 1fr}
  .hero{padding-top:var(--s9)}
}

/* ============================ mobile layer ============================ */
/* hamburger + slide-down menu — button/panel injected by mobile-nav.js on every page */
.nav-burger{display:none;width:44px;height:44px;flex:none;align-items:center;justify-content:center;background:none;border:0;cursor:pointer;color:var(--ink);border-radius:10px;padding:0;margin-left:2px}
.nav.clear .nav-burger{color:#fff}

/* ---------------------------------------------------------------------------
   NAV CTA over a transparent header - data-tw-navcta. Founder 2026-08-30:
   "I want a tweak where the Book a Demo button on the header is using the
   clear one until you scroll, and when the bar stops being transparent I want
   it to go to the green colour we use generally everywhere. So it's only a
   clear Book a Demo button when you're on the landing page."

   `.nav.clear` IS the transparent state - v6Runtime removes that class once the
   hero has passed the header - so the whole behaviour keys off it and needs no
   new JS. When the class goes, these rules stop matching and the button falls
   back to the standard green with no transition to write.

   It follows that this is inherently landing-page-only: `.clear` is only ever
   set on pages with a dark hero. Nothing else needed scoping.
   ------------------------------------------------------------------------ */
[data-tw-navcta="clear"] .nav.clear .nav-cta .btn-green{
  background:transparent;border:1px solid rgba(255,255,255,.62);color:#fff;
  transition:background .18s ease,border-color .18s ease,color .18s ease}
[data-tw-navcta="clear"] .nav.clear .nav-cta .btn-green:hover{
  background:rgba(255,255,255,.14);border-color:#fff}
.mnav{display:none;position:fixed;left:0;right:0;top:66px;bottom:0;z-index:49;background:var(--bg);overflow-y:auto;-webkit-overflow-scrolling:touch;padding:14px 24px 44px}
.mnav .h{font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);margin:24px 0 4px}
.mnav a{display:block;padding:12px 0;font-size:17px;font-weight:550;color:var(--ink);border-bottom:1px solid var(--line);letter-spacing:-.01em}
.mnav a .s{display:block;font-size:12.5px;color:var(--ink-3);font-weight:400;margin-top:2px;letter-spacing:0}
.mnav .mnav-ctas{display:flex;flex-direction:column;gap:10px;margin-top:28px}
.mnav .mnav-ctas a.btn{display:inline-flex;width:100%;padding:14px 19px;border-bottom:1px solid transparent;font-size:15px;font-weight:550;border-radius:var(--r-pill)}
.mnav .mnav-ctas a.btn-green{color:var(--on-dw);border-color:var(--dw)}
.mnav .mnav-ctas a.btn-ghost{color:var(--ink);border:1px solid var(--line-2)}
body.mnav-lock{overflow:hidden}

/* ---- mobile drawer dividers (added 2026-08-16) ----
   A rule under every single item made the drawer read as a dense list, and the
   spacing was uneven: inside a group the border sat midway in a ~25px gap,
   but a group's LAST item had its border orphaned 24px above the next header,
   while the header sat only 4px off the item below it. `data-tw-menu` switches
   the options; see src/app/components/tweaks/tweakConfig.ts.
     groups (default) - rule only where one section ends and the next begins
     none             - no rules at all, spacing does the separating
     all              - a rule under every item (what shipped at launch)      */
.mnav a{border-bottom:0}
[data-tw-menu="all"] .mnav a{border-bottom:1px solid var(--line)}
/* A group ends at the item immediately before the next header (or before the
   CTA block), which is exactly what :has(+ .h) / :has(+ .mnav-ctas) select. */
[data-tw-menu="groups"] .mnav a:has(+ .h),
[data-tw-menu="groups"] .mnav a:has(+ .mnav-ctas){border-bottom:1px solid var(--line)}
/* Even out the rhythm: the header now sits an equal distance from the item
   above it and the item below it, instead of 24px / 4px. */
.mnav .h{margin:22px 0 10px}
.mnav .h:first-child{margin-top:6px}
[data-tw-menu="none"] .mnav a{padding:13px 0}

body.mnav-lock .mnav{display:block}
.nav.mnav-open{background:rgba(255,255,255,.98)!important;backdrop-filter:none!important;-webkit-backdrop-filter:none!important;box-shadow:0 1px 0 var(--line)!important;border-bottom-color:var(--line)!important}
.nav.mnav-open .logo span,.nav.mnav-open .logo b,.nav.mnav-open.clear .logo span,.nav.mnav-open.clear .logo b{color:var(--ink)!important}
.nav.mnav-open .nav-burger,.nav.mnav-open.clear .nav-burger{color:var(--ink)!important}
@media(max-width:900px){
  .nav-burger{display:inline-flex}
  .nav-cta .pilot-link{display:none}
  .nav-cta{gap:6px}
  .hero-ctas{flex-wrap:wrap}
  /* over a dark hero the green CTA hides on MOBILE only (burger carries it);
     desktop keeps Book a Demo visible - do not lift these out of this query */
  .nav.clear .nav-cta .btn-green{display:none}
  .nav.mnav-open.clear .nav-cta .btn-green{display:inline-flex}
}
@media(max-width:700px){
  .foot-grid{grid-template-columns:1fr 1fr;gap:28px 22px}
  /* two columns on phones rather than one long stack. The logo and the Agents
     column span the full width - Agents carries the longest labels and would
     wrap badly in a half-width cell. */
  .foot-grid > .col:first-child{grid-column:1/-1}
  .foot-grid .col-agents{grid-column:1/-1}
  /* Mobile-only running order (founder 2026-07-23): Agents first, then
     Product | Company, then Resources | Connect. Grid `order` does it without
     touching the markup, so desktop and the React twin keep their DOM order.
     Source order is: 1 logo, 2 Product, 3 Company, 4 Agents, 5 Resources, 6 Connect. */
  .foot-grid > .col:nth-child(1){order:0}
  .foot-grid > .col-agents{order:1}
  .foot-grid > .col:nth-child(2){order:2}
  .foot-grid > .col:nth-child(3){order:3}
  .foot-grid > .col:nth-child(5){order:4}
  .foot-grid > .col:nth-child(6){order:5}
  /* 20 agents in one column is a very long scroll on a phone, so the list runs in
     TWO columns (multicol fills down-then-across, which keeps the reading order).
     display:contents lets the three desktop sub-columns' links flow as one list. */
  .foot-grid .col-agents .agents-cols{display:block;columns:2;column-gap:18px}
  .foot-grid .col-agents .agents-c{display:contents}
  /* match the other footer columns exactly - a larger size here made the Agents
     block read as a bigger section than Product / Company / Resources / Connect */
  .foot-grid .col-agents .agents-c a{break-inside:avoid;font-size:var(--foot-fs,13.5px);margin:0}
  /* Footer link spacing is deliberately TIGHT (founder, 2026-07-23: "way too
     much whitespace between links, there shouldn't be any"; reiterated
     2026-07-24 for desktop). An earlier pass padded these to a 44px tap
     target; that read as loose and was reverted. Density wins here, the links
     are still individually tappable. The rule now lives in the base styles at
     every width, so there is nothing phone-specific left to set. */
  /* 8.5px mono is unreadable on a phone; the legal line still stays secondary. */
  .foot-base{font-size:11px;gap:14px;flex-wrap:wrap;justify-content:flex-start}
}
@media(max-width:640px){
  .sec{padding-top:var(--s9);padding-bottom:var(--s9)}
  h1{font-size:clamp(34px,9.6vw,42px)}
  .sub{font-size:16.5px}
  /* The h1's line break is deliberate composition ("Your data platform," / "run by
     an agentic workforce."), so it is honoured at every width - founder, 2026-07-23.
     Only the sub's break collapses on narrow screens, where its two halves would
     otherwise wrap into very short lines. */
  .hero .sub br{display:none}
  /* Mobile-only break in the h1 (founder 2026-08-16): without it the phrase
     splits as "run by an agentic" / "workforce.", breaking the green italic
     across two lines. This puts "agentic workforce." whole on the third line. */
  .hero h1 .br-m{display:inline}
  /* Sub-copy options - data-tw-herosub, see src/app/components/tweaks/.
     `short` drops the context-graph clause; the sentence still reads. */
  [data-tw-herosub="short"] .hero .sub .sub-tail{display:none}
  [data-tw-herosub="tight"] .hero .sub{font-size:15.5px;line-height:1.45;max-width:34ch}
  [data-tw-herosub="scrim"] .hero .sub{font-size:16px;line-height:1.5;padding:14px 16px;
    border-radius:14px;background:rgba(7,11,8,.34);text-shadow:none;
    backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px)}
  .hero-meta{flex-wrap:wrap;justify-content:center}
  .hero-ctas{margin-bottom:var(--s8)}
  .hero-ctas .btn{flex:1 1 auto;max-width:100%}
  .stat{padding:var(--s5) var(--s3)}
  .stat .n{font-size:32px}
  .cards3,.cards2{grid-template-columns:1fr}
  .agents{grid-template-columns:1fr 1fr}
  .agent{padding:12px 11px}
  .frame-body{flex-direction:column;padding:16px}
  .mock-side{width:auto;flex-direction:row;flex-wrap:wrap}
  .feat{padding:var(--s8) 0;gap:var(--s6)}
  .dtable{display:block;overflow-x:auto}
  .cta{padding:28px 22px 30px;min-height:400px;border-radius:var(--r-lg)}
  .cta p{font-size:15.5px;margin:14px 0 22px}
  .cta-actions .btn{flex:1 1 auto}
  .foot-grid{padding:var(--s8) var(--s6)}
  .foot-head{padding:var(--s6) var(--s6) var(--s8)}
  .foot-head p{font-size:clamp(30px,8.6vw,42px)}
  .foot-base{flex-direction:column;align-items:flex-start;gap:6px;justify-content:flex-start;padding-bottom:var(--s7)}
}

/* ============================ Lenis smooth scroll ============================ */
/* https://github.com/darkroomengineering/lenis — recommended base styles.
   Lenis disables native smooth-scroll while active; degrades gracefully without JS. */
html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto !important}
.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain}
.lenis.lenis-stopped{overflow:clip}
.lenis.lenis-smooth iframe{pointer-events:none}

/* ---- hero cursor FX canvas (binary rain; engine in hero-fx.js / useHeroBinaryRain) ---- */
/* binary-rain cursor field (behind hero text, never blocks clicks) */
/* full-bleed like .hero::before - .hero itself is only `wrap` wide, so an inset:0
   canvas left the left/right margins of the viewport dead to the cursor */
#heroFxGlyphs{position:absolute;top:0;left:50%;transform:translateX(-50%);width:100vw;height:100%;
  z-index:0;pointer-events:none;opacity:.92}
.hero.fx-off #heroFxGlyphs{display:none}
@media (prefers-reduced-motion:reduce){#heroFxGlyphs{display:none}}

/* ---- v6 hero treatment (founder-approved 2026-07-23) ----------------------
   Left-aligned, unshadowed, lime em, no in-hero CTA (the nav CTA carries it).
   The homepage bakes this in; the product pages opt in via these classes. */
.hero.text-left{text-align:left}
.hero.text-left .eyebrow{justify-content:flex-start}
.hero.text-left .eyebrow.center::after{display:none}
.hero.text-left h1{margin-left:0!important;margin-right:auto!important;text-align:left!important}
.hero.text-left .sub{margin-left:0!important;margin-right:auto!important;text-align:left!important}
.hero.text-left .hero-ctas{justify-content:flex-start!important}
.hero.text-left .dwconsole{margin-left:0!important;margin-right:auto!important}
/* the swarm hero's stats row holds centre on its own (margin:0 auto +
   .stat{text-align:center}); bring it left too, and drop the first cell's left
   padding so the 22 lines up with the headline edge */
.hero.text-left .stats{margin-left:0!important;margin-right:auto!important}
.hero.text-left .stat{text-align:left}
.hero.text-left .stats .stat:first-child{padding-left:0}
.hero.em-lime h1 em{color:#CAFF02!important}
.hero.no-shadow.dark-hero h1,.hero.no-shadow.dark-hero .sub,
.hero.no-shadow.dark-hero h1 em,.hero.no-shadow.dark-hero .hero-meta{text-shadow:none}
.hero.cta-off .hero-ctas{display:none}

/* ---------------------------------------------------------------------------
   HERO CTA BUTTON - data-tw-herocta. Founder 2026-08-30, two asks that land on
   the same control:
     "I want to add a Book a Demo button below the mobile hero sub copy... show
      me a clear one, and also one that matches our regular Book a Demo button."
     "I also want to see a lime option for the Book a Demo button on the landing
      page."

   The landing hero carries `cta-off` (rule above), so today there is no button
   under the sub-copy at all - the nav CTA carries it. Each option below turns
   the row back on and dresses the button; `off` is what ships today.

   Lime is #CAFF02, the same token the hero's own <em> already uses (line 773),
   so this is the page's existing accent rather than a new colour. Dark ink on
   it because lime cannot carry white type.
   ------------------------------------------------------------------------ */
[data-tw-herocta="green"] .hero.cta-off .hero-ctas,
[data-tw-herocta="ghost"] .hero.cta-off .hero-ctas,
[data-tw-herocta="lime"]  .hero.cta-off .hero-ctas{display:flex}

/* `ghost` - the clear one. Outline and white type, so it sits on the photograph
   without competing with the headline. */
[data-tw-herocta="ghost"] .hero .hero-ctas .btn{
  background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.55);color:#fff}
[data-tw-herocta="ghost"] .hero .hero-ctas .btn:hover{
  background:rgba(255,255,255,.18);border-color:#fff}

/* `lime` */
[data-tw-herocta="lime"] .hero .hero-ctas .btn{
  background:#CAFF02;border:1px solid #CAFF02;color:#11201A;font-weight:600}
[data-tw-herocta="lime"] .hero .hero-ctas .btn:hover{
  background:#d7ff3a;border-color:#d7ff3a}

/* On a phone the button goes full width under the sub-copy rather than sitting
   as a pill beside nothing. */
@media(max-width:640px){
  [data-tw-herocta="green"] .hero .hero-ctas,
  [data-tw-herocta="ghost"] .hero .hero-ctas,
  [data-tw-herocta="lime"]  .hero .hero-ctas{margin-top:20px}
  /* Founder 2026-08-30: "in the mobile version I don't like that the Book a
     Demo button is wide and not normal sized." So it keeps its natural pill
     width rather than stretching to the column. .hero-ctas .btn has
     flex:1 1 auto at this breakpoint (line ~720), which is what was stretching
     it, so that has to be overridden too. */
  [data-tw-herocta="green"] .hero .hero-ctas .btn,
  [data-tw-herocta="ghost"] .hero .hero-ctas .btn,
  [data-tw-herocta="lime"]  .hero .hero-ctas .btn{
    width:auto;flex:0 0 auto;justify-content:center}
}

/* Raise the hero text into the sky's own deep-blue band (founder-approved
   2026-07-23). The sky art runs dark at the top and pale at the bottom, so this
   buys contrast without touching the image - a wash was tried and REJECTED
   (darkening a sunny-day background fights the concept). Measured at 1440:
   sub 83.6% -> 0.5% of its background under WCAG 3:1, em 46.6% -> 5.5%. */
.hero.raised{justify-content:flex-start!important;padding-top:19vh!important}

/* ---- feature sections on phones: keep the POINTS, drop the picture ---------
   The chips are annotations placed over the artwork by an inline left:% - fine on
   a wide stage, unreadable at 390 where they sit on top of the illustration they
   annotate. Founder's call: below 700px the IMAGE goes and the chips become a
   plain readable list of the section's key points. (Earlier attempts, both
   rejected: centring the chips over the image, and hiding the chips instead of
   the image - the points are the content, the illustration is the decoration.) */
@media(max-width:700px){
  .imgstage img{display:none!important}
  .imgstage{display:block!important;box-shadow:none;background:transparent}
  .imgstage .ov-chip{
    position:static!important;left:auto!important;top:auto!important;
    transform:none!important;opacity:1!important;
    display:flex;align-items:flex-start;gap:10px;
    /* width:auto kills the inline width:XXcqw that 2-line chips carry - in list
       mode it made single-line points wrap (founder bug report 2026-08-13).
       background/color important for the same reason: two chips carry an inline
       green fill that must not survive into the list. */
    width:auto!important;white-space:normal;text-align:left;max-width:100%;
    font-size:14px;font-weight:600;line-height:1.4;color:var(--ink)!important;
    background:transparent!important;border-radius:0;box-shadow:none;
    padding:9px 0;margin:0;border-bottom:1px solid var(--line)}
  .imgstage .ov-chip:last-child{border-bottom:none;padding-bottom:0}
  /* `will-change:opacity,transform` promotes each chip to its own layer, which on a
     2x screen softens the text - the pills looked blurry. Nothing animates them here,
     so drop the promotion. */
  .imgstage .ov-chip{will-change:auto!important;backface-visibility:visible}
  /* with the illustration gone these sections are short, so the section padding and
     the row gap read as dead space under the last bullet */
  .feat2{padding:34px 0!important;gap:22px!important}
  .imgstage .ov-chip .ov-ic{width:22px;height:22px;flex:none;border-radius:50%;
    background:var(--dw-tint)!important;color:var(--dw)!important;margin-top:1px}
  .imgstage .ov-chip .ov-ic svg{width:12px;height:12px}
}


/* Founder-dragged chips overhang the stage by up to ~15% on purpose. Between
   phone (where the image is dropped) and full desktop the window gutter can be
   smaller than that overhang, so the pills were clipped at the viewport edge
   (founder bug 2026-08-13; a JS clamp that slid chips inward was worse and got
   reverted). Instead give the stage side head-room in that band: the art gives
   up a little width so the overhangs stay on screen exactly where they were
   placed relative to the image. */
@media(min-width:701px) and (max-width:1560px){
  .feat2 .imgstage{margin-inline:clamp(16px,3.2vw,58px)}
}

/* The whole hero console is a Book-a-Demo target (founder 2026-07-23): a full-cover
   anchor rather than a click handler, so it behaves the same in the static mockups
   and in the React port, which strips page scripts. It sits above the console's own
   controls deliberately - every click in the component should lead to the demo. */
.dwconsole{position:relative;cursor:pointer}
.dwc-cover{position:absolute;inset:0;z-index:9;border-radius:inherit;display:block}
.dwc-cover:focus-visible{outline:2px solid var(--dw-bright);outline-offset:3px}

/* Big pull-quotes carry an inline white-space:nowrap so they hold one line on
   desktop ("Sleep through the 2am incident."). At phone width that clips at the
   edge, so let them wrap. */
@media(max-width:700px){
  .q{white-space:normal!important}
}

/* ============================================================================
   APERTURE - the form-page design (book-demo / contact / design-partners)
   ----------------------------------------------------------------------------
   Merged out of docs/mockups/website/v6-lab/lab-form-pages/ (c1-aperture.css +
   lab-form.css, the approved concept) so any v6 mockup HTML page can use these
   class names directly instead of linking the lab stylesheets.

   >>> TWIN WARNING <<<
   These same .ap / .lf rules ALSO live in src/styles/form-aperture.css, which is
   what the three React form routes load - they render <Navigation hideCTA /> +
   <Footer /> (scoped copies of the chrome below) and deliberately do NOT load
   this file. CHANGE BOTH OR NEITHER. The two copies differ in exactly three
   ways, all forced:
     1. form-aperture.css carries a token-mirror block (--sans/--serif/--mono,
        --ink, --ink-2, --line-2, the --dw family, --card, --r-pill, --sh-sm/md)
        scoped to .ap/.lf, because the React app defines none of them. This copy
        needs no such block: they are on :root at the top of this file.
     2. form-aperture.css carries .ap-scoped copies of .btn / .btn-green and of
        the .eyebrow base declarations. This copy needs neither: both already
        exist at top level above.
     3. The plate image URL: relative `assets/bg/forest-wall.webp` here, served
        `/v6/assets/bg/forest-wall.webp` there. Same file, byte-identical.
   Every remaining declaration is identical. A handful of comments differ, but
   only where they name the file-specific nav/footer source.

   DROPPED AS LAB-ONLY CHROME, not part of the design: the whole .labbar block
   from lab-form.css (the collapsed concept name + background picker), and all of
   bg-variants.css - every rule in it is an `html[data-bg="..."]` token override
   driven by that picker, and the committed page renders on the concept's own
   default, which is the .ap block below. The lab markup's `data-n="01"`
   attributes are inert here too: only c2-ledger.css reads them.
   ========================================================================== */

/* ---- the Aperture composition ----
   The page is split in two. On the left, one of the wallpapers as a tall plate
   that stays put while you fill the form; it carries the headline and the
   one-line promise, set at the bottom where the art is quietest. On the right,
   the form on plain cream with no card around it: nothing to read but the fields.

   Wallpaper: forest-wall, the crop the HOME PAGE closing CTA uses ("ready to go
   autonomous"). Founder pick on 2026-08-08, replacing w06 "Glow symbols b".

   It is the right call for a structural reason, not just taste. The plate is the
   only PORTRAIT frame in the lab (548x650), and every other candidate is a
   landscape image with one horizontal subject, so cover keeps 47% of the width
   and cuts that subject - which is what made w06's hand read as "just some
   cropped finger". A tree canopy is a vertical subject with no single focal
   object to sever, so a narrow vertical slice of it is still a whole picture.
   It also hands the composition what it actually needs: the building wall fills
   the lower half with something dark and quiet, which is exactly where the
   headline and promise sit.

   The cost is real and is the trade to have an opinion about: forest-wall is
   458KB, on a page whose entire job is a five-field form.

   The scrim is anchored to the copy rather than to the plate (see .ap-art-in
   below), so the top of the plate keeps the sky untouched and the darkening only
   ever arrives directly above the first line of text. Measured with the text
   hidden, per-pixel, by verify-lab-form-pages.mjs. */

/* --vpos-ap is Aperture's own horizontal framing, and it exists because of the
   founder's note that the image reads as "just some cropped finger".
   The plate is portrait while every candidate is landscape, so `cover` matches
   the height and crops the WIDTH: only plate-aspect / image-aspect of it
   survives, which is 47% at 16:9 and 63% at 4:3.
   Those percentages are quoted against the plate measured at 1440x900, where it
   is 548x650 and the aspect is 0.843. The plate is fluid, not fixed: the column
   is a fraction of a max-width:1340px grid whose gap is clamp(30px,4vw,68px), so
   a wider viewport spends more on the gap and the plate gets slightly narrower
   (543.6x650, aspect 0.836, at 1920x1080), and min-height:min(560px,100vh-128px)
   shortens it on a shallow window. Across that range the aspect moves by under
   0.01 and the visible-width figures by under a point, so 47/63 hold as round
   numbers - they are just not constants. Framing that narrow slice on the
   image's geometric centre is what cut the hand in half, because the hand is not
   at the centre - in w06 its subject centroid measures 59.2%. Each image
   therefore records its own measured subject centre (_encode_bg.py subject_x),
   and the plate frames on that instead of on 50%. forest-wall's is 72.1%. */
/* --vsc:1 not the measured 0.8 for this image: the founder saw the contact sheet
   tiles at scrim x1.0 and preferred that weight, so it is a deliberate taste
   override of the measured value, not a mistake. It veils MORE, so white text
   contrast only improves; the cost is a darker photograph. */
/* IMAGE PATH: the lab writes url(bg/forest-wall.webp) relative to
   lab-form-pages/. The file was promoted to assets/bg/ so both twins can reach
   it - scripts/sync-v6-assets.mjs copies ['assets','fonts','uploads',...] into
   public/v6/ for the React app and does not include the lab dir. Same move
   _encode_bg.py already made for sky-hand-dense.webp.

   :root, NOT .ap, and the reason is two separate CSS rules that both bite here.

   1. A relative url() inside a CUSTOM PROPERTY resolves against the DOCUMENT,
      not against the stylesheet that declared it. Normal properties resolve
      against the stylesheet, which is why url(assets/bg/tree.png) on .hero
      higher up in this file is correct everywhere. Custom properties are the
      exception, so `assets/bg/...` here means "relative to whatever page is
      being viewed". At / that is /assets/bg/... and correct; from
      /lab-form-pages/ it becomes /lab-form-pages/assets/bg/... and 404s.
   2. Custom properties inherit, and for inherited values the NEAREST ANCESTOR
      wins regardless of specificity or file order. Declared on .ap (which sits
      on <main>) this beat lab-form-pages/c1-aperture.css's :root on every lab
      page, even though that file loads later. The lab could not override it.

   Together those broke the plate on all nine Aperture lab combinations: the
   image 404'd and the gate correctly dropped 36/36 to 27/36. Declared on :root
   instead, c1-aperture.css's own :root loads afterwards and wins for the lab
   (url(bg/forest-wall.webp), co-located, 200), while the root-level mockup pages
   still take this value and resolve it correctly against /. The React routes are
   unaffected either way: form-aperture.css uses an absolute /v6/assets/bg/... so
   it has no document-relative resolution to get wrong. */
:root{--vimg:url(assets/bg/forest-wall.webp);--vpos:center 46%;--vpos-ap:72.1% 50%;--vsc:1}

/* No bottom padding: footer{margin-top:var(--s11)} above already contributes the
   site-wide 128px, verified identical on /, /product/ and /thesis/. The 100px
   that used to be here STACKED on that. Measured form-end to footer before the
   change: 386px desktop, 212px mobile. */
/* align-items:stretch, founder 2026-08-09: "the height of /contact 2 things arent
   the same size", repeated for /book-demo and /design-partners. Under `start`
   neither panel stretched, so the plate sat at exactly its 560px floor everywhere
   while the card was content-sized (605/533/623 at 1440x900). */
.ap{max-width:1340px;margin:0 auto;padding:14px 32px 0;
  display:grid;grid-template-columns:minmax(0,45fr) minmax(0,55fr);gap:clamp(30px,4vw,68px);align-items:stretch}
@media(max-width:640px){.ap{padding-left:0;padding-right:0}}

/* ---- the plate ---- */
/* position:sticky (top:82px) is GONE and equal heights are why: under
   align-items:stretch the plate fills its row, so sticky's slack is zero by
   construction. It was also the cause of the second half of the founder's
   complaint - with 45-63px of slack on /contact and the pilot page it slid the
   plate to top:82 against the card's top:80, a visible 2px step. */
.ap-art{overflow:hidden;border-radius:24px;
  /* Only a floor for a hypothetically tiny form now - the row sets the working
     height. 560 -> 480 so it stops binding: at 560 the four-field /book-demo card
     (533) would be stretched 27px, adding dead white inside the box. */
  min-height:min(480px,calc(100vh - 128px));
  display:flex;flex-direction:column;justify-content:flex-end;
  background-image:var(--vimg);
  background-size:cover;background-position:var(--vpos-ap,var(--vpos));background-repeat:no-repeat}

/* The scrim is anchored to the TEXT, not to the plate.
   Keyed to the plate it went wrong on the pilot page, whose headline runs four
   lines: the copy grew upward into the band of the gradient that is still almost
   clear sky, and the eyebrow measured 1.82:1 against white at 390px. Anchored
   here, the ramp always finishes inside the padding directly above the first line
   whatever the headline's length, and the top of the plate keeps the wallpaper
   completely untouched. */
.ap-art-in{width:100%;padding:124px clamp(26px,3vw,42px) clamp(26px,3vw,42px);
  background:linear-gradient(180deg,
    rgba(4,10,7,0) 0,
    rgba(4,10,7,calc(.42*var(--vsc))) 56px,
    rgba(4,10,7,calc(.70*var(--vsc))) 100px,
    rgba(4,10,7,calc(.86*var(--vsc))) 152px,
    rgba(4,10,7,calc(.93*var(--vsc))) 100%)}
/* The three approved pages carry NO eyebrow on the plate - the form's own name
   moved to .lf-legend instead. Kept so the slot still works if a page wants one. */
.ap-art .eyebrow{color:#fff}
.ap-art .eyebrow::before{background:var(--dw-bright);opacity:1}
.ap-art h1{text-wrap:balance;font-family:var(--serif);font-weight:400;color:#fff;
  font-size:clamp(38px,3.9vw,58px);line-height:1.02;letter-spacing:-.018em;margin:16px 0 14px}
.ap-art h1 em{font-family:var(--serif);font-style:italic;color:#DFF5E5;letter-spacing:0}
.ap-art p{font-family:var(--sans);font-size:15.5px;line-height:1.6;color:#EBF1ED;margin:0;max-width:40ch}
/* Founder 2026-08-09: "I would add a para break line before this - Natively
   integrated with the coding agents your team already uses". Was one <p> with a
   <br>, which gives a new line but no gap. 14px against a 24.8px line reads as a
   break without opening a hole in bottom-pinned copy. p + p so single-paragraph
   pages keep margin:0. */
.ap-art p + p{margin-top:14px}

/* ---- the form side ---- */
/* Founder 2026-08-09: white card around the form, replacing the bare-cream
   treatment the concept shipped with. TWIN of src/styles/form-aperture.css. */
.ap-form{background:var(--card);border:1px solid var(--line-2);border-radius:20px;
  padding:clamp(26px,2.6vw,38px)}
.ap-form .lf{gap:26px}
.ap-form .lf-actions{margin-top:10px}
/* The border-bottom here drew a full-width rule under "Want to try it yourself?",
   which the founder asked to remove on 2026-08-09. The link keeps its own
   underline: that is the only non-colour signal that it IS a link, and dropping
   it would leave colour alone to carry that, which fails WCAG 1.4.1. */
.ap-form .lf{padding-bottom:0}

/* ---- form primitives (.lf) ----
   TWO DELIBERATE DEPARTURES from the pre-Aperture form pages, both for
   legibility, both measured:
   1. Labels are --ink-2 (#4C5A54), not --ink-3 (#8B958F). #8B958F on white
      measures 2.60:1, which fails WCAG AA for a 10px label. #4C5A54 also matches
      what the React components already used (#4A5550) rather than the mockup.
   2. Every field is bound to its label with for/id, and focus paints a RING as
      well as recolouring the underline. A 1px green border-bottom on its own is
      easy to miss and carries no signal for anyone who cannot separate the green
      from the grey. */
.lf{display:flex;flex-direction:column;gap:22px;min-width:0}
/* Two-up field row. Aperture itself never uses it - all three pages run one field
   per line - but it is a .lf primitive, not concept styling, so it ships too. */
.lf-row{display:grid;grid-template-columns:1fr 1fr;gap:22px 30px;min-width:0}
@media(max-width:620px){.lf-row{grid-template-columns:1fr}}
.lf-field{display:flex;flex-direction:column;gap:7px;min-width:0}
.lf-field label{font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-2);line-height:1.4;font-weight:400}
.lf-field input,.lf-field select,.lf-field textarea{
  width:100%;min-width:0;font-family:var(--sans);font-size:16px;line-height:1.5;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--line-2);border-radius:0;
  padding:9px 2px;outline:none;transition:border-color .18s,box-shadow .18s}
.lf-field textarea{resize:vertical;min-height:106px}
/* The chevron is an inline data: URI, so it needs no asset and no path rewrite
   between this copy and the form-aperture.css twin. Its stroke is the literal
   #4C5A54 (--ink-2) because a var() cannot be interpolated into a url(). If
   --ink-2 ever changes, change it here too. */
.lf-field select{cursor:pointer;-webkit-appearance:none;appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234C5A54' stroke-width='2' stroke-linecap='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat:no-repeat;background-position:right 2px center;background-size:15px;padding-right:24px}
.lf-field input::placeholder,.lf-field textarea::placeholder{color:#6B7570;opacity:1}

/* Focus: `:focus` as well as `:focus-visible`, so a pointer user also gets the
   ring on a field they have clicked into. */
.lf-field input:focus,.lf-field select:focus,.lf-field textarea:focus,
.lf-field input:focus-visible,.lf-field select:focus-visible,.lf-field textarea:focus-visible{
  border-bottom-color:var(--dw)}

.lf-actions{display:flex;flex-direction:column;align-items:flex-start;gap:14px;margin-top:4px}
/* The form's own name, moved here from the copy side on 2026-08-09 at the
   founder's request: the field list used to open straight onto "Full Name *" with
   nothing naming it. Sized as a section label rather than a page headline - the
   page already has an h1 - and bound to the form with aria-labelledby. */
/* The dash IS part of the design, restored on founder request 2026-08-09, with
   bold weight. It was removed earlier for the wrong reason: the bar is 26px of
   var(--dw-deep), the same colour as the label text, so the per-pixel contrast
   gate sampled the decoration as the background inside the text's own box and
   scored every page 1:1, passing 0/144 while reporting success. The text was
   always legible. Deleting a real design element to satisfy an instrument is
   backwards, so the instrument changed instead: verify-lab-form-pages.mjs now
   excludes ::before/::after decoration from the background sample.
   TWIN: this rule exists in shared.css, lab-form-pages/lab-form.css and
   src/styles/form-aperture.css. Change all three together. */
.lf-legend{font-family:var(--mono);font-size:11px;letter-spacing:.2em;text-transform:uppercase;
  font-weight:700;color:var(--dw-deep);margin:0 0 4px;display:flex;align-items:center;gap:10px}
.lf-legend::before{content:"";width:26px;height:1.5px;background:var(--dw-deep);flex:none}
.lf-note{font-family:var(--sans);font-size:14px;line-height:1.55;color:var(--ink-2);margin:0;max-width:44ch}
.lf-note a{color:var(--dw-deep);border-bottom:1px solid var(--dw-tint);white-space:nowrap}
.lf-note a:hover{border-bottom-color:var(--dw)}
/* Honeypot: same trick the React forms use. Bots fill it, /api/form drops the
   submission. aria-hidden + tabindex -1 keep it out of the a11y tree. */
.lf-hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;opacity:0;pointer-events:none}

/* ---- submit states ----
   The lab pages are static (onsubmit="return false") so they never rendered
   submitting / error / success. The React routes do, and all three already have
   that copy. Carried here so the twins stay one design. */
/* `disabled` while submitting. Kept as opacity so the button does not move or
   change size mid-submit, and the hover lift is cancelled so a pointer resting on
   it does not imply it is still pressable. */
.ap .btn:disabled,.ap .btn[aria-disabled="true"]{opacity:.6;cursor:default}
.ap .btn:disabled:hover,.ap .btn[aria-disabled="true"]:hover{transform:none;background:var(--dw);border-color:var(--dw);box-shadow:var(--sh-sm)}
/* Submit-failure message. Same metrics as .lf-note so the two read as one voice;
   only the colour differs. #D4183D is not a new colour: it is the --destructive
   already shipping in the React app's theme.css. Measured 5.03:1 against the
   page's #FBFAF8, so it passes WCAG AA for body text. The pre-Aperture components
   used Tailwind `red-400` (#F87171), which measures about 2.4:1 on cream and
   fails. */
.lf-error{font-family:var(--sans);font-size:14px;line-height:1.55;color:#D4183D;margin:0;max-width:44ch}
.lf-error a{color:inherit;border-bottom:1px solid currentColor}
/* Success. It replaces the <form class="lf"> INSIDE .ap-form and leaves the plate
   standing, so the page keeps its composition and its h1 instead of collapsing to
   a centred confirmation on bare cream. That also means .ap needs no variant and
   the grid never changes. */
.ap-ok{display:flex;flex-direction:column;align-items:flex-start;gap:18px;padding-top:6px}
.ap-ok-icon{color:var(--dw);flex:none}
.ap-ok h2{font-family:var(--serif);font-weight:400;font-size:clamp(34px,3.4vw,50px);line-height:1.04;
  letter-spacing:-.018em;color:var(--ink);margin:0}
.ap-ok p{font-family:var(--sans);font-size:15.5px;line-height:1.6;color:var(--ink-2);margin:0;max-width:44ch}
/* The "Back to Home" affordance. A quiet text link, not a second button: the
   page's one green button has already done its job by this point. */
.ap-ok .ap-back{display:inline-flex;align-items:center;gap:10px;font-family:var(--mono);font-size:11px;
  letter-spacing:.18em;text-transform:uppercase;color:var(--ink-2);background:none;border:0;padding:0;
  cursor:pointer;transition:color .18s}
.ap-ok .ap-back:hover{color:var(--ink)}
.ap-ok .ap-back:focus-visible{outline:2px solid var(--dw);outline-offset:3px;border-radius:3px}

/* ---- one column below 1000px ---- */
@media(max-width:1000px){
  .ap{grid-template-columns:1fr;gap:0;padding:0}
  /* The plate keeps a real height on a phone rather than shrink-wrapping the copy.
     Shrink-wrapped, the text filled the whole plate and the top of the first line
     landed in the part of the gradient that is still almost clear sky - measured
     1.87:1 against white. A 400px plate puts the whole text block back inside the
     dark two thirds and the artwork survives. */
  .ap-art{position:static;border-radius:0;min-height:400px}
  .ap-art-in{padding:112px 24px 34px}
  .ap-art h1{font-size:clamp(34px,8.4vw,46px)}
  .ap-form{margin:0 16px;padding:28px 22px}
}

/* ============================================================================
   IP notice placement - founder review 2026-08-21.
   He disliked the standalone band above the footer and asked for it folded into
   the existing copyright line. Every placement ships in the DOM; the attribute
   on <html> picks one. NOTE: none of these hide text from humans while showing
   it to crawlers. That would be cloaking and a manual-action risk. The variant
   that is genuinely agent-only is `meta`, which drops the visible prose entirely
   and leaves the notice in the meta tags, llms.txt, ai.txt, robots.txt and the
   /ip-notice page - all standard metadata channels, not hidden body copy.
   ========================================================================= */
.ip-band{display:none}
.foot-base .ip-foot-line{display:none}
.foot-base .ip-foot-plain{display:inline}
.foot-base .ip-foot-link{display:none}

/* band: the current standalone block above the footer */
[data-tw-ipplace="band"] .ip-band{display:block}

/* footline: folded into the existing copyright sentence (founder's request) */
[data-tw-ipplace="footline"] .foot-base .ip-foot-line{display:inline}
[data-tw-ipplace="footline"] .foot-base .ip-foot-plain{display:none}

/* footlink: copyright stays as-is, just an extra link beside Terms & Privacy */
[data-tw-ipplace="footlink"] .foot-base .ip-foot-link{display:inline}

/* meta: no visible prose anywhere. Agents still get it via meta tags + llms.txt
   + ai.txt + robots.txt + /ip-notice. Humans see only the normal copyright. */
[data-tw-ipplace="meta"] .ip-band{display:none}

.foot-base .ip-foot-line{max-width:min(100%,760px);line-height:1.65}
.foot-base .ip-foot-line a,.foot-base .ip-foot-link a{text-decoration:underline}

/* ============================================================================
   Homepage scroll feel - founder review 2026-08-22: "centre of gravity feels a
   little lower than it should be" scrolling section to section.
   FINDING: a global padding lever CANNOT move this. The homepage authors its
   vertical rhythm INLINE per section (style="padding-top:0", "padding-top:var(--s8)"
   and so on), and inline styles beat any stylesheet rule short of !important.
   Forcing it with !important would flatten spacing that was deliberately tuned
   section by section. So the working levers are all in the glide itself
   (data-tw-scroll in v6Runtime.ts: Lenis lerp + wheelMultiplier). Changing where a
   section RESTS means editing those inline values, which is a separate decision.
   scroll-margin-top below only affects anchored jumps, not wheel scrolling.
   Deliberately NOT scroll-snap: a mandatory snap is the "pause" that killed the
   buttery feel before.
   ========================================================================= */
[data-tw-scroll="settle"] .sec{scroll-margin-top:72px}

/* ============================================================================
   Platform card shade - founder review 2026-08-28. The four cards in the
   "Your data platform runs itself" deck are near-black (#0f1612) on a green
   panel (oklch(.46 .115 160)). He asked to see them in greens, especially one
   that matches the surrounding background. Every option keeps the same border,
   radius and shadow so only the fill changes.
   ========================================================================= */
[data-tw-cards="deep"]   .v4deck.dark .panel{background:oklch(.22 .062 160)}
[data-tw-cards="forest"] .v4deck.dark .panel{background:oklch(.30 .078 160)}
[data-tw-cards="mid"]    .v4deck.dark .panel{background:oklch(.375 .095 160)}
/* `match` is the exact panel green. With no fill contrast left, the card can only
   read as a card through its edge, so the border and shadow do the work. */
[data-tw-cards="match"]  .v4deck.dark .panel{
  background:oklch(.46 .115 160);
  border-color:rgba(255,255,255,.22);
  box-shadow:0 1px 0 rgba(255,255,255,.10) inset, 0 10px 30px rgba(0,0,0,.16)}
/* `glass` sits ON the background rather than over it: a light film, so the tree
   photograph behind the panel stays faintly readable through the card. */
[data-tw-cards="glass"]  .v4deck.dark .panel{
  background:rgba(255,255,255,.07);
  backdrop-filter:blur(6px);
  border-color:rgba(255,255,255,.20);box-shadow:none}

/* ============================================================================
   Slide-in reveal - founder asked for it to be CONSISTENT across the site.
   FINDING: it was not. Three systems shipped side by side.
     .rv        16px / .60s  cubic-bezier(.3,.7,.3,1)   most sections
     .hv        24px / .80s  cubic-bezier(.22,1,.36,1)  hero entry
     V6Cta.tsx  38px / .95s  motion/react               the CTA card
   `unified` puts all three on one distance, duration and curve. The other
   options scale that single system rather than reintroducing three.
   Reduced-motion still wins: the existing prefers-reduced-motion rules are
   later in this file and disable transforms regardless of the choice here.
   ========================================================================= */
[data-tw-anim="unified"] .rv,[data-tw-anim="unified"] .hv{
  transform:translateY(20px);
  transition:opacity .7s cubic-bezier(.22,1,.36,1),transform .7s cubic-bezier(.22,1,.36,1)}
[data-tw-anim="subtle"] .rv,[data-tw-anim="subtle"] .hv{
  transform:translateY(10px);
  transition:opacity .45s cubic-bezier(.22,1,.36,1),transform .45s cubic-bezier(.22,1,.36,1)}
[data-tw-anim="pronounced"] .rv,[data-tw-anim="pronounced"] .hv{
  transform:translateY(30px);
  transition:opacity .9s cubic-bezier(.22,1,.36,1),transform .9s cubic-bezier(.22,1,.36,1)}
[data-tw-anim="fade"] .rv,[data-tw-anim="fade"] .hv{
  transform:none;transition:opacity .6s ease}
[data-tw-anim="unified"] .rv.in,[data-tw-anim="subtle"] .rv.in,
[data-tw-anim="pronounced"] .rv.in,[data-tw-anim="fade"] .rv.in,
[data-tw-anim="unified"] .hv.in,[data-tw-anim="subtle"] .hv.in,
[data-tw-anim="pronounced"] .hv.in,[data-tw-anim="fade"] .hv.in{opacity:1;transform:none}

/* ============================================================================
   Product feature image scale - founder 2026-08-28: the images "look small".
   Verified NOT a regression: local and production both render 437x292 from a
   1536x1024 asset, identical, so nothing this session changed it.
   Cause is the grid ratio. .feat2 is `1fr 1.08fr` and the IMAGE STAGE IS THE
   SECOND CHILD (.feat2-copy comes first), so widening the first column shrinks
   the picture. These widen the SECOND column. `.flip` rows reverse the visual
   order, so they get the mirrored ratio.
   Desktop only; mobile is already a single stacked column.
   ========================================================================= */
@media (min-width: 861px) {
  [data-tw-imgscale="even"] .feat2{grid-template-columns:1fr 1.25fr}
  [data-tw-imgscale="lead"] .feat2{grid-template-columns:1fr 1.5fr;gap:56px}
  [data-tw-imgscale="big"]  .feat2{grid-template-columns:1fr 1.8fr;gap:48px}
  [data-tw-imgscale="full"] .feat2{grid-template-columns:1fr 2.1fr;gap:44px;padding:76px 0}

  /* ── `panel`: the Apollo reference (founder 2026-08-29) ──────────────────
     What actually distinguishes that layout from ours is not scale, it is the
     SOFT PANEL: the screenshot sits inside a large rounded grey field with real
     air around it, so it reads as a framed artefact rather than a floating png
     on cream. The copy column also narrows, so the picture leads the row.

     MEASURED OFF THE REFERENCE rather than guessed. At its 2000px width the
     content runs 105..1965; copy occupies 105..720 (33% of the content) and the
     grey panel 1055..1890 (45%), with a ~335px gap. Hence 1fr / 1.35fr and a
     wide clamped gap.

     NOT BLEEDING to the window edge, which is what I built first: the reference
     panel stops ~75px short of the content edge, so a bleed would be a
     different design. It also broke - the .ov-chip overlay is sized in cqw
     container units, so a wider stage grew the chips until they pushed 179px
     past the viewport. Keeping the stage inside the measure keeps that overlay
     working as drawn.

     `.stage.imgstage` uses BOTH classes on purpose. The mockup sets
     `.stage.imgstage{padding:0;background:transparent}` in its own <style>,
     which extractV6Body lifts in AFTER shared.css, so a single-class selector
     ties on specificity and loses on order - the first pass measured a
     transparent panel with zero padding. */
  /* 1.62fr, not the 1.35fr the reference's own ratio implies: the panel's
     padding comes out of the column, so matching the reference's PANEL width
     would have left the picture at 494px - smaller than the 525px currently
     shipping, which is the opposite of what this option is for. Sized so the
     IMAGE lands near 590px and the panel around the reference's proportion. */
  [data-tw-imgscale="panel"] .feat2{grid-template-columns:1fr 1.62fr;gap:clamp(40px,4.4vw,80px);padding:84px 0}
  [data-tw-imgscale="panel"] .feat2.flip{grid-template-columns:1.62fr 1fr}
  [data-tw-imgscale="panel"] .feat2 .lead{max-width:38ch}
  [data-tw-imgscale="panel"] .feat2 .stage.imgstage{
    background:var(--featfield, var(--bg-2));
    border-radius:26px;
    padding:clamp(22px,2.6vw,48px);
  }
  /* Inside the panel the image is the artefact, not the surface: tighter
     corners and a lighter shadow, because the panel is already doing the lift. */
  [data-tw-imgscale="panel"] .feat2 .stage.imgstage img{
    border-radius:14px;box-shadow:0 18px 40px rgba(16,50,40,.15)}
  @media(max-width:1100px){
    [data-tw-imgscale="panel"] .feat2,
    [data-tw-imgscale="panel"] .feat2.flip{grid-template-columns:1fr 1.2fr}
  }

  /* ── `apollo`: the reference's actual geometry ────────────────────────────
     Founder 2026-08-29: the `panel` option above has the soft field but not the
     PLACEMENT. He is right. Measured off his screenshot at 2000px wide, with a
     content span of 105..1890:

       copy  105..720   615px  34.5%
       gap   720..1053  333px  18.7%
       panel 1053..1890 837px  46.9%   and 840px TALL - square

     Two things `panel` gets wrong and this fixes. The gap is 18.7% of the
     content, roughly a fifth of the row given over to nothing; `panel` used a
     66px gap at 1500px, a third of that. And the field is SQUARE, not the
     image's own 3:2 - the artwork floats in the middle of a tall panel with
     real air above and below it, which is what makes the copy read as centred
     against something rather than beside it.

     WHY A PSEUDO-ELEMENT AND NOT aspect-ratio ON THE STAGE. The stage carries
     .ov-chip overlays positioned in percentages and sized in container units.
     Changing the stage's own box from 3:2 to 1:1 moves every chip off the
     artwork it is meant to label. Drawing the square field BEHIND the image
     instead leaves the stage geometry - and so the chips - untouched.

     The offsets are in cqw because the stage already declares
     container-type:inline-size. For a 3:2 image of width W in a square field
     with side padding p: the field is W+2p across, so each vertical inset is
     ((W+2p) - W/1.5)/2 = 0.167W + p. At p = 6.8cqw (the reference's 57/837)
     that is 23.5cqw. */
  [data-tw-imgscale="apollo"] .feat2,
  [data-tw-imgscale="apollowide"] .feat2,
  [data-tw-imgscale="dots"] .feat2,
  [data-tw-imgscale="nopanel"] .feat2{
    grid-template-columns:1fr 1.174fr;
    gap:0 21.9%;
    align-items:center;
    padding:104px 0}
  [data-tw-imgscale="apollo"] .feat2.flip,
  [data-tw-imgscale="apollowide"] .feat2.flip,
  [data-tw-imgscale="dots"] .feat2.flip,
  [data-tw-imgscale="nopanel"] .feat2.flip{grid-template-columns:1.174fr 1fr}
  [data-tw-imgscale="apollo"] .feat2 .lead,
  [data-tw-imgscale="apollowide"] .feat2 .lead,
  [data-tw-imgscale="dots"] .feat2 .lead,
  [data-tw-imgscale="nopanel"] .feat2 .lead{max-width:34ch}
  /* THE IMAGE FILLS THE STAGE; the field overhangs it. This is the third
     arrangement and the only one that satisfies all three constraints.

       attempt 1  field overhangs, image fills   -> gap measured 15.5% not 18.7%,
                  because the field reached into the air
       attempt 2  field flush, image inset 86.4% -> proportions exact, but the
                  .ov-chip overlays are positioned against the STAGE, so
                  insetting the image left them sitting off the artwork and 59px
                  past the viewport
       attempt 3  field overhangs AND the CSS gap is widened to compensate, so
                  the VISUAL air is 18.7% while the image still fills the stage
                  and the chips stay exactly where they were drawn.

     Numbers: inset p = 6.8/86.4 = 7.87% of the stage. Field = stage x 1.1574,
     so for a 46.9% field the stage is 40.5% of the row and the copy 34.5%,
     i.e. 1 : 1.174. CSS gap = 18.7% + 7.87% x 40.5% = 21.9%.
     Vertical: a 3:2 image at full stage width is 0.667S tall against a 1.1574S
     square, so (1.1574 - 0.667) / 2 = 24.5cqw above and below. */
  [data-tw-imgscale="apollo"] .feat2 .stage.imgstage::before,
  [data-tw-imgscale="apollowide"] .feat2 .stage.imgstage::before,
  [data-tw-imgscale="dots"] .feat2 .stage.imgstage::before,
  [data-tw-imgscale="nopanel"] .feat2 .stage.imgstage::before{
    content:"";position:absolute;z-index:0;
    left:-7.87cqw;right:-7.87cqw;top:-24.5cqw;bottom:-24.5cqw;
    background:var(--featfield, var(--bg-2));border-radius:26px}
  [data-tw-imgscale="apollo"] .feat2 .stage.imgstage img,
  [data-tw-imgscale="apollowide"] .feat2 .stage.imgstage img,
  [data-tw-imgscale="dots"] .feat2 .stage.imgstage img,
  [data-tw-imgscale="nopanel"] .feat2 .stage.imgstage img{
    position:relative;z-index:1;
    border-radius:14px;box-shadow:0 18px 40px rgba(16,50,40,.15)}
  /* `apollowide` - the same geometry, on a wider measure.
     The reference's proportions cost image size on our page: their content span
     is 1785px, ours is capped at 1084px whatever the window, so a 46.9% field
     lands at 508px and the picture inside at 439px - smaller than the 525px
     currently shipping. Giving the product sections a 1560px measure buys back
     the scale while keeping the proportions exact. Scoped to the feature rows'
     own section so the rest of the page keeps its reading measure. */
  /* min() against the window, not a bare 1560, and 200px of slack rather than the
   section's own 96px of padding: the grey field overhangs its grid column by
   7.87cqw on each side, so at the wider measure the right-hand overhang cleared
   the section box and pushed 43px past the viewport. */
  [data-tw-imgscale="apollowide"] .sec:has(> .feat2){max-width:min(1560px, calc(100vw - 200px))}

  /* Below the reference's own breakpoint the fifth-of-the-row gap stops being
     air and starts being a squeeze, so it comes back to something usable. */
  @media(max-width:1240px){
    [data-tw-imgscale="apollo"] .feat2, [data-tw-imgscale="apollowide"] .feat2,
    [data-tw-imgscale="dots"] .feat2, [data-tw-imgscale="nopanel"] .feat2,
    [data-tw-imgscale="apollo"] .feat2.flip, [data-tw-imgscale="apollowide"] .feat2.flip,
    [data-tw-imgscale="dots"] .feat2.flip, [data-tw-imgscale="nopanel"] .feat2.flip{gap:0 9%}
  }
  [data-tw-imgscale="even"] .feat2.flip{grid-template-columns:1.25fr 1fr}
  [data-tw-imgscale="lead"] .feat2.flip{grid-template-columns:1.5fr 1fr}
  [data-tw-imgscale="big"]  .feat2.flip{grid-template-columns:1.8fr 1fr}
  [data-tw-imgscale="full"] .feat2.flip{grid-template-columns:2.1fr 1fr}
}

/* ============================================================================
   Products dropdown weight - founder 2026-08-28: "isn't elegant and regal enough
   to get people to click". Today the four product cards are 18.5px/600 titles
   over 16.5px descriptions in 8px/12px gutters, which reads like a list rather
   than a menu of four products. Each option below turns up type weight, size and
   spacing together, because doing one without the others just makes it shouty.
   Desktop only; the mobile drawer is a separate component.
   ========================================================================= */
@media (min-width: 861px) {
  /* bolder: same footprint, more typographic authority */
  [data-tw-ddprod="bolder"] .dd-products{gap:10px 14px;padding:20px 16px 16px}
  [data-tw-ddprod="bolder"] .dd-products .t{font-size:20px;font-weight:700;letter-spacing:-.01em}
  [data-tw-ddprod="bolder"] .dd-products .s{font-size:15.5px;margin-top:6px}
  [data-tw-ddprod="bolder"] .dd-products a{padding:14px 14px}

  /* roomy: bigger cards, clear separation, description steps back */
  [data-tw-ddprod="roomy"] .dd-products{gap:14px 18px;padding:24px 20px 20px;
    width:min(720px,calc(100vw - 340px))}
  [data-tw-ddprod="roomy"] .dd-products .t{font-size:22px;font-weight:700;letter-spacing:-.015em;line-height:1.25}
  [data-tw-ddprod="roomy"] .dd-products .s{font-size:15px;margin-top:8px;line-height:1.5}
  [data-tw-ddprod="roomy"] .dd-products a{padding:18px 18px;border-radius:10px}

  /* showcaseair: chosen from the lab 2026-08-29. Showcase's own cards, kept at
     their exact size, in a panel the width of the Agents mega, with everything
     the extra width buys left as air either side.

     The width expression is the mega's own - min(1440px, calc(100vw - 380px))
     from .dd-agents-mega above - reused verbatim rather than a lookalike
     number, so the two menus cannot drift apart. That consistency is the whole
     reason he picked it.

     310px tracks because that is what Showcase's cards measure: its 680px panel
     less 24px of padding a side less the 12px gap, halved. Two empty 1fr tracks
     do the centring, so the air stays equal at any width, and every cell is
     placed explicitly - with auto-placement a link drops into an air column the
     moment a row fills. */
  /* Cards LEFT-ANCHORED under the Products label, not centred in the panel -
     founder 2026-08-29, twice. The first pass only fixed it for ddwide=full,
     which is why he still saw it wrong: he was reviewing on ddwide=current,
     where the centred grid put the first card 120px to 390px right of the
     label depending on window width.

     There is no single offset that works, because each ddwide mode anchors the
     panel differently, and the alignment target is fixed: the Products trigger
     sits at a constant 189px from the viewport left at every width, since the
     header is left-anchored. So the padding is 189px minus wherever the panel
     starts, worked out per mode. Measured panel lefts: 175px here (the panel
     hangs 14px left of its trigger), 16px on `full`, and a viewport-centred
     expression on `wide`.

     This base rule is the ddwide=current case: left:-14px, so 14px of padding
     puts the first card exactly on the label. Width-independent, which the
     other two are not. */
  [data-tw-ddprod="showcaseair"] .dd-products{
    width:min(1440px, calc(100vw - 380px));padding:24px 24px 24px 14px;
    grid-template-columns:minmax(0,310px) minmax(0,310px) 1fr;
    grid-template-rows:auto 1fr 1fr;gap:12px}
  [data-tw-ddprod="showcaseair"] .dd-products .ds-h{grid-column:1/3;grid-row:1;border-bottom:0;padding:4px 2px 8px}
  [data-tw-ddprod="showcaseair"] .dd-products a:nth-of-type(1){grid-column:1;grid-row:2}
  [data-tw-ddprod="showcaseair"] .dd-products a:nth-of-type(2){grid-column:2;grid-row:2}
  [data-tw-ddprod="showcaseair"] .dd-products a:nth-of-type(3){grid-column:1;grid-row:3}
  [data-tw-ddprod="showcaseair"] .dd-products a:nth-of-type(4){grid-column:2;grid-row:3}
  [data-tw-ddprod="showcaseair"] .dd-products a{padding:20px 20px 22px;border-radius:12px;
    background:var(--bg-2);border:1px solid transparent;
    transition:border-color .16s,background .16s,transform .16s}
  /* The green dash under each card is gone - founder 2026-08-29: "remove the
     dash thats at the bottom of each card". It was a hover affordance that grew
     26px -> 54px; the card's own background and lift already carry that, so
     nothing is lost but the ornament. */
  [data-tw-ddprod="showcaseair"] .dd-products a:hover{background:var(--card);border-color:var(--line-2);
    transform:translateY(-2px)}
  [data-tw-ddprod="showcaseair"] .dd-products .t{font-size:23px;font-weight:700;letter-spacing:-.018em;line-height:1.15}
  [data-tw-ddprod="showcaseair"] .dd-products .s{font-size:15.5px;margin-top:8px;line-height:1.5}

  /* regal: the founder's word. Serif display titles, generous air, hairline
     rules instead of filled tiles, so it reads as a considered menu. */
  [data-tw-ddprod="regal"] .dd-products{gap:16px 22px;padding:28px 24px 24px;
    width:min(760px,calc(100vw - 320px))}
  [data-tw-ddprod="regal"] .dd-products .t{font-family:var(--serif,Georgia,serif);
    font-size:25px;font-weight:600;letter-spacing:-.02em;line-height:1.18}
  [data-tw-ddprod="regal"] .dd-products .s{font-size:15px;margin-top:9px;line-height:1.5;color:var(--ink-2)}
  [data-tw-ddprod="regal"] .dd-products a{padding:18px 16px;border-radius:0;background:transparent;
    border-top:1px solid var(--line)}
  [data-tw-ddprod="regal"] .dd-products a:hover{background:transparent;border-top-color:var(--dw)}
  [data-tw-ddprod="regal"] .dd-products .ds-h{font-size:12px;letter-spacing:.22em;padding-bottom:10px}
}

/* ============================================================================
   Persona band shade - founder 2026-08-28, asked for these three cards ("For
   Platform Leads / For Data Engineers / For Analysts & Data Scientists") to get
   the same treatment as the platform card shade group, having shipped `mid`
   there.

   The ladder is deliberately the SAME oklch ramp as [data-tw-cards], so the two
   decks can be judged as a set rather than drifting apart. One difference that
   matters: the platform cards sit on a green panel over a photograph, these sit
   on flat cream, so the same value reads lighter here and the light end of the
   ramp needs its text colours flipped rather than just its fill.

   Specificity note: .pscard's own background lives in index.html's <style>,
   which extractV6Body lifts INTO the page after shared.css. An attribute
   prefix takes these to (1,1,0) so they win regardless of injection order. */
[data-tw-pcards="deep"]   .pscard{background:oklch(.22 .062 160)}
[data-tw-pcards="forest"] .pscard{background:oklch(.30 .078 160)}
[data-tw-pcards="mid"]    .pscard{background:oklch(.375 .095 160)}
[data-tw-pcards="bright"] .pscard{background:oklch(.46 .115 160)}
[data-tw-pcards="bright"] .pscard .psk{color:#CFF3DF}
/* `tint` inverts the band: a pale green card on cream with dark type, so the
   trio recedes into the page instead of reading as three dark blocks. */
[data-tw-pcards="tint"] .pscard{background:oklch(.94 .028 160);border:1px solid oklch(.88 .04 160)}
[data-tw-pcards="tint"] .pscard .psk{color:oklch(.48 .12 160)}
[data-tw-pcards="tint"] .pscard .pst{color:var(--ink)}
/* `outline` keeps the cream ground and draws the card with an edge only. */
[data-tw-pcards="outline"] .pscard{background:transparent;border:1px solid oklch(.86 .045 160)}
[data-tw-pcards="outline"] .pscard .psk{color:oklch(.48 .12 160)}
[data-tw-pcards="outline"] .pscard .pst{color:var(--ink)}

/* ============================================================================
   Platform deck width - founder 2026-08-29, with an Apollo screenshot: run the
   green box end to end with no white gutter beside it.

   Today .platdark is a rounded card inside #platform, which is max-width:1300px
   sitting in .wrap's own max-width + padding, so the green stops well short of
   the window on a wide screen.

   Breaking out of that chain needs viewport units - there is no ancestor to
   inherit full width from. The negative-margin trick is `calc(50% - 50vw)` on
   both sides, and the reason the rules below ALSO pin the inner content back to
   a measure is that letting an eyebrow, an h2 and a four-card deck run to the
   window edges is not what the reference does either: the sample is full-bleed
   BACKGROUND with centred content.

   Specificity: #platform's own rules live in index.html's <style>, which
   extractV6Body lifts into the page AFTER shared.css. `[data-tw-platbleed=..]
   #platform > .platdark` is (1,2,0) and clears the (0,1,0) `.platdark` base
   regardless of injection order.
   ========================================================================= */

/* `inset` - the Scale AI treatment the founder sent 2026-08-29: near the full
   width, but a thin margin survives on both sides and the corners stay round,
   so the panel reads as a card floating on the page rather than a band welded
   to it. Measured off their screenshot: ~35px of page either side of a 1095px
   window, i.e. a hair over 3%. Uses the same break-out as the others, just
   stopping short of the edge. */
[data-tw-platbleed="inset"] #platform > .platdark{
  margin-left:calc(50% - 50vw + clamp(16px, 3.2vw, 44px));
  margin-right:calc(50% - 50vw + clamp(16px, 3.2vw, 44px));
  padding-left:max(clamp(26px,4vw,56px), calc(50vw - var(--maxw) / 2));
  padding-right:max(clamp(26px,4vw,56px), calc(50vw - var(--maxw) / 2));
}
[data-tw-platbleed="inset"] body{overflow-x:clip}

/* Founder 2026-08-30: "remove the white side padding on mobile for this section
   as well, the fix was properly applied on the By the Numbers section."

   The earlier fix took the SECTION's padding to 0 and that part held - the
   section measured left 0, width 390. What was left is the deck panel's own
   16px inset, which comes from platbleed="inset" rather than from gutters, so
   the gutters fix could never have reached it. The clamp floors at 16px, and
   16px each side of a 390px screen is the last of the white edge he is pointing
   at.
   Matched to the By the Numbers panel exactly - that measures left 0, width
   390, margin 0, and keeps its 28px radius at full bleed - so the two green
   blocks now sit on the same edge on a phone. */
@media(max-width:640px){
  [data-tw-platbleed="inset"] #platform > .platdark{
    margin-left:calc(50% - 50vw);
    margin-right:calc(50% - 50vw)}
}

/* Shared by every bleed option: escape the measure, drop the side edges. */
[data-tw-platbleed="bleed"] #platform > .platdark,
[data-tw-platbleed="flush"] #platform > .platdark,
[data-tw-platbleed="rounded"] #platform > .platdark{
  margin-left:calc(50% - 50vw);
  margin-right:calc(50% - 50vw);
  border-left:0;border-right:0;
  padding-left:max(clamp(26px,4vw,56px), calc(50vw - var(--maxw) / 2));
  padding-right:max(clamp(26px,4vw,56px), calc(50vw - var(--maxw) / 2));
}
/* `bleed` - square across, white band above and below kept. */
[data-tw-platbleed="bleed"] #platform > .platdark{border-radius:0}
/* `rounded` - full width but the big radius survives on the top corners, which
   is what the reference screenshot actually shows against the bar above it. */
[data-tw-platbleed="rounded"] #platform > .platdark{
  border-radius:var(--r-xl) var(--r-xl) 0 0;border-bottom:0}
/* `flush` - the literal reading of "no white space": edge to edge AND vertically
   butted against its neighbours, so the green is the only thing between the
   section above and the one below. */
[data-tw-platbleed="flush"] #platform{padding-top:0!important;padding-bottom:0!important}
[data-tw-platbleed="flush"] #platform > .platdark{border-radius:0;border-top:0;border-bottom:0}

/* A full-bleed child inside a centred parent is the classic way to introduce a
   horizontal scrollbar, because 100vw counts the vertical scrollbar's width.
   Clip at the page root rather than trusting every ancestor to behave. */
[data-tw-platbleed="bleed"] body,
[data-tw-platbleed="flush"] body,
[data-tw-platbleed="rounded"] body{overflow-x:clip}

/* ============================================================================
   Platform deck motion - founder 2026-08-29, asked for "cool smooth animation
   that's consistent too" alongside the full-bleed option.

   CONSISTENT means one thing here: every option below uses the SAME curve and
   duration token as the unified slide-in reveal above,
   cubic-bezier(.22,1,.36,1) at .7s, scaled rather than replaced. No new easing
   is introduced, which is the whole point of the earlier reveal audit.

   WHY animation-timeline AND NOT the .rv observer. .platdark carries no `.rv`
   class, and adding one would put a major section behind the reveal observer:
   it would start at opacity 0 and stay invisible if the JS ever failed, which
   is the exact failure the reveal-flash guard in extractV6Body exists to catch.
   A scroll-driven CSS animation needs no class, no observer and no JS, and
   under @supports it simply does not apply where the browser lacks it - the
   deck renders in its final state rather than in a broken one.

   `widen` only reads as motion when a bleed option is on, because it animates
   from the boxed measure OUT to the full width. On `current` the start and end
   are the same box and nothing appears to move. That is stated in the tweak
   note rather than silently doing nothing.
   ========================================================================= */
@supports (animation-timeline: view()) {
  /* Clip-path, not width or margin: it is composited, so the panel widens
     without reflowing the four cards inside it on every frame. The start inset
     is exactly #platform's 1300px measure, so the animation begins on the
     boxed edge the visitor already knows. */
  [data-tw-platanim="widen"] #platform > .platdark,
  [data-tw-platanim="both"] #platform > .platdark{
    animation:dw-plat-widen both cubic-bezier(.22,1,.36,1);
    animation-timeline:view();
    animation-range:entry 10% entry 85%;
  }
  @keyframes dw-plat-widen{
    from{clip-path:inset(0 max(0px, calc(50% - 650px)) round 28px)}
    to{clip-path:inset(0 0 0 0 round 0)}
  }
}
/* `stagger` retunes the deck's existing four-card cascade onto the unified
   curve. The delays were .13s steps on cubic-bezier(.3,.7,.3,1); this puts them
   on the same .7s / (.22,1,.36,1) as every other reveal and widens the step a
   little so the cascade reads as deliberate rather than as lag. */
/* The .7s is now --platdur so it can be slowed without editing this rule.
   Founder 2026-08-30: "also then can be slower than .7s." The per-card delays
   below scale with it, otherwise a slower card duration just makes the four
   overlap into one blur instead of reading as a cascade. */
[data-tw-platanim="stagger"] .v4deck .panel.rv,
[data-tw-platanim="both"] .v4deck .panel.rv{
  transform:translateY(20px);
  transition:opacity var(--platdur,.7s) cubic-bezier(.22,1,.36,1) var(--rvd,0s),
             transform var(--platdur,.7s) cubic-bezier(.22,1,.36,1) var(--rvd,0s)}
[data-tw-platanim="stagger"] .v4deck .panel.rv-w1,[data-tw-platanim="both"] .v4deck .panel.rv-w1{--rvd:calc(.08s * var(--platstep,1))}
[data-tw-platanim="stagger"] .v4deck .panel.rv-w2,[data-tw-platanim="both"] .v4deck .panel.rv-w2{--rvd:calc(.20s * var(--platstep,1))}
[data-tw-platanim="stagger"] .v4deck .panel.rv-w3,[data-tw-platanim="both"] .v4deck .panel.rv-w3{--rvd:calc(.32s * var(--platstep,1))}
[data-tw-platanim="stagger"] .v4deck .panel.rv-w4,[data-tw-platanim="both"] .v4deck .panel.rv-w4{--rvd:calc(.44s * var(--platstep,1))}
[data-tw-platanim="stagger"] .v4deck .panel.rv.in,[data-tw-platanim="both"] .v4deck .panel.rv.in{opacity:1;transform:none}

/* Reduced motion outranks every option here: hold the final state. */
@media (prefers-reduced-motion: reduce){
  [data-tw-platanim] #platform > .platdark{animation:none!important;clip-path:none!important}
  [data-tw-platanim] .v4deck .panel.rv{transition:none!important;transform:none!important}
}

/* ============================================================================
   Homepage gutters - founder 2026-08-29: after exploring full bleed on two
   individual sections, asked to see it for EVERY section below the hero.

   Scope is the four sections that actually sit under the hero on the homepage:
   #platform, #personas, .btn-sec and #customers. Named explicitly rather than
   matched as `.sec.wrap`, because that class pair is the layout of every
   content page on the site and this option is a homepage question.

   The hero is deliberately excluded - the founder said "after the landing
   page", and the hero's own measure is what holds its headline together.

   !important IS LOAD-BEARING, not laziness. #personas and .btn-sec set
   `max-width:1300px` in an INLINE style attribute (index.html:518, 535), and an
   inline declaration outranks any stylesheet selector no matter how specific.
   This is the same trap that silently killed the scroll-feel padding lever
   earlier in this session; without !important two of the four sections would
   simply not respond and the option would look half-broken.
   ========================================================================= */
[data-tw-gutters="wide"] #platform,
[data-tw-gutters="wide"] #personas,
[data-tw-gutters="wide"] .btn-sec,
[data-tw-gutters="wide"] #customers{max-width:1560px!important}

[data-tw-gutters="bleed"] #platform,
[data-tw-gutters="bleed"] #personas,
[data-tw-gutters="bleed"] .btn-sec,
[data-tw-gutters="bleed"] #customers{
  max-width:none!important;padding-left:48px!important;padding-right:48px!important}

/* MOBILE REGRESSION, founder 2026-08-30: "there seems to be a regression on the
   By the Numbers section in the mobile view, where there's white space and
   almost like a padding to the side of it, and we don't have that currently."
   Mine. `bleed` was shipped page-wide and its 48px breathing edge is a desktop
   number: on a 390px screen it takes 96px, a quarter of the width, and the
   panel inside measured 294px where it used to run the full 390. The edge goes
   to 0 on a phone, which is what shipped before this option existed. */
@media(max-width:640px){
  [data-tw-gutters="bleed"] #platform,
  [data-tw-gutters="bleed"] #personas,
  [data-tw-gutters="bleed"] .btn-sec,
  [data-tw-gutters="bleed"] #customers{
    padding-left:0!important;padding-right:0!important}
}

[data-tw-gutters="full"] #platform,
[data-tw-gutters="full"] #personas,
[data-tw-gutters="full"] .btn-sec,
[data-tw-gutters="full"] #customers{
  max-width:none!important;padding-left:0!important;padding-right:0!important}

/* At `full` the section is already flush with the window, so a deck still trying
   to break out of it would push past the edge. Neutralise the break-out ONLY
   where the deck has not been given a width of its own: `platbleed` is the
   group that owns this panel, and the founder hit exactly this - picking
   `wide`/`full` gutters silently undid his chosen Scale-style inset
   (2026-08-29). :not() keeps the two groups composable in all 20 combinations
   rather than letting whichever loads last win. */
html:not([data-tw-platbleed="inset"]):not([data-tw-platbleed="bleed"]):not([data-tw-platbleed="rounded"]):not([data-tw-platbleed="flush"])[data-tw-gutters="full"] #platform > .platdark{
  margin-left:0;margin-right:0}
/* Nothing should ever sit hard against the glass, so the deck keeps an internal
   inset for its type even when its box is edge to edge. */
[data-tw-gutters="full"] #platform > .platdark,
[data-tw-gutters="bleed"] #platform > .platdark{
  padding-left:clamp(26px,4vw,56px);padding-right:clamp(26px,4vw,56px)}

[data-tw-gutters="bleed"] body,
[data-tw-gutters="full"] body{overflow-x:clip}

/* ============================================================================
   All nav menus at one width - founder 2026-08-29, with the Scale AI site as
   the reference: "can I see an option where all the drop downs are full width".

   Today the three menus are three different widths. Agents already opens at
   calc(100vw - 380px), Read More at calc(100vw - 440px), and Products is a
   346px column - so hovering across the nav makes the panel jump size twice.
   These options put all three on one width, which is what the reference does.

   The centring uses left:50% + translateX(-50%) rather than inset:0, because
   .dd-menu is absolutely positioned inside a .dd that is only as wide as its
   own nav label; there is no full-width ancestor to stretch against.
   ========================================================================= */
@media (min-width: 861px) {
  /* position:FIXED, not absolute. .dd-menu is absolutely positioned inside a
     .dd that is only as wide as its own nav label, so `left:50%` centres the
     panel on the TRIGGER, not the window - measured, the first pass put the
     Products panel at x=-499 and the Agents panel at x=-418, both hanging off
     the left edge. Fixed anchors all three to the viewport, which is the whole
     point of the option: identical geometry no matter which label you hover.
     top is the 56px nav plus the existing 12px gap. */
  [data-tw-ddwide="wide"] .dd-menu,
  [data-tw-ddwide="full"] .dd-menu{
    position:fixed;top:68px;right:auto;min-width:0;max-width:none;
    transform:translateY(-6px) scale(.985)}
  [data-tw-ddwide="wide"] .dd:hover .dd-menu,
  [data-tw-ddwide="full"] .dd:hover .dd-menu{transform:none}
  /* The two megas carry their own left/width at (0,1,0) - .dd-agents-mega has
     left:0 / min(1440px, 100vw-380) and .dd-read-mega left:-14px / min(1420px,
     100vw-440). Restating them here rather than relying on the generic
     .dd-menu rule alone, because "all three the same width" is the entire ask
     and the first pass measured 1440 / 1418 / 1418 - visibly not the same. */
  [data-tw-ddwide="wide"] .dd-menu,
  [data-tw-ddwide="wide"] .dd-agents-mega,
  [data-tw-ddwide="wide"] .dd-read-mega{
    left:calc(50vw - min(720px, 50vw - 48px));width:min(1440px, calc(100vw - 96px))}
  [data-tw-ddwide="full"] .dd-menu,
  [data-tw-ddwide="full"] .dd-agents-mega,
  [data-tw-ddwide="full"] .dd-read-mega{left:16px;width:calc(100vw - 32px)}
  /* The hover bridge is what lets the pointer travel from the label to the
     panel across the 12px gap. Fixed positioning moves the panel out of the
     trigger's coordinate space, so the bridge has to be re-drawn over the gap
     the panel actually leaves. */
  [data-tw-ddwide="wide"] .dd::after,
  [data-tw-ddwide="full"] .dd::after{content:"";position:absolute;left:0;right:0;top:100%;height:14px}

  /* Products is a 346px column today, so at these widths it needs a row layout
     or the four products stretch into unreadable line lengths. */
  [data-tw-ddwide="wide"] .dd-products,
  [data-tw-ddwide="full"] .dd-products{
    grid-template-columns:repeat(4,1fr);grid-template-rows:auto 1fr;gap:16px}
  [data-tw-ddwide="wide"] .dd-products .ds-h,
  [data-tw-ddwide="full"] .dd-products .ds-h{grid-column:1/-1}

  /* ...unless Products is on `showcaseair`, which the founder chose separately.
     The row layout above would override its 1fr/310/310/1fr tracks and its
     three rows, and because showcaseair places every card explicitly, the two
     bottom cards would land in an implicit fourth row outside the panel.

     Both choices survive here: the frame takes ddwide's full width, the cards
     keep the Showcase size he picked, and the extra width becomes more of the
     air that was the point of the option. That also honours the original
     rationale - showcaseair exists to match the Agents mega, and at this
     setting the mega is full width too, so it follows it there.

     Second pass, founder 2026-08-29: "it's not sized to be the same as the rest
     of them... also the four cards should show right below the header product
     rather than in the middle of the drop down."

     The panel was already the same width as the other two - measured at 1600px
     all three are 1568. What was inconsistent is where the CONTENT starts: the
     Agents and Read More megas begin theirs at x=41, while the centred version
     put the first card at x=484, and that number drifted with the viewport
     (644 / 484 / 404 / 324 at 1920 / 1600 / 1440 / 1280). So it read as a
     different menu even though the frame matched.

     Now the cards are left-anchored under the Products label and all the air
     falls on the right. 173px is the measured offset from the panel's left
     edge (fixed at 16px) to the Products trigger (189px), which is constant at
     every width because the header is left-anchored - verified at 1920, 1600,
     1440 and 1280. It is a literal because the panel is position:fixed and so
     has no access to the trigger's coordinate space; if the nav gains or loses
     an item to the left of Products, this number moves with it.

     (0,3,0) with both html attributes, so it beats the (0,2,0) rules above. */
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="wide"] .dd-products,
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="full"] .dd-products{
    grid-template-columns:minmax(0,310px) minmax(0,310px) 1fr;
    grid-template-rows:auto 1fr 1fr;gap:12px;
    padding:24px 24px 24px 173px}

  /* `wide` centres the panel on the viewport, so its left edge moves with the
     window and the padding has to move with it. max(0px, ...) because above
     ~1830px the centred panel actually starts to the RIGHT of the label (240px
     vs 189px at 1920), and no padding can pull content left of its own panel -
     there the cards sit flush at the panel edge instead. */
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="wide"] .dd-products{
    padding-left:max(0px, calc(189px - 50vw + min(720px, 50vw - 48px)))}

  [data-tw-ddprod="showcaseair"][data-tw-ddwide="wide"] .dd-products .ds-h,
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="full"] .dd-products .ds-h{grid-column:1/3}
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="wide"] .dd-products a:nth-of-type(1),
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="full"] .dd-products a:nth-of-type(1){grid-column:1;grid-row:2}
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="wide"] .dd-products a:nth-of-type(2),
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="full"] .dd-products a:nth-of-type(2){grid-column:2;grid-row:2}
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="wide"] .dd-products a:nth-of-type(3),
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="full"] .dd-products a:nth-of-type(3){grid-column:1;grid-row:3}
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="wide"] .dd-products a:nth-of-type(4),
  [data-tw-ddprod="showcaseair"][data-tw-ddwide="full"] .dd-products a:nth-of-type(4){grid-column:2;grid-row:3}
}

/* ============================================================================
   Nav menu open animation - the founder liked the way the Scale AI menus come
   in. Theirs is a short downward settle with a fade, not a scale pop.

   Consistency rule as everywhere else in this file: the curve and duration are
   the unified reveal token, cubic-bezier(.22,1,.36,1), not a new easing. What
   changes per option is only the distance and whether the children stagger.
   ========================================================================= */
@media (min-width: 861px) {
  /* `settle` - the panel itself slides down and fades; children arrive with it. */
  [data-tw-ddanim="settle"] .dd-menu{
    transform:translateY(-10px);transition:opacity .26s cubic-bezier(.22,1,.36,1),
    transform .34s cubic-bezier(.22,1,.36,1),visibility 0s linear .34s}
  [data-tw-ddanim="settle"] .dd:hover .dd-menu{transform:none;transition-delay:0s}
  [data-tw-ddanim="settle"] .dd-menu>*{opacity:1;transform:none;transition:none}

  /* `cascade` - the panel settles, then its columns arrive a beat behind, which
     is closer to what the reference actually does on a wide mega. */
  [data-tw-ddanim="cascade"] .dd-menu{
    transform:translateY(-10px);transition:opacity .24s cubic-bezier(.22,1,.36,1),
    transform .32s cubic-bezier(.22,1,.36,1),visibility 0s linear .32s}
  [data-tw-ddanim="cascade"] .dd:hover .dd-menu{transform:none;transition-delay:0s}
  [data-tw-ddanim="cascade"] .dd-menu>*{opacity:0;transform:translateY(8px);
    transition:opacity .30s cubic-bezier(.22,1,.36,1) .10s,transform .30s cubic-bezier(.22,1,.36,1) .10s}
  [data-tw-ddanim="cascade"] .dd:hover .dd-menu>*{opacity:1;transform:none}

  /* `instant` - no motion at all, for judging the layouts without it. */
  [data-tw-ddanim="instant"] .dd-menu,
  [data-tw-ddanim="instant"] .dd-menu>*{transition:none;transform:none}
}
@media (prefers-reduced-motion: reduce){
  [data-tw-ddanim] .dd-menu,[data-tw-ddanim] .dd-menu>*{transition:opacity .01s;transform:none}
}

/* ============================================================================
   Scale-style inset for the sections BELOW the deck - founder 2026-08-29, after
   choosing `inset` for the platform deck: "can we extend the scale style inset
   styling to the other sections too".

   Two readings, so two options, because "the styling" could mean the width or
   the card and guessing would cost a round trip:

     insetw   every section takes the DECK'S width - the same near-full-width
              with a thin margin - so the whole page lines up on one edge.
     panels   that, plus the soft rounded surface behind each one, which is what
              the reference page actually does with its sections.

   Both use the deck's own clamp(16px,3.2vw,44px) so the edges cannot drift
   apart from it. !important throughout for the same reason the other gutters
   options need it: #personas and .btn-sec set max-width inline.
   ========================================================================= */
[data-tw-gutters="insetw"] #personas,
[data-tw-gutters="insetw"] .btn-sec,
[data-tw-gutters="insetw"] #customers,
[data-tw-gutters="panels"] #personas,
[data-tw-gutters="panels"] .btn-sec,
[data-tw-gutters="panels"] #customers{
  max-width:none!important;
  margin-left:clamp(16px, 3.2vw, 44px)!important;
  margin-right:clamp(16px, 3.2vw, 44px)!important;
  padding-left:max(clamp(26px,4vw,56px), calc(50vw - var(--maxw) / 2))!important;
  padding-right:max(clamp(26px,4vw,56px), calc(50vw - var(--maxw) / 2))!important;
}
/* #platform is deliberately absent above: `platbleed="inset"` already lands the
   deck on this exact line (44..1556 at 1600px), so re-framing the section around
   it only pushed the deck inward by its own padding. Leave the deck to platbleed
   and the two line up on their own. */

/* `panels` only: the surface. The deck brings its own, so it is excluded. */
[data-tw-gutters="panels"] #personas,
[data-tw-gutters="panels"] .btn-sec,
[data-tw-gutters="panels"] #customers{
  background:var(--bg-2);
  border-radius:28px;
  /* These three set their vertical padding inline (0, s8/0, s8/0), which is
     invisible without a surface and cramped with one. */
  padding-top:clamp(40px,4.4vw,64px)!important;
  padding-bottom:clamp(40px,4.4vw,64px)!important;
  margin-bottom:18px!important;
}
[data-tw-gutters="insetw"] body,
[data-tw-gutters="panels"] body{overflow-x:clip}

/* ============================================================================
   Nav menu animation, second pass - founder 2026-08-29: "the cascade is too
   fast so a human doesn't notice it. I'm also trying to make the animations
   more consistent across the website." Then, reviewing these: "on the products
   dropdown the cards are already there, they should appear on the animation.
   And the animation can be a little bit slower on the products one."

   TWO separate faults, and the first one is why his cards never faded.

   1. A SPECIFICITY COLLISION, not a timing problem. The stagger targets
      `.dd-menu > *` at (0,2,0). The Showcase+air cards carry their own
      `[data-tw-ddprod="showcaseair"] .dd-products a` at (0,2,1), whose
      transition lists border-color, background and transform - and NOT
      opacity. That won, so the cards had no opacity transition at all: they
      snapped from 0 to 1 while the menu's section header above them faded
      correctly. Measured: header dur=0.42s prop=opacity,transform; cards
      dur=0.16s prop=border-color,background,transform.

      Fixed by prefixing `html` so these rules are (0,2,1) too and win on
      source order, and by naming background and border-color in the same
      transition so the cards keep their hover feel rather than snapping there
      instead.

   2. `cascade` itself gave EVERY child the same .10s delay, so nothing
      staggered - one group fade that started late. Left in place unchanged so
      the two can be compared rather than quietly redefined under him.

   The consistency point is real and measurable: the site's own reveals run
   .6s-.95s on cubic-bezier(.22,1,.36,1) (.rv .6s, .rv-up .95s, hero .8s,
   deck .7s) while the nav ran .24-.34s. Same curve, under half the duration.
   `sitetempo` lands on the .7s the rest of the page already uses.

   Timing is carried in three custom properties so the Products menu can be
   slowed on its own - his last note - without a second copy of every rule:
     --ddd  child duration      --dds  per-child step      --dd0  first delay
   Products runs about 1.35x the mega, because it has four big cards rather
   than five columns of links and the same speed reads as hurried on them.

   Stagger is per-child via :nth-child. Eight steps covers every menu here:
   Products has 5 children, the Agents mega 6, Read More 6. Delays apply only
   under :hover so closing stays immediate - a staggered close feels sticky.
   ========================================================================= */
@media (min-width: 861px) {
  /* `readable` - the cascade, actually staggered, and RETUNED 2026-08-29 after
     the founder called it "too slow". Two things were making it feel that way,
     and only one was these numbers:

     The first was that this option was never on. The def edit that was supposed
     to ship it silently failed, so he was watching the base animation - which
     carries a .5s transition-delay and only cancels it for children 1-3. With
     Showcase+air's five children, cards 4 and 5 sat idle for half a second.
     That is the "doesn't get triggered properly" half of the report.

     The second is real: at .42s/.08s the mega settled in .86s and Products, at
     1.35x, took 1.10s. Long for a nav menu you cross with a pointer. Now the
     mega settles in ~.64s and Products in ~.78s, keeping Products a touch
     slower as he asked without the wait. Steps stay at 50-70ms, still inside
     the range where arrivals read as separate rather than as one event. */
  html[data-tw-ddanim="readable"] .dd-menu{--ddd:.36s;--dds:.05s;--dd0:.03s;
    transform:translateY(-10px);transition:opacity .26s cubic-bezier(.22,1,.36,1),
    transform .38s cubic-bezier(.22,1,.36,1),visibility 0s linear .38s}
  html[data-tw-ddanim="readable"] .dd-products{--ddd:.46s;--dds:.07s;--dd0:.04s}

  /* `sitetempo` - the same cascade at the page's own reveal duration. Pick
     this if the goal is the header and the body reading as one system. */
  html[data-tw-ddanim="sitetempo"] .dd-menu{--ddd:.7s;--dds:.09s;--dd0:.08s;
    transform:translateY(-12px);transition:opacity .32s cubic-bezier(.22,1,.36,1),
    transform .46s cubic-bezier(.22,1,.36,1),visibility 0s linear .46s}
  html[data-tw-ddanim="sitetempo"] .dd-products{--ddd:.95s;--dds:.12s;--dd0:.1s}

  /* `sweep` - most pronounced: 110ms steps and 16px of travel, so the columns
     clearly arrive left to right. At 8 children the last lands .77s after the
     first, which is a long time to hold a hover. */
  html[data-tw-ddanim="sweep"] .dd-menu{--ddd:.6s;--dds:.11s;--dd0:.07s;
    transform:translateY(-12px);transition:opacity .3s cubic-bezier(.22,1,.36,1),
    transform .44s cubic-bezier(.22,1,.36,1),visibility 0s linear .44s}
  html[data-tw-ddanim="sweep"] .dd-products{--ddd:.8s;--dds:.15s;--dd0:.09s}

  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu{transform:none;transition-delay:0s}
  /* background and border-color are named here on purpose: this rule has to
     outrank the cards' own transition to get opacity animating, which means it
     replaces it, so their hover treatment has to come along or it snaps. */
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd-menu>*{
    opacity:0;transform:translateY(var(--ddy,10px));
    transition:opacity var(--ddd) cubic-bezier(.22,1,.36,1),transform var(--ddd) cubic-bezier(.22,1,.36,1),
    background .16s cubic-bezier(.22,1,.36,1),border-color .16s cubic-bezier(.22,1,.36,1)}
  html[data-tw-ddanim="sweep"] .dd-menu>*{--ddy:16px}
  html[data-tw-ddanim="sitetempo"] .dd-menu>*{--ddy:12px}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*{opacity:1;transform:none}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*:nth-child(1){transition-delay:calc(var(--dd0) + 0*var(--dds))}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*:nth-child(2){transition-delay:calc(var(--dd0) + 1*var(--dds))}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*:nth-child(3){transition-delay:calc(var(--dd0) + 2*var(--dds))}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*:nth-child(4){transition-delay:calc(var(--dd0) + 3*var(--dds))}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*:nth-child(5){transition-delay:calc(var(--dd0) + 4*var(--dds))}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*:nth-child(6){transition-delay:calc(var(--dd0) + 5*var(--dds))}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*:nth-child(7){transition-delay:calc(var(--dd0) + 6*var(--dds))}
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd:hover .dd-menu>*:nth-child(8){transition-delay:calc(var(--dd0) + 7*var(--dds))}
}
@media (prefers-reduced-motion: reduce){
  html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd-menu,html:is([data-tw-ddanim="readable"],[data-tw-ddanim="sitetempo"],[data-tw-ddanim="sweep"]) .dd-menu>*{
    transition:opacity .01s!important;transform:none!important;transition-delay:0s!important}
}

/* ============================================================================
   Product feature images, third pass - founder 2026-08-29: "I like the ones
   where we remove the side spacing, like the gutter spacing, for example in
   reference wider. I also like the bigger images, but I don't like the soft
   panel we've added in some of them... big looks really good so lets see big
   with no gutters."

   So the two things he likes are separable from the thing he does not: the
   wide measure and the image scale are keepers, the grey field is not. All
   three options below run on `apollowide`'s measure - min(1560px, 100vw-200px)
   - which is what "no gutters" means here. 200px of slack rather than the
   section's own 96px because the apollo field overhangs its column by 7.87cqw
   a side, and at the wider measure that overhang pushed 43px past the viewport
   the first time.
   ========================================================================= */
@media (min-width: 861px) {
  /* Founder 2026-08-30: "reference geometry is good, but the green dot field
     one changes the shape, and it shouldn't change the shape from reference
     geometry - it should just change the background grey to the green dot
     field. Similar feedback for the no panel, bigger image option."

     Right. Both had drifted into being different LAYOUTS rather than different
     FIELDS: they took a wider measure than `apollo`, `nopanel` also moved the
     columns to 1:1.359 and the gap to 18.7% with 88px padding, and both carried
     an earlier gap breakpoint at 1400. So comparing them against the reference
     compared four things at once.
     They now inherit apollo's geometry verbatim (see the apollo block above,
     where they are named alongside it) and differ in exactly one property: the
     field. dots paints the dot motif, nopanel paints nothing. */
  /* bigwide needs more slack than the other two, and it is the .ov-chip layer
     that decides how much, not the picture. The chips are absolutely positioned
     against the stage and sized in cqw with white-space:nowrap, so they scale
     with the image and hang past its right edge - about 16% of stage width. At
     200px of slack the image reached 793px and two chips landed at 1612 and
     1638 against a 1600 viewport, 38px of horizontal scroll. This is the same
     overlay that broke the first Apollo panel, for the same reason.
     320px of slack clears it and still leaves an image far larger than Big:
     778px against Big's 666px.

     The max(1180px, ...) floor matters: with a bare min(), the 320px of slack
     bit below ~1500px and made bigwide SMALLER than plain Big - 525 vs 574 at
     1440, 422 vs 584 at 1280 - which defeats the whole point of the option.
     1180px is the section's normal measure, so it can never lose to Big. */
  [data-tw-imgscale="bigwide"] .sec:has(> .feat2){
    max-width:max(1180px, min(1560px, calc(100vw - 320px)))}

  /* `bigwide` - literally his ask: Big's proportions on the wide measure, no
     field at all. Big is 1fr 1.8fr; the only change is the measure under it. */
  [data-tw-imgscale="bigwide"] .feat2{
    grid-template-columns:1fr 1.8fr;gap:clamp(40px,4.4vw,72px);
    align-items:center;padding:76px 0}
  [data-tw-imgscale="bigwide"] .feat2.flip{grid-template-columns:1.8fr 1fr}
  [data-tw-imgscale="bigwide"] .feat2 .stage.imgstage img{
    border-radius:16px;box-shadow:0 22px 50px rgba(16,50,40,.16)}

  /* `nopanel` - reference geometry, no field behind the image. Geometry comes
     from the apollo block; the only thing here is removing the field. */
  [data-tw-imgscale="nopanel"] .feat2 .stage.imgstage::before{display:none}

  /* `dots` - "replace the soft panel with the green dots". Reference geometry
     kept exactly, including the overhanging field, but the field is the dot
     motif the site already uses instead of a flat fill. That motif is not
     invented here: conductor.html and agent-swarm.html both draw
     radial-gradient(circle, #fff 1.4px, transparent 2px) at 26px on their dark
     stages. Same grid, same dot size, brand green instead of white because
     this one sits on cream. No border and no fill, so it reads as texture
     behind the image rather than another box around it. */
  /* `dots` - reference geometry, the flat grey field swapped for the dot motif.
     Geometry comes from the apollo block; this changes the field's paint only,
     which is the whole ask. */
  [data-tw-imgscale="dots"] .feat2 .stage.imgstage::before{
    background:none;
    background-image:radial-gradient(circle, oklch(0.58 0.14 156 / .30) 1.4px, transparent 2px);
    background-size:26px 26px}

  /* The separate 1400px gap breakpoint these two carried is gone with the rest
     of their bespoke geometry: they follow apollo's 1240 rule now, or they
     would still be a different shape at some widths. */
}

/* ============================================================================
   Feature panel shade - founder 2026-08-29, same message: "an option where the
   soft panel color has a few different options."

   Its own group rather than more imgscale values, because shade multiplies
   against three panelled layouts (`panel`, `apollo`, `apollowide`) and folding
   them together would mean twelve options to read instead of three plus four.
   Same reason the platform deck's card shade is separate from its width.

   Drives one custom property; the three layouts read var(--featfield) with the
   current grey as the fallback, so `grey` is a true no-op and the group is
   inert until touched. Ramp is the same oklch hue as the platform cards, so
   the two decks can be judged as a set.
   ========================================================================= */
[data-tw-featfield="cream"]{--featfield:#FBFAF8}
[data-tw-featfield="tint"]{--featfield:oklch(0.975 0.018 158)}
[data-tw-featfield="sage"]{--featfield:oklch(0.94 0.035 158)}
[data-tw-featfield="mint"]{--featfield:oklch(0.90 0.055 158)}

/* ============================================================================
   Agents mega treatment - founder 2026-08-29: "lets go with carded wide for
   the agents mega", chosen from lab-dropdowns.html.

   The lab mock had its own markup (.ag-sec / .ag-item with a real <img> per
   agent). The shipping mega does not: its links are text only, in Navigation.tsx
   and in every v6 mockup's inline header. Rather than add an <img> to twenty
   links across ~15 mockup files AND the React component, the mascot arrives as
   a background-image on a ::before, keyed off the slug already in each href.
   That works unchanged in both stylesheets and both markups, and reverts by
   flipping one attribute.

   Paths are absolute /v6/... deliberately: shared.css is served from /v6/ so a
   relative url() would resolve there, but the v6chrome.css twin is bundled by
   Vite out of src/app/components/v6/, where the same relative path would 404.

   Width is NOT set here. `carded` is the treatment only; the mega's width comes
   from the ddwide group, which he has already shipped on `full`. Keeping them
   separate is what stops the two from fighting the way ddwide and ddprod did.
   ========================================================================= */
@media (min-width: 861px) {
  [data-tw-ddmega="carded"] .dd-agents-mega{padding:26px 30px 28px;gap:10px 20px}
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col{
    background:var(--bg-2);border-radius:14px;padding:16px 14px 14px}
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col > .ds-h{
    border-bottom-color:transparent;padding:0 8px 8px;margin-bottom:6px;font-size:12.5px}
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a{
    display:grid;grid-template-columns:40px 1fr;align-items:center;gap:13px;
    padding:9px;font-size:17px;font-weight:500;white-space:normal}
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a::before{
    content:"";height:40px;width:40px;
    background-repeat:no-repeat;background-position:bottom center;background-size:contain}
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a:hover{background:var(--dw-tint)}
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="pipeline-building-agent"]::before{background-image:url(/v6/assets/mascots/m-6.png)}  /* Pipeline Building Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="incident-debugging-agent"]::before{background-image:url(/v6/assets/mascots/m-12.png)}  /* Incident Debugging Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="building-quality-monitoring-agent"]::before{background-image:url(/v6/assets/mascots/m-17.png)}  /* Quality Monitoring Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="schema-evolution-agent"]::before{background-image:url(/v6/assets/mascots/m-9.png)}  /* Schema Evolution Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="data-change-review-agent"]::before{background-image:url(/v6/assets/mascots/m-16.png)}  /* Data Change Review Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="data-access-governance-agent"]::before{background-image:url(/v6/assets/mascots/m-8.png)}  /* Data Access & Governance Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="security-agent"]::before{background-image:url(/v6/assets/mascots/m-4.png)}  /* Data Security Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="identity-agent"]::before{background-image:url(/v6/assets/mascots/m-15.png)}  /* Identity Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="cost-savings-cleanup-agent"]::before{background-image:url(/v6/assets/mascots/m-23.png)}  /* Cost Savings & Data Cleanup Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="data-migration-agent"]::before{background-image:url(/v6/assets/mascots/m-7.png)}  /* Data Migration Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="data-science-insights-agent"]::before{background-image:url(/v6/assets/mascots/m-2.png)}  /* Data Science & Insights Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="introducing-ml-agent"]::before{background-image:url(/v6/assets/mascots/m-13.png)}  /* MLOps & Models Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="observability-agent"]::before{background-image:url(/v6/assets/mascots/m-20.png)}  /* Observability Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="streaming-agent"]::before{background-image:url(/v6/assets/mascots/m-19.png)}  /* Real-Time Streaming Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="conductor-agent"]::before{background-image:url(/v6/assets/mascots/m-3.png)}  /* Autonomous Data-Conductor Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="ingest-agent"]::before{background-image:url(/v6/assets/mascots/m-1.png)}  /* Data Ingestion Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="search-research-agent"]::before{background-image:url(/v6/assets/mascots/m-11.png)}  /* Search & Research Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="why-we-built-unified-data-context-agent"]::before{background-image:url(/v6/assets/mascots/m-18.png)}  /* Data Context & Catalog Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="usage-intelligence-agent"]::before{background-image:url(/v6/assets/mascots/m-21.png)}  /* Usage Intelligence Agent */
  [data-tw-ddmega="carded"] .dd-agents-mega > .ds-col a[href*="connectors-agent"]::before{background-image:url(/v6/assets/mascots/m-10.png)}  /* Connectors Agent */
}

/* ============================================================================
   Panelled-section fill + the personas section's fate - founder 2026-08-29,
   reviewing `gutters=panels`: "I like the sizing of the panelled, but I want
   the back panel section for the by the numbers to be the same green as we
   have on the background on the component."

   The green is not a new value: oklch(0.46 0.115 160) is exactly what
   #platform > .platdark already paints, so the By the Numbers panel and the
   platform deck read as the same material rather than two greens a step apart.

   Scoped to [data-tw-gutters="panels"] on purpose - without a panel there is
   no surface to colour, so the group is inert at every other gutters setting
   and cannot surprise him later.

   Text has to flip with the fill. The section's own copy is near-black ink on
   cream, which is unreadable on this green; the .fig-box charts inside carry
   their own white background and keep their dark text untouched.
   ========================================================================= */
/* THE GREEN IS THE .gk CARD'S OWN, not the deck's - founder 2026-08-29 with a
   screenshot: "its not the right shade of green, I want it to be the darker
   green you see on the Dramatically less DataOps background."

   I had used the platform deck's oklch(0.46 0.115 160). Sampling his screenshot
   put the outer panel at #006b41 (that value) and the inner card at #005c35
   falling to #00502f - a gradient, and darker. The first DOM sweep missed it
   because it only read background-COLOR and this is a background-image:
   .gk carries linear-gradient(160deg, oklch(0.42 0.105 158), oklch(0.36 0.085 158)).

   The panel now takes that exact gradient, and .gk's own is switched off, so
   the two do not stack. Two gradients of the same ramp at different box sizes
   resolve their stops differently and would show a seam where the card sits on
   the panel; one gradient across the whole panel gives the single block of dark
   green he is asking for. */
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] #customers{
  background:linear-gradient(160deg, oklch(0.42 0.105 158), oklch(0.36 0.085 158))}
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec .gk,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec .gk{
  background:none}
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] #customers{color:#fff}
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec :is(h2,h3,p,.eyebrow,.lead),
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec :is(h2,h3,p,.eyebrow,.lead),
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] #customers :is(h2,h3,p,.eyebrow,.lead){
  color:#fff}
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec .eyebrow,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec .eyebrow,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] #customers .eyebrow{
  color:oklch(0.90 0.075 158)}
/* THE CHART CARDS KEEP THEIR OWN INK. The white-text flip above swept up every
   p/h2/h3 inside the section, including the figure captions - which live on a
   WHITE card, so they turned white-on-white and vanished. The founder read the
   resulting gap as "a lot of white space at the bottom from the stretch"; it
   was not stretch at all, it was his caption text made invisible. Measured:
   cap-p color rgb(76,90,84) on grey, rgb(255,255,255) on btngreen, card
   background rgb(255,255,255) in both.

   (0,5,0) against the flip's (0,4,0), so it wins without !important. */
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec .fig-box,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec .fig-box,
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec .fig-box :is(h2,h3,p,.eyebrow,.lead),
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec .fig-box :is(h2,h3,p,.eyebrow,.lead){
  color:var(--ink)}
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec .fig-box .cap-p,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec .fig-box .cap-p{
  color:var(--ink-2)}

/* The charts bring their own white card, so they are left alone - only the
   shadow is deepened, a white card on green needs more separation than a white
   card on cream. */
[data-tw-gutters="panels"][data-tw-panelfill="btngreen"] .btn-sec .fig-box,
[data-tw-gutters="panels"][data-tw-panelfill="bothgreen"] .btn-sec .fig-box{
  box-shadow:0 18px 44px rgba(6,26,18,.28)}


/* ============================================================================
   Nav menu height + inset - founder 2026-08-29, after shipping the full-width
   menus: "the nav menu sizing is not consistent across the different drop
   downs. We can fill in air and white space where needed, but it's not
   consistent."

   The width is already consistent - all three measure identically at every
   width, that part landed. What jumps is HEIGHT. Measured at 1600 on `full`:

     Products     198px
     Read More    380px
     Agents mega  565px

   so moving the pointer across the nav grows the panel by 367px and then
   shrinks it again. The content inset differs too, by a smaller 16px: the
   panels start their content at 31 / 47 / 33 because the megas carry heavier
   padding than the simple menu.

   CSS cannot measure a sibling's height, so `match` uses the tallest menu's
   measured natural height as a literal floor. min() against vh so a short
   window never gets a menu taller than the viewport, and align-content:start
   so the extra space lands as air BELOW the content rather than stretching the
   rows - which is what he asked for.
   ========================================================================= */
@media (min-width: 861px) {
  [data-tw-ddheight="match"] .dd-menu,
  [data-tw-ddheight="mid"] .dd-menu{
    padding:30px;align-content:start}
  [data-tw-ddheight="match"] .dd-menu{min-height:min(570px, 64vh)}

  /* `mid` - the middle ground. Rather than hold every menu at the tallest
     one's height, this also brings the Agents mega DOWN by tightening its rows,
     so the common floor can sit near the Read More menu instead of the mega.
     Less dead air under Products, at the cost of a denser mega. */
  [data-tw-ddheight="mid"] .dd-menu{min-height:min(430px, 58vh)}
  [data-tw-ddheight="mid"] .dd-agents-mega > .ds-col a{padding-top:5px;padding-bottom:5px}
  [data-tw-ddheight="mid"] .dd-agents-mega > .ds-col{padding:12px 12px 10px}
  [data-tw-ddheight="mid"] .dd-agents-mega > .ds-col a::before{height:32px;width:32px}
  [data-tw-ddheight="mid"] .dd-agents-mega > .ds-col a{grid-template-columns:32px 1fr;font-size:16px}
}

/* ============================================================================
   By the Numbers chart cards - founder 2026-08-29 with a screenshot: "extra
   whitespace to trim at the bottom of these charts due to the stretch".

   Two separate causes, and the bigger one is not the stretch.

   1. A HARD FLOOR. Both cards carry `min-height:544px` as an INLINE style
      (index.html:542 and :582), so it beats the stylesheet - there is already a
      `.btn-sec .fig-box{min-height:0!important}` rule at line 84, but it only
      applies at the mobile breakpoint. Above that, the charts shrink with the
      column width while the floor does not move, so the gap opens up as the
      window narrows. That is the whitespace he is seeing. !important here
      because nothing else beats an inline declaration.

   2. THE STRETCH, which is real but small. The grid is align-items:stretch, so
      the card with less content matches the taller one. Removing the floor
      leaves only this difference, which is the tidy equal-bottom behaviour worth
      keeping - the alternative is a ragged edge between the two cards.

   Kept as a straight fix rather than an option because a fixed pixel floor
   under a fluid chart is a defect at every width, not a preference.
   ========================================================================= */
@media (min-width: 861px) {
  .btn-sec .fig-box{min-height:0!important}
}


/* =========================================================================
   TWEAK BAR: bottom-CTA width (`data-tw-cta`) + size (`data-tw-ctasize`)
   -------------------------------------------------------------------------
   TWO STYLESHEETS, ONE HEADER/FOOTER. The v6-injected routes (homepage `/`
   and `/product/*`) load THIS file at runtime. The React content routes
   (/blog, /resources, /docs, /book-demo) load a scoped copy at
   src/app/components/v6/v6chrome.css instead. A tweak variant that lives in
   only one of the two silently no-ops on half the site - which is exactly
   what happened here: both groups shipped in v6chrome.css only, so the
   options moved the blog CTA and did nothing on home or the product pages.

   These rules are the TWINS of the `data-tw-cta` / `data-tw-ctasize` block in
   v6chrome.css (search `data-tw-cta` there). Keep the two in sync; the values
   below are copied verbatim so `compact` measures the same on every route.

   SELECTOR MAP (v6chrome scoped copy -> this file's real markup):
     .v6ch-cta-wrap   ->  .wrap:has(.cta)   the CTA's own 1180px container
     .v6ch.cta        ->  .cta              the dark card itself
   `.v6ch-cta-wrap` is a purpose-built wrapper that mirrors `.wrap` exactly
   (max-width 1180px = --maxw); here the CTA already sits in a plain `.wrap`,
   so `:has()` is what narrows it to the one that holds the card. Markup is
   `<div class="wrap"><div class="cta">` on index.html, agent-swarm.html,
   conductor.html, context-wizard.html, incanti-console.html, thesis.html,
   partners.html and pricing.html.

   `html[...]` rather than a bare attribute selector on purpose: the flush
   rule at line 385 (`body.pf-flush-all .wrap:has(.cta)`) scores 0,3,1 and
   would otherwise beat a bare `[data-tw-cta="narrow"] .wrap:has(.cta)`
   (0,3,0) and zero the narrow gutter back out.

   `home` and `large` are the defaults and intentionally have no rules - they
   ARE the shipped homepage geometry.
   ========================================================================= */

/* Edge to edge, card intact: spans the viewport, keeps the rounded corners and
   the shadow, so it still reads as the component rather than as a band. */
html[data-tw-ctawide="on"][data-tw-cta="edge"] .wrap:has(.cta),
html[data-tw-ctawide="on"][data-tw-cta="flush"] .wrap:has(.cta){max-width:none;width:100vw;margin-left:calc(50% - 50vw)}
/* Full bleed goes one step further and squares the corners off. */
html[data-tw-ctawide="on"][data-tw-cta="flush"] .wrap:has(.cta) .cta{border-radius:0}

/* ---------------------------------------------------------------------------
   `refinset` - the Scale AI bottom-CTA geometry, measured off the live page
   rather than off the screenshot. At a 1440 viewport their card sits at
   left 16 / right 1424: 16px of breathing edge each side, on a 24px radius.
   Ours ships at 0 inset with 28px corners, which is why the rounding had
   nothing to read against - a radius only shows if there is ground beside it.

   Founder 2026-08-30: "the bottom CTA needs just a little bit of breathing
   room, look at the border and the whitespace below the component on the Scale
   AI page as a geometry reference."

   SHIPPED EVERYWHERE 2026-08-30: "reference inset for the CTA width on EVERY
   page - home page, blog pages, product pages. I want this to be the same
   component and the same reference inset everywhere."
   The ctawide gate came off these rules in the same change. It existed so that
   widening the port could not silently redesign the homepage off an ambiguous
   sentence; now that one treatment is wanted on every page, the gate would only
   mean a control that has to be ON for the shipped look to appear.

   The air BELOW is ours, not theirs: on their page the card's own margin and
   its parent's bottom padding are both 0, and the space comes from the next
   section. We have a footer directly underneath, so it is added here.
   ------------------------------------------------------------------------ */
html[data-tw-cta="refinset"] .wrap:has(.cta){
  max-width:none;width:calc(100vw - 32px);margin-left:calc(50% - 50vw + 16px);
  margin-bottom:72px}
html[data-tw-cta="refinset"] .wrap:has(.cta) .cta{border-radius:24px}
@media(max-width:640px){
  html[data-tw-cta="refinset"] .wrap:has(.cta){
    width:calc(100vw - 24px);margin-left:calc(50% - 50vw + 12px);margin-bottom:48px}
}

/* Article width: the card lines up with a text column.
   The second selector in each pair is for the HOMEPAGE only. Its card carries
   `id="homeCta"`, and line 383's `body.pf-flush-all .wrap:has(#homeCta)`
   scores 1,2,1 on the strength of that id - it beat the 0,3,0 narrow rule and
   zeroed the gutter back out, so home measured 800px wide where blog and the
   product pages measured 704px. `html ... body` lifts it to 1,2,2. */
html[data-tw-ctawide="on"][data-tw-cta="narrow"] .wrap:has(.cta),
html[data-tw-ctawide="on"][data-tw-cta="narrow"] body .wrap:has(#homeCta){max-width:800px;padding:0 24px}
@media(min-width:768px){
  html[data-tw-ctawide="on"][data-tw-cta="narrow"] .wrap:has(.cta),
  html[data-tw-ctawide="on"][data-tw-cta="narrow"] body .wrap:has(#homeCta){padding:0 48px}
}

/* Compact size: a shorter card with a smaller headline. */
html[data-tw-ctasize="compact"] .wrap:has(.cta) .cta{
  min-height:clamp(260px,26vw,340px);padding:clamp(28px,3.4vw,44px)}
html[data-tw-ctasize="compact"] .wrap:has(.cta) .cta h2{font-size:clamp(27px,3.2vw,40px)}
html[data-tw-ctasize="compact"] .wrap:has(.cta) .cta p{margin:12px 0 20px;font-size:15.5px}
@media(max-width:640px){
  html[data-tw-ctasize="compact"] .wrap:has(.cta) .cta{min-height:290px;padding:24px 20px 26px}
  html[data-tw-ctasize="compact"] .wrap:has(.cta) .cta h2{font-size:27px}
}

/* ===========================================================================
   PERSONA BAND AS THE #customers SIDE COLUMN
   Founder-signed-off 2026-08-30: "Now this is good to go and add to the
   localhost version."

   SOURCE OF TRUTH FOR THE DESIGN: docs/mockups/website/v6/lab-personas.html.
   That page is a variant explorer driven by attributes on <html>
   (data-g / data-e / data-t / data-p / data-s) and it fetches index.html so it
   judges the REAL markup. The founder locked exactly one combination:

       data-g="bleed"  data-e="featurefill"  data-t="current"
       data-p="mix7"   data-s="none"

   Only that combination is ported. The attribute selectors are stripped,
   because the shipping page carries no data-* attributes, and everything is
   scoped to #customers / #personas so none of it can reach another section.
   The unchosen options (hug, rail, numbered, serif, feature, featurebig, wide,
   narrow, tight, airy, bleedwide, flat, frame, masthead, bands, mix1-mix6,
   mix8) are deliberately NOT here - they are dead options, and data-t turned
   out to carry no rules at all. Go back to the lab to explore them again.

   Cascade position is the same as the lab's. There, these rules sit in a
   <style> in <head> that index.html's own inline <style> blocks are appended
   AFTER at runtime; here they sit in shared.css, which the React port attaches
   to <head> while index.html's lifted <style> blocks land inside the injected
   body. Both times the page's own rules come later, so the !importants below
   are doing exactly the work they do in the lab.

   The lab also loads shared.css, so it picks these up too. Its own rules are
   one attribute-selector MORE specific than the stripped versions here, so the
   reference page is still governed by its own <style> and nothing about it
   changes.
   ======================================================================== */

/* --- GEOMETRY -------------------------------------------------------------
   Base = the narrow case: one column, and (see the gate at the bottom) no band
   at all. */
#customers{display:grid;align-items:start;column-gap:var(--gap,14px)}
/* 19vw lands on 358px at the founder's 1886 (the 360 he signed off) and floors
   at 320 at the gate, so the column shrinks with the window instead of holding
   360 and eating the content measure. */
#customers{--col:clamp(320px,19vw,380px)}
#customers{grid-template-columns:minmax(0,1fr)}
#customers > .sec-head,#customers > .partners-wrap,#customers > .qgrid,#personas{grid-column:1}
#personas{margin-top:14px!important}

/* `bleed` - the no-gutter version the founder singled out (2026-08-29: "I like
   no gutter for the Partners & integrations section with persona card section
   beside on the right"). The section keeps a measure for reading but the whole
   band runs to the window edge.

   THE SECOND SELECTOR IN EACH LIST IS NOT DECORATION. The shipping page runs
   html[data-tw-gutters="panels"], the "scale-style inset" treatment above
   (line ~1841), which names #customers and #personas explicitly and sets
   max-width / margins / padding with !important at (1,1,0). This block at
   (1,0,0) lost to it outright: measured on the homepage before the fix,
   #customers had padding-left 353px (max(56, 50vw - 590)) and the persona
   column was a 56px ribbon on the right edge - the exact failure the lab's own
   comment describes, arriving by a different route. The lab page carries no
   data-tw-* attributes at all, so nothing there could have caught it.

   Matching (1,1,0) and winning on source order is the fix. The pair opts these
   two sections out of `panels` and `insetw` specifically - every OTHER section
   the treatment names (#platform, .btn-sec) still gets it, which is what the
   founder chose on 2026-08-29 and is not being revisited here. The panel's own
   surface (background, radius, vertical padding, bottom margin) has to be
   unwound by hand for the same reason: a bleed section that keeps a grey
   rounded panel behind it is neither placement. */
#customers,[data-tw-gutters] #customers{
  max-width:none!important;margin-left:0!important;margin-right:0!important;
  margin-bottom:0!important;
  padding-left:clamp(20px,3vw,44px)!important;padding-right:clamp(20px,3vw,44px)!important;
  /* index.html sets these two inline; `panels` overrides them with !important,
     so restoring them here is what keeps the section's vertical rhythm. */
  padding-top:var(--s8)!important;padding-bottom:0!important;
  background:none!important;border-radius:0!important}

/* The gate: 1240px of content column + 320 side + 14 gap + 88 padding = 1660.
   1240 is where the hero quote still sets on a readable measure. */
@media (min-width:1660px){
  #customers{grid-template-rows:auto auto 1fr;
    /* A FIXED second track, not minmax(0,--col): minmax has a 0 min, so the
       1fr track is offered all the free space first and the side column is
       left with whatever is over. It measured 56px wide at 1886 through 2200 -
       the cards were a 56px ribbon hanging off the right edge with 74px of
       horizontal page scroll behind it. */
    grid-template-columns:minmax(0,1fr) var(--col)}
  #customers > .sec-head{grid-column:1;grid-row:1}
  #customers > .partners-wrap{grid-column:1;grid-row:2}
  #customers > .qgrid{grid-column:1;grid-row:3}
  #personas{grid-column:2;grid-row:2/4;margin-top:0!important}
  /* Fractional ONLY in this range. .partners-wrap ships fixed 1fr/335/335
     tracks (1064px of them) and .qgrid ships 1.15fr/1fr/1fr; in the narrowed
     content column they overflow and slide under the persona column. Outside
     this range the shipped tracks are left alone, because index.html collapses
     .partners-wrap to one column at <=940px and .qgrid at <=860px and an
     unscoped !important would outrank both. */
  #customers .partners-wrap,#customers .qgrid{
    grid-template-columns:repeat(3,minmax(0,1fr))!important}
}

/* --- THE BAND STOPS BEING A SECTION --------------------------------------
   Top-aligned with the partners row. --ptop is .partners-wrap's own top margin,
   which the persona column does not share because it is a sibling CELL rather
   than a child of that wrap. It is measured at runtime (usePersonaBandMetrics
   in v6Runtime.ts, twinned by the inline script at the foot of index.html)
   rather than hardcoded, so it tracks if that margin ever changes.

   THE SIDE PADDING MUST STAY ZERO. #personas is a <section class="sec wrap">
   and .wrap carries 48px of horizontal padding. As a grid CELL that padding
   insets the cards inside their own track: the column measured 1482..1842 while
   the cards sat 1530..1794, 264px wide in a 360px track, so the visible gap
   between the quote wall and the first card was 14px of track gap PLUS 48px of
   padding = 62px. That is the whitespace the founder reported four times, and
   it regressed once already when a `padding:0!important` was rewritten into a
   `padding-top:...`. Written as four LONGHANDS so adding a top value can never
   silently hand the sides back. */
#personas,[data-tw-gutters] #personas{padding-top:var(--ptop,0px)!important;
  padding-left:0!important;padding-right:0!important;padding-bottom:0!important;
  max-width:none!important;margin:0!important;
  background:none!important;border-radius:0!important;
  display:flex!important;flex-direction:column;gap:var(--cardgap,14px);align-self:stretch}
#personas > *{display:flex!important;flex-direction:column;gap:var(--cardgap,14px);height:100%}
#personas .pscard{padding:20px 22px!important;border-radius:16px;
  flex:1 1 0;min-height:132px;justify-content:center}

/* --- `featurefill` + `mix7` ----------------------------------------------
   The size hierarchy, and the palette that outranks its fills.

   In the lab these are two layers: [data-e="featurefill"] paints all three
   cards #0F7A47 with a gradient on the lead card, and then
   html[data-p^="mix"][data-e="featurefill"] - one selector MORE specific -
   repaints every card with --c-personas. Under mix7 that is #4C7058, the light
   green the quote cards ship with today, so the green fill and the gradient are
   both DEAD in the locked combination. They are not reproduced here; the fill
   below is the colour that actually renders. Everything else (radius, padding,
   flex, type) is `featurefill`'s and is ported as-is.

   mix7 is black / green / light green - founder 2026-08-29, "can I see a black
   - green - light green option, using the light green we use for the current
   section." #4C7058 is not one of the Fig. 2 autonomy rungs the other mixes
   draw on, but it is the section's own established green and already carries
   white serif text on the live site, so its contrast is proven rather than
   assumed. */
#personas{align-self:stretch!important}
#personas > *{justify-content:space-between!important;height:100%!important}
#personas .pscard{background:#4C7058!important;border-radius:18px!important;
  padding:22px 24px!important;justify-content:flex-start!important;flex:1 1 auto}
#personas .pscard:nth-child(1){padding:26px 28px 30px!important;min-height:var(--prh,220px)}
/* The lead card's share of the column. grow=1 means all three cards are equal;
   higher makes box 1 longer and boxes 2 and 3 correspondingly shorter, since
   they share what is left. Founder LOCKED 2.45 on 2026-08-29 - measured
   377 / 185px at his width. The slider that dialled it in has been removed. */
#personas .pscard:nth-child(1){flex:var(--leadgrow,2.45) 1 auto}
#personas .psk{color:rgba(255,255,255,.66)!important}
#personas .pst{font-family:var(--serif)!important;color:#fff!important;
  font-weight:400!important;letter-spacing:normal!important;
  font-size:clamp(18px,1.32vw,23px)!important;line-height:1.26!important;margin-top:12px}
#personas .pscard:nth-child(1) .pst{
  font-size:clamp(21px,1.7vw,29px)!important;line-height:1.2!important;margin-top:14px}

/* The rest of the mix7 palette. NAMING .pcount AS WELL AS .partners-row IS NOT
   OPTIONAL: the "85+ integrations" card is a SIBLING of the two .partners-row
   cards, not one of them, so a rule that names only .partners-row leaves it on
   the shipped #0B100D while the other two move to #191F1B - founder
   2026-08-29, "the black isn't consistent across these 3 and should be."
   Measured before the fix: 25,31,27 / 25,31,27 / 11,16,13.

   .plogo ships at #9AA6A0, a grey tuned for the black card. On the green ground
   that drops to roughly 2:1, so it is lifted to white at 78%. */
#customers .partners-row,#customers .pcount{background:#191F1B!important}
#customers .qcard,#customers .quotecard{background:#0F7A47!important}
#customers .plogo{color:rgba(255,255,255,.78)!important}
#customers .plogo.dim{color:rgba(255,255,255,.42)!important}

/* --- LABEL GAPS -----------------------------------------------------------
   Founder 2026-08-29: "no whitespace below Partners & integrations and none
   below For Platform Leads either." Measured before: 66px under the section
   eyebrow (.sec-head margin-bottom 48 + .partners-wrap top margin 18) and 26px
   under .psk. The eyebrow's 48px is the one doing the damage, so it goes to 0
   and the wrap's 18px carries the whole gap - that 18px is deliberate, --ptop
   reads it to keep the persona column on the partners row's line, so zeroing it
   here would knock the alignment out again.

   The serif carries ~12px of leading above its cap height, so 8px of margin
   still read as 20px; 2px lands the label about 14px above its sentence. */
#customers > .sec-head{margin-bottom:0!important}
#personas .psk{margin-bottom:0!important;line-height:1.25!important}
#personas .pst{margin-top:2px!important}
#personas .pscard:nth-child(1) .pst{margin-top:2px!important}

/* --- TYPE THAT SCALES WITH THE CARD --------------------------------------
   Founder 2026-08-29, repeatedly: "the whitespace before the last column still
   persists." The horizontal geometry was exact and had been for several passes
   - at 1920/1728/1512 the tracks are [1fr 360px], the gap is 14px, 0px to
   spare. What he was seeing is VERTICAL: `featurefill` grows the cards to fill
   the column beside a taller quote wall and the copy does not grow with them,
   so the slack lands under each last line (45/32/32 at 1920, 209/149/149 at
   1440).

   Fixed by driving the size off the card's own height instead of pinning it.
   `container-type:size` - not inline-size - is legal here for one reason only:
   the card's height comes from the flex container (flex:1 1 auto inside a
   stretched column), never from its own content, so querying that height cannot
   loop. Clamped so the type can never overflow the card.

   The two factors are tuned separately and by measurement. Card 1 carries three
   lines to the others' two, so it fills first and overflows first; a single
   shared factor either starved cards 2 and 3 or pushed card 1 past its padding.
   Swept 10.5 / 13.5 / 15 / 16cqh against 11 / 11.5 / 12.5 / 13.5 at five
   widths. */
#personas .pscard{padding:26px 26px 28px!important;min-height:0;container-type:size}
#personas .pst{font-size:clamp(17px,18cqh,34px)!important;line-height:1.22!important}
#personas .pscard:nth-child(1) .pst{
  font-size:clamp(20px,13.5cqh,40px)!important;line-height:1.18!important}
#personas .psk{font-size:clamp(9px,3cqh,11.5px)!important}

/* --- BELOW THE GATE -------------------------------------------------------
   Founder 2026-08-30: "I don't want this ever to be at the bottom, I would
   rather it disappear at this size as well."

   The band exists to fill the empty space beside the partners row. Below 1660
   there is no such space, and the earlier answer - reflow it into a full-width
   row under the quote wall - just moved the problem: three more blocks of copy
   at the bottom of a section that has already made its point. The side
   placement is the ONLY placement. If it does not fit beside, it does not
   appear. Do not "restore" a row mode here. */
@media (max-width:1659.98px){
  /* Selector list matches the base block above: that rule sets display:flex
     !important at (1,1,0) to beat `panels`, so a bare `#personas` here at
     (1,0,0) would lose to it and the band would still render below the gate. */
  #personas,[data-tw-gutters] #personas{display:none!important}
}


/* Deck cascade speed - data-tw-platdur. Separate from the motion STYLE so the
   two compose: any of the platanim treatments can be run at any speed instead
   of me minting stagger-slow, both-slow and so on. --platstep scales the
   per-card delays alongside the duration; without it a slower card duration
   just makes the four overlap into a blur. */
/* The duration has to reach the deck whatever motion treatment is selected.
   Scoping it to stagger/both left the control inert on the default `current`
   cascade - measured 0.6s at every speed setting - which is exactly the kind of
   dead control that wastes a review. */
html[data-tw-platdur] .v4deck .panel.rv{
  transition-duration:var(--platdur,.7s)!important}

[data-tw-platdur="slow"]{--platdur:1.1s;--platstep:1.5}
[data-tw-platdur="slower"]{--platdur:1.6s;--platstep:2.1}
[data-tw-platdur="slowest"]{--platdur:2.2s;--platstep:2.8}
/* The widen keyframe is scroll-driven (animation-timeline:view()), so its pace
   comes from the scroll range, not a duration. Widening the range is the
   equivalent of slowing it down. */
[data-tw-platdur="slow"] #platform > .platdark{animation-range:entry 5% entry 95%}
[data-tw-platdur="slower"] #platform > .platdark{animation-range:entry 0% entry 100%}
[data-tw-platdur="slowest"] #platform > .platdark{animation-range:entry 0% exit 15%}


/* ---------------------------------------------------------------------------
   FADE ONLY WINS OVER THE DECK. Founder 2026-08-30: "I liked fade only in terms
   of the animation site-wide, so let's do that. And I feel like that overrules
   the platform deck motion, right, because that should also be fade only."

   Correct, and it has to be enforced rather than assumed: the deck's motion is
   authored separately from the site-wide reveal, so without these rules the
   page would fade everything in EXCEPT the four deck cards, which would still
   slide 20px, and the green panel, which would still widen. That is the exact
   inconsistency the site-wide option exists to remove.

   Both are suppressed: the cards' translate, and the scroll-driven widen
   keyframe. Opacity still animates, so the deck fades with everything else.
   ------------------------------------------------------------------------ */
html[data-tw-anim="fade"] .v4deck .panel.rv{transform:none!important}
html[data-tw-anim="fade"] #platform > .platdark{
  animation:none!important;clip-path:none!important}
