/* Self-hosted so the text paints in the real face the first time. Loaded from
   Google, the browser drew every heading in system-ui first and swapped when
   the files landed — and Outfit is narrower than system-ui, so at 360px the
   headline wrapped to an extra line in the fallback and the whole hero jumped
   up 45px when the swap happened. Same origin, preloaded, no swap window. */
/* One variable file per family instead of one per weight. A static face
   carries a complete alphabet, so four weights of Outfit meant four complete
   copies — 128KB for what is visually one typeface. A variable face carries the
   outlines once plus the data to interpolate between weights: 31KB for the same
   four, and every weight in between for free.
   Deliberately NOT subset. The film's copy is fixed so its own cut can drop the
   glyphs it never renders; this stylesheet serves a form, so the next character
   through it is whatever a visitor types. */
@font-face{font-family:Outfit;font-style:normal;font-weight:100 900;font-display:block;src:url(../assets/fonts/outfit-var-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:Outfit;font-style:normal;font-weight:100 900;font-display:block;src:url(../assets/fonts/outfit-var-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:Figtree;font-style:normal;font-weight:300 900;font-display:block;src:url(../assets/fonts/figtree-var-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:Figtree;font-style:normal;font-weight:300 900;font-display:block;src:url(../assets/fonts/figtree-var-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:Figtree;font-style:italic;font-weight:300 900;font-display:block;src:url(../assets/fonts/figtree-var-italic-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:Figtree;font-style:italic;font-weight:300 900;font-display:block;src:url(../assets/fonts/figtree-var-italic-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:100 800;font-display:optional;src:url(../assets/fonts/jetbrains-mono-var-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:100 800;font-display:optional;src:url(../assets/fonts/jetbrains-mono-var-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}

/* MorphSquads site — design system. Flat Ink + dot texture (no spotlight on web). */
:root{
  --ink:#0A1628;--ink2:#12233F;--navy:#16264A;--graphite:#2A3441;--slate:#5C6B7A;--mist:#DDE4EA;--cloud:#F3F6F8;--paper:#FFFFFF;
  --teal:#1FD1B2;--teal-d:#0A7D6B;--coral:#FF6B4A;--coral-d:#C43D20;--sun:#FFC857;--mint:#7FE0A8;--amber:#FFA257;
  --morph:linear-gradient(90deg,#1FD1B2,#FFC857 55%,#FF6B4A);
  --display:Outfit,system-ui,sans-serif;--body:Figtree,system-ui,sans-serif;--mono:'JetBrains Mono',ui-monospace,monospace;
  --dots:radial-gradient(rgba(148,178,255,.07) 1px,transparent 1.4px);
  /* context-aware text tokens — .light redefines these, and because custom
     properties inherit, inline styles that use them adapt automatically. */
  --fg-strong:#fff;--fg-muted:#93A1B2;--acc:var(--teal);
}
*{box-sizing:border-box} html{scroll-behavior:smooth}
body{margin:0;background:var(--ink);color:#E8EDF4;font-family:var(--body);font-size:16.5px;line-height:1.6;-webkit-font-smoothing:antialiased}
h1,h2,h3,h4{font-family:var(--display);margin:0;letter-spacing:-0.02em;line-height:1.08;text-wrap:balance}
p{margin:0} a{color:inherit;text-decoration:none} img{max-width:100%;display:block}
.wrap{max-width:1200px;margin:0 auto;padding:0 28px}
.eyebrow{font-family:var(--display);font-size:12.5px;font-weight:600;letter-spacing:.18em;text-transform:uppercase;color:var(--teal);text-wrap:balance}
.light .eyebrow{color:var(--teal-d)}
/* Section rhythm was a flat 110px at every width, so the gap between a band's
   top rule and its heading was identical on a 1280px desktop and a 360px
   phone — proportionate on one, ~40% of the viewport width on the other.
   Scale it with the viewport; the desktop value is unchanged (9vw only
   reaches 110px at ~1222px, where the clamp caps it). */
/* The nav is fixed, so an anchor jump parks a section's top at y=0 — under the
   nav, which then covers the first 72px of the band. Offsetting the jump is
   the fix; padding every section to dodge a header is not. */
section{position:relative;padding:clamp(64px,9vw,110px) 0;scroll-margin-top:76px}
.sect-dark{background:var(--ink) var(--dots) 0 0/26px 26px}
.sect-dark2{background:var(--ink2) var(--dots) 0 0/26px 26px}
.light{background:var(--cloud);color:var(--ink);
  --fg-strong:var(--ink);--fg-muted:var(--slate);--acc:var(--teal-d)}
.light .sub{color:var(--graphite)}
h2.sect{font-size:clamp(34px,4.6vw,54px);font-weight:800;letter-spacing:-0.03em}
.sub{font-size:19px;color:#93A1B2;max-width:64ch;margin-top:16px}
.center{text-align:center} .center .sub{margin-inline:auto}
/* reveal utility */
.rv{opacity:0;transform:translateY(34px);transition:opacity .8s cubic-bezier(.2,.7,.2,1),transform .8s cubic-bezier(.2,.7,.2,1)}
.rv.on{opacity:1;transform:none}
.rv.d1{transition-delay:.1s}.rv.d2{transition-delay:.2s}.rv.d3{transition-delay:.3s}.rv.d4{transition-delay:.4s}
@media (prefers-reduced-motion:reduce){.rv{opacity:1;transform:none;transition:none} *{animation-duration:.001s!important}}

/* ---------- nav ---------- */
/* A landmark is only useful if it can be reached: this lets a keyboard or
   screen-reader user jump the nav straight to the content. */
.skip{position:absolute;left:-9999px;top:0;z-index:200;background:var(--teal);
  color:var(--ink);font-family:var(--display);font-weight:700;font-size:14px;
  padding:12px 18px;border-radius:0 0 8px 0}
.skip:focus{left:0}
#nav{position:fixed;inset:0 0 auto 0;z-index:90;transition:background .4s,box-shadow .4s}
#nav.solid{background:rgba(8,15,29,.85);backdrop-filter:blur(14px);box-shadow:0 1px 0 rgba(255,255,255,.06)}
#nav .wrap{display:flex;align-items:center;gap:30px;height:72px}
/* A logo is a lockup, not three independent sizes. The wordmark used to step
   21px -> 16px at a single breakpoint (a visible 24% jump while resizing) while
   the mark stayed 34px and the gap 11px, so the proportions broke exactly where
   the type shrank. Drive all three from one value: the desktop ratios are
   mark = 1.619x and gap = 0.524x the font size, so at the 21px cap the lockup is
   pixel-identical to before (34px mark, 11px gap) and simply scales below it. */
/* <b> defaults to font-weight:bolder, which is RELATIVE: inside the logo (800)
   it resolves to 900, and in body copy (Figtree 400) to 700. We ship neither, so
   the browser was synthesising both — a smeared approximation instead of a cut
   the designer drew, on the wordmark that sits above the fold on every page.
   Pin them to weights that actually exist. Costs nothing to download. */
.logo b.wm{font-weight:800}
b,strong{font-weight:600}
.logo{--lg:21px;display:flex;align-items:center;gap:calc(var(--lg)*.524);font-family:var(--display);font-weight:800;font-size:var(--lg);letter-spacing:-0.02em;color:#fff}
.logo .mk{width:calc(var(--lg)*1.619);height:calc(var(--lg)*1.619);flex:none}.logo .mk svg{width:100%;height:100%;display:block}
.logo .wm span{color:var(--teal)}
/* Only the NAV is space-constrained — it is one fixed-height row that has to
   hold the CTA and the toggle too. The footer has room for the full lockup at
   320px (brand column 264px, lockup ~185px), and its tagline/description/links
   are fixed sizes, so scaling the logo there made the type relationships drift:
   tag/logo swung 0.762 -> 1.067, i.e. the tagline ended up LARGER than the
   wordmark on phones. Keep the footer lockup at its designed size. */
#nav .logo{--lg:clamp(15px,4.05vw,21px)}
#nav a.nl{font-family:var(--display);font-weight:500;font-size:14.5px;color:#B9C4D4;transition:color .25s}
#nav a.nl:hover{color:#fff}
#nav .sp{flex:1}
.btn{display:inline-flex;align-items:center;gap:9px;font-family:var(--display);font-weight:700;font-size:15px;padding:13px 22px;border-radius:8px;background:var(--teal);color:var(--ink);border:0;cursor:pointer;transition:transform .25s,box-shadow .25s;position:relative;overflow:hidden}
.btn:hover{transform:translateY(-2px);box-shadow:0 10px 30px rgba(31,209,178,.35)}
.btn.sm{padding:10px 16px;font-size:13.5px}
.btn.ghost{background:transparent;color:#E8EDF4;border:1.5px solid #2B3B5C}
.btn.ghost:hover{border-color:var(--teal);box-shadow:none;color:#fff}
.light .btn.ghost{color:var(--ink);border-color:var(--mist)}

/* ---------- hero ---------- */
#hero{min-height:100svh;display:grid;place-items:center;padding:clamp(104px,12vw,150px) 0 clamp(60px,7vw,90px);overflow:hidden;background:var(--ink) var(--dots) 0 0/26px 26px}
#hero::after{content:"";position:absolute;inset:0;background:radial-gradient(46% 60% at 74% 50%,rgba(31,209,178,.09),transparent 70%);pointer-events:none}
.hgrid{position:relative;z-index:2;display:grid;grid-template-columns:1.05fr .95fr;gap:70px;align-items:center;max-width:1200px;margin:0 auto;padding:0 28px}
@media(max-width:980px){
  /* gap:70px is a horizontal gutter for the two-column layout; when the grid
     stacks it becomes a vertical gap instead, on top of the ~60px of empty
     space the goo canvas already carries above its artwork. Measured at 850px
     that put 182px between the ticker and the first painted pixel. Only the
     row axis needs relaxing — the column gutter still applies at >=980. */
  .hgrid{grid-template-columns:1fr;text-align:center;justify-items:center;row-gap:10px}
  /* text-align:center centres the TEXT inside each box, not the boxes. .sub is
     capped at 54ch (657px), so once the column grows past that — from ~750px up
     — the paragraph's box stayed pinned to the column's left edge while the
     headline centred on the full column: the body drifted left of the headline,
     133px adrift by 979px. A capped box in a centred layout must be centred. */
  #hero .sub{margin-inline:auto}
}
#hero .kick{font-family:var(--display);font-weight:600;font-size:13.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--teal);text-wrap:balance}
/* Tracked all-caps eats width fast: at .22em the hero eyebrow wrapped to two
   lines on every phone from 320 to 430, orphaning "SQUAD." on its own line.
   Tracking is what consumes the space, not the 12.5px size (already near the
   floor) — so relax the tracking on small screens, and balance the wrap so
   that where it still must break, both lines carry weight. */
@media(max-width:560px){.eyebrow{letter-spacing:.11em}#hero .kick{letter-spacing:.12em}}
#hero h1{font-size:clamp(42px,4.8vw,64px);font-weight:800;letter-spacing:-0.035em;margin:20px 0 18px;color:#fff}
/* The per-word delay rides in as --wd from build_site.py, so the whole entrance
   is declarative: the words are staged in the markup and animated by the sheet,
   with no script in the path that could blank them after they have painted. */
@keyframes wordIn{from{opacity:0;filter:blur(14px)}to{opacity:1;filter:blur(0)}}
#hero h1 .w{display:inline-block;animation:wordIn .7s cubic-bezier(.2,.8,.2,1) both;animation-delay:var(--wd,0s)}
@media (prefers-reduced-motion:reduce){#hero h1 .w{animation-delay:0s}}
#hero .sub{font-size:19px;max-width:54ch;margin-top:0}
/* A .rv slide-up is a SCROLL idiom: it says "this arrived as you came down the
   page". Above the fold nothing arrived — the reader has not scrolled at all —
   and these reveals were driven by the scroll observer, whose first callback
   lands after first paint. On a cold load the network hides that. On a refresh,
   with everything cached and paint instant, you watch the hero sit 34px low and
   climb into place. Being a transform it never counted as layout shift, which
   is why it measured clean. In the two heroes the entrance is a fade. */
#hero .rv,.phero .rv{transform:none}
/* ...and the fade itself must not wait for JS. Every element here carries .rv,
   which is opacity:0 until site.js adds .on, so the kick, sub, CTAs and canvas
   could not appear until a 72KB deferred script had downloaded, parsed and run
   — roughly half a second after the headline was already on screen, and worse
   on a slow device where that script competes for the same core. The fade is
   pure opacity here (see transform:none above), so an animation expresses it
   exactly and needs nothing but the stylesheet. site.js still adds .on; it now
   agrees with where the animation already finished rather than driving it. */
@keyframes heroIn{from{opacity:0}to{opacity:1}}
#hero .rv,.phero .rv{animation:heroIn .8s cubic-bezier(.2,.7,.2,1) both}
#hero .rv.d1,.phero .rv.d1{animation-delay:.1s}
#hero .rv.d2,.phero .rv.d2{animation-delay:.2s}
#hero .rv.d3,.phero .rv.d3{animation-delay:.3s}
#hero .rv.d4,.phero .rv.d4{animation-delay:.4s}
#hero .ctas{display:flex;gap:14px;margin-top:32px;flex-wrap:wrap}
@media(max-width:980px){#hero .ctas{justify-content:center}}
#ticker{margin-top:36px;font-family:var(--mono);font-size:12.5px;color:#5C7290;height:20px;overflow:hidden}
#ticker div{transition:transform .6s,opacity .6s}
/* shapeshifter hero visual: gooey dots morphing ? -> members -> M -> check */
.glyphviz{position:relative;width:min(500px,100%);aspect-ratio:1/1.06;justify-self:center}
.gv-goo{position:absolute;inset:0 0 48px 0;filter:url(#gooA);contain:paint}
/* Set by the hero itself when it measures that this device cannot render the
   goo at a reasonable frame rate. The blobs stop merging; everything else about
   the animation is unchanged. */
.glyphviz.lite .gv-goo{filter:none}
#gvc{width:100%;height:100%;display:block}
.gv-dots{position:absolute;inset:0 0 48px 0;pointer-events:none;filter:blur(.4px)}
#gvc2{width:100%;height:100%;display:block}
.gvov{position:absolute;inset:0 0 48px 0;opacity:0;transition:opacity .45s ease;pointer-events:none}
.gvov.on{opacity:1}
#ovQ span{position:absolute;left:50%;top:49%;transform:translate(-50%,-50%);font-family:var(--display);font-weight:800;font-size:120px;color:var(--ink);line-height:1}
#ovIcons img{position:absolute;border-radius:50%;transform:translate(-50%,-50%) scale(.7);transition:transform .5s cubic-bezier(.34,1.56,.64,1)}
.gvov.on img{transform:translate(-50%,-50%) scale(1)}
.gv-cap{position:absolute;left:0;right:0;bottom:8px;text-align:center;font-family:var(--mono);font-size:12.5px;color:#5C7290;transition:opacity .35s;white-space:nowrap}
.gv-cap b{color:var(--teal);font-weight:500}
@media (prefers-reduced-motion:reduce){.gv-goo{filter:url(#gooA)}}
.scrollcue{position:absolute;left:50%;bottom:26px;transform:translateX(-50%);width:2px;height:56px;background:linear-gradient(180deg,transparent,var(--teal));border-radius:2px;animation:cue 2s ease-in-out infinite}
@keyframes cue{0%,100%{opacity:.3;transform:translateX(-50%) scaleY(.6)}50%{opacity:1;transform:translateX(-50%) scaleY(1)}}

/* ---------- problem (sticky failing chat) ---------- */
#problem{padding:0}
#problem .py{height:320vh;position:relative}   /* 6 beats; 420vh gave each ~480px of scroll */
#problem .stick{position:sticky;top:0;height:100svh;display:grid;align-items:center;overflow:hidden;padding:clamp(78px,9svh,92px) 0 clamp(20px,3svh,28px)}
#problem .cols{display:grid;grid-template-columns:1fr 1.05fr;gap:56px;align-items:center}
/* Deliberately a smaller scale than the site-wide h2 (clamp(34,4.6vw,54)): this
   heading sits in one column of a PINNED stage whose content already needs
   ~747px against the viewport height, so a larger heading pushes the stage
   into clipping. Divergence is intentional — not drift. */
#problem h2.sect{font-size:clamp(28px,4.4vw,40px)}   /* the pinned stage is height-bound */
@media(max-width:900px){#problem .cols{grid-template-columns:1fr}}
/* ── the failing chat ──────────────────────────────────────────────────────
   This used to be a list of six criticisms beside a nine-message chat, each on
   its own cadence. Nothing said which point belonged to which exchange, and on
   a phone the columns stacked: you read six abstract failures, scrolled past a
   gap, then read a conversation. The two could never be on screen together.

   It is now an annotated transcript. Each beat is one grid ROW holding the
   exchange and the verdict it earns, so they align by construction rather than
   by arithmetic — and when the row collapses on a phone the verdict simply
   falls under its own exchange. A spine with a marker per beat makes the pair
   explicit, and the verdict lands AFTER the failure it names: you watch the bot
   fail, then you are told what it just did. */
.phead{max-width:820px;margin:0 auto 40px;text-align:center}
#problem .phead .sub{margin-inline:auto}

/* The chat stays a real widget on a real screen — that frame is what makes it
   read as a PRODUCT failing rather than as generic message bubbles. What changed
   is that the verdicts now sit in a column beside it, aligned to the exchange
   each one judges, instead of stacking as an unlinked list.

   The frame is built from per-beat segments that abut with no gap, so the white
   surface is continuous on desktop while every beat is still its own grid row —
   which is what lets the row collapse on a phone and drop the verdict directly
   under its own exchange. */
.tscript{max-width:1020px;margin:0 auto;transition:filter .6s ease}
.device{display:contents}
.tscript.dead{filter:grayscale(.5) brightness(.84)}

.tshead,.beat{display:grid;grid-template-columns:minmax(0,1fr) 34px minmax(0,.8fr);
  align-items:stretch}
.tshead .bar{grid-column:1;background:#59606B;color:#fff;padding:13px 16px;
  border-radius:14px 14px 0 0;font-family:var(--display);font-weight:700;font-size:14.5px;
  box-shadow:0 -2px 0 rgba(0,0,0,.15)}
.tshead .bar small{display:block;font-weight:400;font-family:var(--body);font-size:11.5px;color:#C9CDD3}

/* Pinned, the screen is a WINDOW: the transcript scrolls inside it as the page
   scrolls, so beats arrive one at a time the way they always did — but because a
   beat is still one row, its exchange and its verdict scroll together and cannot
   drift apart. Unpinned (below), the window opens to full height and the whole
   transcript simply lists.
   Scoped to #problem: the persona section's chat window carries class="log" too,
   and a bare .log gave it this section's fixed height and its white backing
   panel — a 336px slab of #fff laid over the left of that transcript on any
   screen wide enough to miss the mobile override that hid it. */
#problem .log{position:relative;height:clamp(236px,42svh,392px);overflow:hidden}
/* The composer: a chat window without one reads as a cropped screenshot, and the
   space beneath a short conversation has nothing to sit on. It matches the white
   panel's width so it belongs to the screen, not the margin. */
.cinput{position:absolute;left:0;bottom:0;z-index:2;height:46px;
  width:calc((100% - 34px) * 0.5556);
  display:flex;align-items:center;gap:9px;padding:0 13px;
  background:#fff;border-top:1px solid #ECEEF2}
.cinput span{flex:1;min-width:0;font-size:12.5px;color:#AAB2BF;background:#F2F4F7;
  border-radius:999px;padding:8px 13px;line-height:1.25;
  /* A flex item defaults to min-width:auto, so it refuses to shrink below its
     text and the placeholder wrapped to two lines inside a fixed 44px bar —
     the field then overflowed the composer and took the alignment with it.
     It truncates now instead of growing. */
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cinput i{width:26px;height:26px;flex:none;border-radius:50%;background:#D8DDE5;
  position:relative}
.cinput i::after{content:"";position:absolute;left:9px;top:8px;
  border-left:7px solid #fff;border-top:5px solid transparent;border-bottom:5px solid transparent}

#problem .log::before{content:"";position:absolute;left:0;top:0;bottom:0;z-index:0;background:#fff;
  width:calc((100% - 34px) * 0.5556);
  box-shadow:0 26px 60px rgba(0,0,0,.45)}
.loginner{display:grid;will-change:transform;contain:layout paint}

.beat{position:relative}
.beat .chat{grid-column:1;padding:11px 14px;display:flex;flex-direction:column;justify-content:center;gap:8px;position:relative;z-index:1}
.beat:last-child .chat{padding-bottom:16px}
.beat .vd{grid-column:3;align-self:center}

/* spine between the screen and its margin notes */
.beat::before{content:"";position:absolute;top:0;bottom:0;
  left:calc((100% - 34px) * 0.5556 + 17px);width:1px;background:#22324F}
.beat:first-child::before{top:50%}
.beat:last-child::before{bottom:50%}
.beat::after{content:"";position:absolute;top:50%;width:9px;height:9px;border-radius:50%;
  left:calc((100% - 34px) * 0.5556 + 17px);transform:translate(-50%,-50%);
  background:var(--ink);border:1px solid #22324F;
  transition:background .45s ease,border-color .45s ease,box-shadow .45s ease}
.beat.on::after{background:var(--coral);border-color:var(--coral);
  box-shadow:0 0 0 5px rgba(255,107,74,.16)}

.bmsg{max-width:86%;padding:9px 13px;border-radius:13px;font-size:12.8px;line-height:1.45;
  opacity:0;transform:translateY(9px);width:fit-content;
  transition:opacity .45s ease,transform .45s cubic-bezier(.2,.8,.2,1)}
.bmsg.on{opacity:1;transform:none}
.bmsg.u{margin-left:auto;background:#3E4653;color:#fff;border-bottom-right-radius:4px}
.bmsg.b{margin-right:auto;background:#F0F1F4;color:#4A5260;border-bottom-left-radius:4px}
.bmsg.rage{background:var(--coral);color:#fff;font-weight:600;letter-spacing:.04em;margin-left:auto}

/* the verdict strikes rather than fades */
.vd{position:relative;padding:13px 16px 13px 19px;border-radius:11px;margin-left:6px;
  background:rgba(12,25,48,.5);border:1px solid #22324F;
  opacity:0;transform:translateX(-10px) scale(.985);
  transition:opacity .5s ease,transform .55s cubic-bezier(.16,1,.3,1),
             border-color .5s ease,background .5s ease}
.vd::before{content:"";position:absolute;left:0;top:12px;bottom:12px;width:2px;border-radius:2px;
  background:var(--coral);transform:scaleY(0);transform-origin:top;
  transition:transform .6s cubic-bezier(.16,1,.3,1) .1s}
.beat.on .vd{opacity:1;transform:none;border-color:rgba(255,107,74,.34);background:rgba(26,20,30,.6)}
.beat.seen .vd{opacity:.4;transform:none;border-color:#22324F}
.beat.seen .vd::before{transform:scaleY(1);background:#3B4A66}
.beat.seen::after{background:#3B4A66;border-color:#3B4A66}
.beat.on .vd::before{transform:scaleY(1)}
.vdn{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;color:var(--coral);
  display:block;margin-bottom:5px;opacity:.75}
.vd b{font-family:var(--display);color:#fff;display:block;font-size:16px;margin-bottom:3px;letter-spacing:-.01em}
.vd p{font-size:13px;color:#93A1B2;line-height:1.5}
.vdrail{display:none}


@media(max-width:560px){
  .bmsg{max-width:90%;font-size:12.5px}
  .vd b{font-size:15px}
}

/* ---------- squad bench ---------- */
#squad .bench{display:grid;grid-template-columns:repeat(6,1fr);gap:18px;margin-top:56px}
@media(max-width:1000px){#squad .bench{grid-template-columns:repeat(3,1fr)}}
@media(max-width:560px){#squad .bench{grid-template-columns:repeat(2,1fr)}}
.mem{position:relative;display:grid;justify-items:center;gap:14px;padding:26px 14px 22px;border:1px solid #22324F;border-radius:14px;background:rgba(12,25,48,.55);transition:transform .35s,border-color .35s,box-shadow .35s;cursor:default}
.mem:hover{transform:translateY(-8px);border-color:var(--teal);box-shadow:0 18px 44px rgba(31,209,178,.16)}
.mem img{width:86px;height:86px;border-radius:50%;transition:transform .35s}
.mem:hover img{transform:scale(1.1)}
.mem b{font-family:var(--display);font-size:15.5px;color:#fff;text-align:center;letter-spacing:-0.01em}
.mem p{font-size:13px;color:#93A1B2;text-align:center;line-height:1.45}
#squad .note{margin-top:26px;text-align:center;color:#7C8BA3;font-size:15px}
#squad .note b{color:var(--teal)}

/* ---------- rail (how it works) ---------- */
#how{padding:0}
#how .py{height:340vh;position:relative}
#how .stick{position:sticky;top:0;height:100svh;display:grid;place-items:center;overflow:hidden}
#how .head{text-align:center;margin-bottom:70px}
.railwrap{position:relative;width:min(1150px,92vw);max-width:100%}  /* 92vw ignores .wrap's 28px padding, so it ran 30px past the content box
     on a 320px phone; the sticky stage's overflow:hidden used to hide that */
#rail{position:absolute;left:8%;right:8%;top:56px;height:4px;border-radius:2px;
  background:var(--morph);transform-origin:left;transform:scaleX(var(--rp,0));
  box-shadow:0 0 18px rgba(255,200,87,.3)}
.hsteps{display:grid;grid-template-columns:repeat(4,1fr);gap:24px}
.hstep{display:grid;justify-items:center;text-align:center;opacity:0;transform:translateY(34px) scale(.85);transition:all .6s cubic-bezier(.34,1.56,.64,1)}
.hstep.on{opacity:1;transform:none}
.hstep .ring{width:112px;height:112px;border-radius:50%;border:2.5px solid;display:grid;place-items:center;background:var(--ink);position:relative;z-index:2}
.hstep .ring img{width:94px;height:94px;border-radius:50%}
.hstep.t .ring{border-color:var(--teal);box-shadow:0 0 0 7px var(--ink),0 0 34px rgba(31,209,178,.4)}
.hstep.m .ring{border-color:var(--mint);box-shadow:0 0 0 7px var(--ink),0 0 34px rgba(127,224,168,.35)}
.hstep.a .ring{border-color:var(--amber);box-shadow:0 0 0 7px var(--ink),0 0 34px rgba(255,162,87,.35)}
.hstep.c .ring{border-color:var(--coral);box-shadow:0 0 0 7px var(--ink),0 0 34px rgba(255,107,74,.4)}
.hstep .n{font-family:var(--mono);font-size:12px;letter-spacing:.2em;margin-bottom:10px}
.hstep.t .n{color:var(--teal)}.hstep.m .n{color:var(--mint)}.hstep.a .n{color:var(--amber)}.hstep.c .n{color:var(--coral)}
.hstep b{display:block;font-family:var(--display);font-weight:700;font-size:21px;color:#fff;margin:16px 0 7px;letter-spacing:-0.015em}
.hstep p{font-size:14.5px;color:#93A1B2;line-height:1.5;max-width:26ch}

/* ---------- persona ---------- */
#persona .pwrap{display:grid;grid-template-columns:1.15fr .85fr;gap:34px;margin-top:54px;align-items:start}
@media(max-width:960px){#persona .pwrap{grid-template-columns:1fr}}
.pchat{background:#fff;border-radius:16px;color:var(--graphite);overflow:hidden;box-shadow:0 30px 80px rgba(0,0,0,.45)}
.pchat .hd{background:var(--ink);color:#fff;padding:14px 18px;display:flex;gap:11px;align-items:center;font-family:var(--display);font-weight:700;font-size:15px}
.pchat .hd .mk{width:26px;height:26px}.pchat .hd .mk svg{width:100%;height:100%}
.pchat .hd .hdby{font-weight:500;font-size:11.5px;color:var(--fg-muted);margin-left:auto}
.pchat .log{padding:20px;display:grid;gap:12px}
.pmsg{max-width:86%;padding:12px 15px;border-radius:14px;font-size:14.5px;line-height:1.55}
.pmsg.u{justify-self:end;background:var(--navy);color:#fff;border-bottom-right-radius:4px}
.pmsg.b{justify-self:start;background:#F2F5F8;border-bottom-left-radius:4px;color:var(--graphite);border:1px solid #E7EAF0}
#preply.morphing{animation:replyMorph .5s ease}
@keyframes replyMorph{0%{opacity:1;filter:blur(0)}45%{opacity:.15;filter:blur(6px)}100%{opacity:1;filter:blur(0)}}
.ptabs{display:flex;gap:10px;flex-wrap:wrap;margin-top:26px}
.ptab{font-family:var(--display);font-weight:600;font-size:14px;padding:10px 18px;border-radius:999px;border:1.5px solid #2B3B5C;color:#B9C4D4;background:transparent;cursor:pointer;transition:all .3s}
.ptab:hover{border-color:var(--teal);color:#fff}
.ptab.act{background:var(--teal);border-color:var(--teal);color:var(--ink)}
.pconsole{background:#040D1C;border:1px solid rgba(31,209,178,.3);border-radius:14px;padding:20px 22px;background-image:linear-gradient(rgba(31,209,178,.045) 1px,transparent 1px),linear-gradient(90deg,rgba(31,209,178,.045) 1px,transparent 1px);background-size:24px 24px}
.pconsole .cl{font-family:var(--mono);font-size:10.5px;letter-spacing:.18em;color:#7C8BA3;text-transform:uppercase;margin-bottom:10px}
.pchips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:18px}
.pchip{font-family:var(--mono);font-size:11.5px;padding:5px 11px;border-radius:999px;border:1px solid rgba(31,209,178,.35);color:var(--teal);background:rgba(31,209,178,.08)}
.pchip.p{color:#C9D3DF;border-color:rgba(255,255,255,.22);background:rgba(255,255,255,.05)}
.pchip.p::before{content:"☺ ";opacity:.7}
.plead{font-family:var(--display);font-weight:700;font-size:14px;color:#fff;border-top:1px solid #14243F;padding-top:14px}
.plead em{font-style:normal;color:var(--teal)}
#persona .big-line{margin-top:44px;text-align:center;font-family:var(--display);font-weight:700;font-size:clamp(20px,2.6vw,28px);color:#fff;letter-spacing:-0.02em}

/* ---------- theatre ---------- */
#film{background:#050D1B var(--dots) 0 0/26px 26px}
.theatre{position:relative;border-radius:18px;overflow:hidden;margin-top:52px;border:1px solid #1D2C4C;box-shadow:0 40px 110px rgba(0,0,0,.6);aspect-ratio:16/9;background:#000}
/* The film is composed at 1600x900 and scaled whole, so stage scale is simply
   theatreWidth/1600 — at 390px that is 0.21, which renders the film's own 14px
   labels at under 3px. Reclaiming the wrap's 28px gutters is the free part of
   the answer (+17% on a phone); fullscreen is the rest. Negative margin rather
   than 100vw so it can never introduce a horizontal scrollbar. */
@media(max-width:640px){
  .theatre{margin-inline:-28px;border-radius:0;border-left:0;border-right:0}
}
/* the portrait cut is 9:16, so the box must be too — otherwise a vertical film
   letterboxes inside a 16:9 frame down to nothing */
.theatre.vertical{aspect-ratio:9/16}
/* In fullscreen the inline height fitTheatre sets must give way. */
#theatre:fullscreen,#theatre:-webkit-full-screen{
  width:100%;height:100%;max-height:none;margin:0;border:0;border-radius:0;aspect-ratio:auto}
#theatre:fullscreen iframe,#theatre:-webkit-full-screen iframe{width:100%;height:100%}
.theatre img{width:100%;height:100%;object-fit:cover;opacity:.85;transition:transform .8s,opacity .5s}
.theatre:hover img{transform:scale(1.02)}
.theatre iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
.playbtn{position:absolute;inset:0;display:grid;place-items:center;cursor:pointer;background:radial-gradient(60% 60% at 50% 50%,rgba(4,10,20,.15),rgba(4,10,20,.55));border:0;width:100%}
.playbtn .pring{width:110px;height:110px;border-radius:50%;background:rgba(10,22,40,.75);backdrop-filter:blur(8px);border:2.5px solid var(--teal);display:grid;place-items:center;color:var(--teal);font-size:34px;padding-left:6px;position:relative;transition:transform .3s}
.playbtn:hover .pring{transform:scale(1.08)}
.playbtn .pring::after{content:"";position:absolute;inset:-12px;border-radius:50%;border:2px solid rgba(31,209,178,.4);animation:cue2 1.8s ease-out infinite}
@keyframes cue2{0%{transform:scale(.9);opacity:.9}100%{transform:scale(1.25);opacity:0}}
.chapters{display:flex;gap:9px;flex-wrap:wrap;justify-content:center;margin-top:22px}
.chap{font-family:var(--mono);font-size:12px;padding:7px 13px;border-radius:999px;border:1px solid #22324F;color:#93A1B2;cursor:pointer;background:transparent;transition:all .25s}
.chap:hover{border-color:var(--teal);color:var(--teal)}

/* ---------- scenarios (light) ---------- */
#scen .cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:18px;margin-top:54px}
/* No fixed height: the faces are stacked in one grid cell, so the card is as
   tall as its tallest face and no taller. The old 330px was tuned for the
   desktop column width and left ~100px of dead space on a phone, where the
   card is much wider and the quote no longer wraps. */
.scard{perspective:1200px;cursor:pointer}
.scard .in{position:relative;width:100%;height:100%;display:grid;
  transition:transform .7s cubic-bezier(.2,.7,.2,1);transform-style:preserve-3d}
.scard:hover .in,.scard.flip .in{transform:rotateY(180deg)}
.sface{grid-area:1/1;border-radius:16px;padding:28px;backface-visibility:hidden;
  display:flex;flex-direction:column;justify-content:space-between;gap:18px}
.sface.f{background:#fff;border:1px solid var(--mist);box-shadow:0 14px 40px rgba(10,22,40,.08)}
.sface.f .q{font-family:var(--display);font-weight:800;font-size:clamp(22px,2.2vw,27px);letter-spacing:-0.025em;color:var(--ink);line-height:1.15}
.sface.f .who{font-family:var(--mono);font-size:11px;letter-spacing:.16em;color:var(--slate);text-transform:uppercase}
.sface.b{background:var(--ink);color:#D6DEE7;transform:rotateY(180deg);border:1px solid var(--navy);
  /* a dark surface nested inside a .light section: the inherited text tokens
     are the on-light values, which rendered <b> copy in Ink on Ink (the word
     "no" vanished). Any element that flips the surface back must re-declare. */
  --fg-strong:#fff;--fg-muted:#93A1B2;--acc:var(--teal)}
.sface.b p{font-size:14.5px;line-height:1.55;color:#B9C4D4}
.sface.b .gchip{align-self:flex-start;font-family:var(--mono);font-size:11.5px;padding:5px 12px;border-radius:999px;background:var(--mint);color:var(--ink);font-weight:500}
.sface.b i{color:#fff;font-style:normal;font-family:var(--display);font-weight:600}

/* ---------- outcomes ---------- */
#out .herorow{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:54px}
#out .subrow{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:16px}
@media(max-width:960px){#out .herorow,#out .subrow{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* two columns of ~100px on a phone wrapped the captions to one or two words
   per line — unreadable. Stat cards go single-column below 560px. */
@media(max-width:560px){#out .herorow,#out .subrow{grid-template-columns:minmax(0,1fr)}
  #out .met{text-align:left}}
.met{background:rgba(15,29,55,.7);border:1px solid #22324F;border-radius:14px;padding:26px 24px}
.met b{font-family:var(--mono);font-size:46px;font-weight:500;color:var(--teal);letter-spacing:-0.02em;display:block}
.met b i{font-style:normal;font-size:.55em;opacity:.75}
.met.s b{color:var(--sun)}.met.c b{color:var(--coral)}
.met span{display:block;font-size:15px;color:#A9B6C6;margin-top:9px;line-height:1.45}
.met em{display:block;font-family:var(--mono);font-style:normal;font-size:11.5px;color:var(--mint);margin-top:10px}
.subrow .met b{font-size:32px}
.auditpill{display:flex;align-items:center;gap:10px;justify-content:center;margin:26px auto 0;width:fit-content;font-family:var(--mono);font-size:12.5px;color:var(--mint);background:rgba(127,224,168,.07);border:1px solid rgba(127,224,168,.3);border-radius:999px;padding:10px 22px}
.auditpill i{width:9px;height:9px;border-radius:50%;background:var(--mint);box-shadow:0 0 10px var(--mint)}
#out .fine{margin-top:12px;text-align:center;font-family:var(--mono);font-size:11px;color:#5C7290}
#out .usp{margin-top:60px;text-align:center;font-family:var(--display);font-weight:800;font-size:clamp(24px,3.4vw,40px);letter-spacing:-0.03em;color:#fff}
#out .usp .hl{color:var(--teal)}

/* ---------- domains ---------- */
#dom{overflow:hidden}
.mq{display:flex;gap:16px;margin-top:20px;will-change:transform}
.mq.r2{margin-top:16px}
.dchip{flex:none;display:flex;align-items:center;gap:13px;background:rgba(12,25,48,.65);border:1px solid #22324F;border-radius:999px;padding:11px 24px 11px 12px}
.dchip img{width:44px;height:44px;border-radius:50%}
.dchip b{font-family:var(--display);font-size:15.5px;color:#fff;white-space:nowrap}
.dchip small{display:block;font-size:12px;color:#7C8BA3;white-space:nowrap}

/* ---------- guardrails ---------- */
#guard .cols{display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:center;margin-top:20px}
@media(max-width:960px){#guard .cols{grid-template-columns:1fr}}
.rails{position:relative;height:380px;border-radius:16px;overflow:hidden;background:#040D1C;border:1px solid rgba(31,209,178,.25)}
.rails::before,.rails::after{content:"";position:absolute;top:0;bottom:0;width:3px;background:linear-gradient(180deg,transparent,var(--teal) 15% 85%,transparent);box-shadow:0 0 14px rgba(31,209,178,.5)}
.rails::before{left:22%}.rails::after{right:22%}
.flowdot{position:absolute;width:9px;height:9px;border-radius:50%;background:var(--teal);opacity:.85;box-shadow:0 0 10px var(--teal)}
.glines{position:absolute;left:26%;right:26%;top:20px;display:grid;gap:11px;font-family:var(--mono);font-size:10.5px;color:#5C7290}
.gl{display:grid;gap:16px}
.gitem{display:grid;grid-template-columns:26px 1fr;gap:14px}
.gitem .gi{color:var(--teal);font-size:15px}
.gitem b{font-family:var(--display);color:#fff;display:block;font-size:17px;margin-bottom:3px}
.gitem p{font-size:14.5px;color:#93A1B2}

/* ---------- backstage pass (light) ---------- */
#pass .cols{display:grid;grid-template-columns:1fr .8fr;gap:70px;align-items:center;margin-top:10px}
@media(max-width:960px){#pass .cols{grid-template-columns:1fr}}
#pass ul{margin:22px 0 0;padding:0;list-style:none;display:grid;gap:13px}
#pass li{display:grid;grid-template-columns:24px 1fr;gap:12px;font-size:16px;color:var(--graphite)}
#pass li i{color:var(--teal-d);font-style:normal;font-weight:600}
#pass li b{color:var(--ink)}
/* An <img> width/height attribute becomes a presentational hint — a real CSS
   height. Setting only `width` in CSS overrides the width hint and leaves the
   height hint standing, so the image renders squashed at any size below its
   natural width. Neutralise it once; any rule that sets a real height wins on
   specificity. */
img{max-width:100%;height:auto}

.ticketwrap{perspective:1100px;display:grid;place-items:center}
#ticket3d{width:min(330px,80%);height:auto;border-radius:18px;transition:transform .2s ease-out;box-shadow:0 40px 90px rgba(10,22,40,.35);will-change:transform}
.shakedown{margin-top:26px;padding:18px 22px;border-left:3px solid var(--teal);background:#fff;border-radius:0 10px 10px 0;font-size:15px;color:var(--graphite)}

/* ---------- form ---------- */
#interest{background:var(--ink) var(--dots) 0 0/26px 26px}
.formcard{max-width:760px;margin:50px auto 0;background:rgba(12,25,48,.75);border:1px solid #22324F;border-radius:18px;padding:40px;backdrop-filter:blur(6px)}
.frow{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
@media(max-width:640px){.frow{grid-template-columns:1fr}}
label{display:block;font-family:var(--display);font-weight:600;font-size:13px;color:#B9C4D4;margin-bottom:7px;letter-spacing:.02em}
input,textarea,select{width:100%;background:#0A1830;border:1.5px solid #22324F;border-radius:9px;color:#fff;font-family:var(--body);font-size:15px;padding:13px 15px;transition:border-color .25s;outline:none}
input:focus,textarea:focus,select:focus{border-color:var(--teal)}
/* The browser's own "Please fill in this field" bubble is unstyled, shows one
   field at a time and disappears after a few seconds — easy to miss on a phone.
   Same constraints, our own message, and it stays until the field is fixed. */
#lead .bad input,#lead .bad select,#lead .bad textarea{border-color:var(--coral)}
#lead .bad input:focus,#lead .bad textarea:focus{border-color:var(--coral)}
.ferr{margin:7px 0 0;font-family:var(--display);font-weight:600;font-size:12.5px;
  color:#FF8A6E;display:flex;align-items:center;gap:6px}
.ferr::before{content:"!";display:inline-grid;place-items:center;width:15px;height:15px;
  border-radius:50%;background:var(--coral);color:#fff;font-size:10.5px;flex:none}
textarea{min-height:110px;resize:vertical}
.chips{display:flex;flex-wrap:wrap;gap:9px}
.chip{font-family:var(--display);font-weight:600;font-size:13.5px;padding:9px 16px;border-radius:999px;border:1.5px solid #2B3B5C;color:#B9C4D4;cursor:pointer;transition:all .25s;user-select:none}
.chip.act{background:var(--teal);border-color:var(--teal);color:var(--ink)}
.formfoot{display:flex;align-items:center;justify-content:space-between;gap:18px;margin-top:24px;flex-wrap:wrap}
.formfoot small{color:#5C7290;font-size:13px}
#form-success{text-align:center;padding:40px 20px;display:none}
#form-success .dots{display:flex;gap:10px;justify-content:center;margin-bottom:22px}
#form-success .dots i{width:16px;height:16px;border-radius:50%;animation:fuse 1.6s ease-in-out infinite}
#form-success .dots i:nth-child(1){background:var(--teal)}#form-success .dots i:nth-child(2){background:var(--mint);animation-delay:.15s}#form-success .dots i:nth-child(3){background:var(--amber);animation-delay:.3s}#form-success .dots i:nth-child(4){background:var(--coral);animation-delay:.45s}
@keyframes fuse{0%,100%{transform:translateX(0) scale(1)}50%{transform:translateX(-4px) scale(1.25)}}
#form-success h3{color:#fff;font-size:26px}
#form-success p{color:#93A1B2;margin-top:10px}

/* ---------- faq ---------- */
#faq details{border:1px solid #22324F;border-radius:12px;background:rgba(12,25,48,.5);margin-top:12px;overflow:hidden}
#faq summary{cursor:pointer;list-style:none;padding:20px 24px;font-family:var(--display);font-weight:600;font-size:17px;color:#fff;display:flex;justify-content:space-between;align-items:center;transition:color .25s}
#faq summary::-webkit-details-marker{display:none}
#faq summary::after{content:"+";font-size:22px;color:var(--teal);transition:transform .3s}
#faq details[open] summary::after{transform:rotate(45deg)}
#faq details p{padding:0 24px 20px;color:#A9B6C6;font-size:15px;max-width:80ch}
#faq details p b{color:#E8EDF4}

/* ---------- footer ---------- */
footer{background:#050C18;padding:70px 0 50px;border-top:1px solid #14233E}
footer .cols{display:flex;justify-content:space-between;gap:40px;flex-wrap:wrap;align-items:flex-start}
footer .tag{font-family:var(--display);font-weight:600;font-size:16px;color:#B9C4D4;margin-top:14px;max-width:34ch}
footer .desc{font-size:13.5px;color:#5C7290;margin-top:8px;max-width:44ch}
footer nav{display:grid;gap:9px;font-size:14.5px;color:#93A1B2}
footer nav a:hover{color:#fff}
/* `justify-content:space-between` + `flex-wrap` reflows badly in the middle
   range: at 680-860 the first nav was flung to the far right of row 1 while the
   second orphaned below it on the left, splitting the two link groups apart.
   Space-between only behaves when every item is on one line. Below 861 lay the
   footer out on an explicit grid instead — brand across the top, the two link
   columns paired beneath it — and leave the (correct) desktop flex untouched. */
@media(max-width:860px){
  footer .cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:34px 40px}
  footer .cols > :first-child{grid-column:1 / -1}
}
@media(max-width:430px){footer .cols{grid-template-columns:minmax(0,1fr)}}
footer .base{margin-top:44px;padding-top:22px;border-top:1px solid #14233E;display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;font-family:var(--mono);font-size:12px;color:#5C7290}

/* ---------- pricing page ---------- */
.phero{min-height:72svh;display:grid;place-items:center;text-align:center;padding:clamp(112px,12.5vw,160px) 0 clamp(60px,7vw,90px);background:var(--ink) var(--dots) 0 0/26px 26px}
.factor{display:grid;grid-template-columns:52px minmax(0,1fr);gap:18px;padding:22px 24px;border:1px solid #22324F;border-radius:14px;background:rgba(12,25,48,.6)}
.factor .fn{font-family:var(--mono);font-size:13px;color:var(--teal);border:1px solid rgba(31,209,178,.4);border-radius:50%;width:40px;height:40px;display:grid;place-items:center}
.factor b{font-family:var(--display);font-size:17.5px;color:#fff;display:block;margin-bottom:4px}
.factor p{font-size:14.5px;color:#93A1B2}
.inc{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:14px;margin-top:44px}
.inc .i{display:grid;grid-template-columns:26px minmax(0,1fr);gap:12px;padding:18px 20px;border:1px solid #22324F;border-radius:12px;background:rgba(12,25,48,.5);font-size:15px;color:#B9C4D4}
.inc .i i{color:var(--acc);font-style:normal;font-weight:600}
.inc .i b{color:#fff}
/* coverage dial */
.dialwrap{display:grid;grid-template-columns:340px 1fr;gap:60px;align-items:center;margin-top:54px}
@media(max-width:900px){.dialwrap{grid-template-columns:1fr;justify-items:center}}
#dial{width:min(300px,100%);height:auto;aspect-ratio:1}
.covitems{display:grid;gap:18px}
.cov{display:grid;grid-template-columns:26px minmax(0,1fr);gap:14px}
.cov i{color:var(--sun);font-style:normal}
.cov b{font-family:var(--display);color:#fff;display:block;font-size:17px;margin-bottom:3px}
.cov p{font-size:14.5px;color:#93A1B2}
.fraction{margin-top:40px;padding:24px 28px;border-radius:14px;background:linear-gradient(90deg,rgba(31,209,178,.1),rgba(255,107,74,.08));border:1px solid rgba(31,209,178,.3);font-family:var(--display);font-weight:600;font-size:19px;color:#fff;text-align:center}
.steps{counter-reset:st;display:grid;gap:0;margin-top:50px;position:relative}
.steps::before{content:"";position:absolute;left:25px;top:30px;bottom:30px;width:2px;background:linear-gradient(180deg,var(--teal),var(--sun),var(--coral))}
.step{display:grid;grid-template-columns:52px 1fr;gap:22px;padding:20px 0;position:relative}
.step .sn{counter-increment:st;width:52px;height:52px;border-radius:50%;background:var(--ink);border:2px solid var(--teal);display:grid;place-items:center;font-family:var(--mono);font-size:15px;color:var(--teal);z-index:1}
.step .sn::before{content:counter(st)}
.step b{font-family:var(--display);font-size:18px;color:#fff;display:block;margin:6px 0 4px}
.step p{font-size:15px;color:#93A1B2;max-width:66ch}

/* generated UI icons (replace emoji) */
.ic{width:20px;height:20px;display:inline-block;vertical-align:-4px;flex:none;object-fit:contain}
.ptab .ic{margin-right:8px;width:19px;height:19px}
.cov i .ic{width:26px;height:26px;vertical-align:0}
/* active tab is solid teal — the teal icon must flip to ink like its label */
.ptab.act .ic{filter:brightness(0) saturate(100%)}

/* FAQ on the light surface. Every one of these overrides an explicit dark
   assumption in the block above (#fff text, a navy card, muted-on-dark body) —
   a small illustration of what a full light theme would need everywhere. */
#faq.light details{border-color:var(--mist);background:var(--paper)}
#faq.light summary{color:var(--ink)}
#faq.light summary::after{color:var(--teal-d)}
#faq.light summary:hover{color:var(--teal-d)}
#faq.light details p{color:var(--graphite)}
#faq.light details p b{color:var(--ink)}

/* ── brand spectrum walk ──────────────────────────────────────────────────────
   The gradient is the brand's meaning ("becomes what the customer needs"), so
   it travels through the page as a thread rather than becoming wallpaper:
   a hairline at each section boundary and the eyebrow label both sit at that
   section's position along teal → sun → coral. Surfaces stay neutral, so no
   contrast or accent-hierarchy is spent. --tone/--tone-d are stamped per
   section by build_site.py. */
section{position:relative}
section:not(:first-of-type)::before{
  content:"";position:absolute;left:0;right:0;top:0;height:2px;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--tone,transparent) 20%,
                             var(--tone,transparent) 80%,transparent);
  opacity:.8}
.eyebrow{color:var(--tone,var(--teal))}
.light .eyebrow{color:var(--tone-d,var(--teal-d))}

/* Each section carries a wash at its spectrum position, anchored to the BOTTOM
   edge. Anchoring it at the top put the brightest part directly behind the
   eyebrow and headline, washing them out — headings always sit at the top of a
   section, so the light pools at the opposite end and gathers into the boundary
   hairline instead of competing with type. */
.sect-dark,.sect-dark2{
  background-image:
    radial-gradient(130% 46% at 50% 100%,
      color-mix(in srgb, var(--tone,#1FD1B2) 16%, transparent), transparent 74%),
    var(--dots);
  background-size:auto, 26px 26px;
  background-repeat:no-repeat, repeat}
.light{
  background-image:
    radial-gradient(120% 42% at 50% 100%,
      color-mix(in srgb, var(--tone,#1FD1B2) 13%, transparent), transparent 76%);
  background-repeat:no-repeat}

/* #squad on the light surface — the narrative pivot from problem to alternative,
   so the surface change carries the argument. Each rule below replaces an
   explicit dark assumption; the avatars need special care because they are
   near-white coins with no transparency and would otherwise vanish on cloud. */
#squad.light .mem{background:var(--paper);border-color:var(--mist)}
#squad.light .mem:hover{border-color:var(--teal-d);box-shadow:0 16px 40px rgba(10,125,107,.16)}
#squad.light .mem img{box-shadow:0 0 0 1px var(--mist)}
#squad.light .mem b{color:var(--ink)}
#squad.light .mem p{color:var(--slate)}
#squad.light .note{color:var(--graphite)}
#squad.light .note b{color:var(--teal-d)}

/* Nav scroll progress. The morph gradient is laid out across the full viewport
   width (background-size:100vw) and *revealed* by the growing width — so it is
   never a squashed gradient, and the leading edge always shows the hue of the
   part of the page you are in. The nav's existing faint bottom hairline acts as
   the track underneath it. */
#nav::after{
  content:"";position:absolute;left:0;bottom:0;height:2px;width:var(--p,0%);
  background:var(--morph);background-size:100vw 100%;background-repeat:no-repeat;
  pointer-events:none;transition:width .12s linear}
@media(prefers-reduced-motion:reduce){#nav::after{transition:none}}

/* The inclusions list on the light band (pricing). Same shape as the FAQ and
   #squad conversions: a dark card, near-white bold and muted-on-dark body text
   all need light equivalents. The tick marker already adapts via --acc. */
.light .inc .i{background:var(--paper);border-color:var(--mist);color:var(--graphite)}
.light .inc .i b{color:var(--ink)}

.phero .wrap{width:100%}
.covlab span,.covlab em{white-space:nowrap}

/* ── pricing hero: pricing table → job description ────────────────────────────
   Three familiar SaaS tiers assemble, their per-seat prices scramble, then the
   whole thing breaks into fragments that morph into a single job description.
   The transformation is the argument — "priced like a team, not a tool" — so it
   needs no caption. Layers are stacked absolutely in one stage so the morph
   happens in place. */
.jobmorph{margin:54px auto 0;max-width:900px;width:100%}
.jm-stage{position:relative;min-height:306px}

.jm-tiers{position:absolute;inset:0;display:grid;grid-template-columns:repeat(3,1fr);
  gap:16px;align-items:center;transition:opacity .4s ease}
.jm-tiers.gone{opacity:0;transition:opacity .16s ease}
.jm-tier{border:1px solid rgba(148,178,255,.14);border-radius:14px;padding:20px 18px;
  background:rgba(148,178,255,.035);text-align:left;
  opacity:0;transform:translateY(16px);
  transition:opacity .55s cubic-bezier(.2,.8,.2,1),transform .55s cubic-bezier(.2,.8,.2,1)}
.jm-tiers.on .jm-tier{opacity:1;transform:none}
.jm-tiers.on .jm-tier:nth-child(2){transition-delay:.09s}
.jm-tiers.on .jm-tier:nth-child(3){transition-delay:.18s}
.jm-tier b{display:block;font-family:var(--display);font-size:13px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:#7C8BA3}
.jm-price{display:block;font-family:var(--display);font-weight:800;font-size:34px;
  color:#9FB0C6;margin-top:10px;letter-spacing:-.02em}
.jm-price i{font-style:normal;font-variant-numeric:tabular-nums}
.jm-tier em{display:block;font-style:normal;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.08em;text-transform:uppercase;color:#5C7290;margin-top:4px}
.jm-tier u{display:block;height:6px;border-radius:3px;background:rgba(148,178,255,.11);
  margin-top:12px;text-decoration:none}
.jm-tier u:nth-of-type(2){width:76%}
.jm-tier u:nth-of-type(3){width:54%}

.jm-fx{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}

.jm-card{position:absolute;left:50%;top:50%;width:min(540px,100%);
  transform:translate(-50%,-50%) scale(.94);opacity:0;
  border:1px solid rgba(31,209,178,.34);border-radius:16px;padding:24px 26px;
  background:linear-gradient(180deg,rgba(12,25,48,.96),rgba(10,22,40,.96));
  box-shadow:0 26px 70px rgba(0,0,0,.5);text-align:left;
  transition:opacity .6s cubic-bezier(.2,.8,.2,1),transform .6s cubic-bezier(.2,.8,.2,1)}
.jm-card.on{opacity:1;transform:translate(-50%,-50%) scale(1)}
.jm-head{display:flex;align-items:center;gap:13px;padding-bottom:15px;margin-bottom:15px;
  border-bottom:1px solid rgba(31,209,178,.18)}
.jm-head .mk{width:34px;height:34px;flex:none}
.jm-head .mk svg{width:100%;height:100%;display:block}
.jm-head b{display:block;font-family:var(--display);font-weight:700;font-size:19px;color:#fff;
  letter-spacing:-.015em}
.jm-head small{display:block;font-family:var(--mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--teal);margin-top:3px}
.jm-list{margin:0;display:grid;gap:11px}
.jm-list > div{display:grid;grid-template-columns:104px 1fr;gap:12px;align-items:baseline}
.jm-list dt{font-family:var(--mono);font-size:11px;letter-spacing:.12em;
  text-transform:uppercase;color:#7C8BA3}
.jm-list dd{margin:0;font-size:15px;color:#D6DEE9}

@media(max-width:620px){
  .jobmorph{margin-top:38px}
  .jm-stage{min-height:340px}
  .jm-tiers{gap:8px}
  .jm-tier{padding:14px 11px;border-radius:11px}
  .jm-tier b{font-size:11px;letter-spacing:.06em}
  .jm-price{font-size:23px;margin-top:7px}
  .jm-tier em{font-size:10.5px;letter-spacing:.04em}
  .jm-card{padding:19px 17px}
  .jm-head b{font-size:16.5px}
  .jm-list > div{grid-template-columns:1fr;gap:2px}
  .jm-list dd{font-size:14px}
}
@media(max-width:430px){
  /* Three columns in a 264px page left each card ~65px of inner width, and the
     type had been crushed to 9px/8px to make "ENTERPRISE" and "per seat / month"
     fit — well under any legible floor. The column count was the problem, not the
     type: stack the tiers instead and the same words fit at a readable size. The
     decorative bars go, which keeps three stacked cards inside the 340px stage. */
  .jm-tiers{grid-template-columns:minmax(0,1fr);gap:10px}
  .jm-tier{padding:11px 14px}
  .jm-tier b{font-size:11.5px;letter-spacing:.06em}
  .jm-price{font-size:21px;margin-top:2px}
  .jm-tier em{font-size:10.5px;letter-spacing:.04em;margin-top:2px}
  .jm-tier u{display:none}
}
/* Ties the job card back to the question the visitor came with. Without it the
   card answers "what is it" but never "how is it priced". */
.jm-foot{margin:15px 0 0;padding-top:13px;border-top:1px solid rgba(31,209,178,.16);
  font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:#7C8BA3}
.jm-foot b{color:var(--teal);font-weight:500}
/* the held "rejection" beat: the tiers visibly stop applying before they break */
.jm-tiers.dead .jm-tier{filter:grayscale(1) brightness(.62);transform:scale(.985);
  transition:filter .45s ease,transform .45s ease}
.jm-tiers.dead .jm-price{color:#5C7290}
@media(max-width:620px){.jm-foot{font-size:11px;letter-spacing:.05em}}

/* ── mobile nav ───────────────────────────────────────────────────────────────
   Below 720px the four section links did not fit and rendered off-screen — they
   were unreachable on any phone. They now collapse behind a toggle while the
   CTA stays visible, since that is the conversion path. display:contents keeps
   the desktop flex layout byte-identical. */
.nlinks{display:contents}
.navtog{display:none}
@media(max-width:900px){
  #nav .wrap{gap:12px}
  .nlinks{display:grid;position:absolute;left:0;right:0;top:100%;
    background:rgba(8,15,29,.97);backdrop-filter:blur(14px);
    border-top:1px solid rgba(255,255,255,.07);
    box-shadow:0 24px 50px rgba(0,0,0,.5);
    padding:6px 20px 14px;gap:2px;
    opacity:0;visibility:hidden;transform:translateY(-8px);
    transition:opacity .22s ease,transform .22s ease,visibility .22s}
  #nav.open .nlinks{opacity:1;visibility:visible;transform:none}
  .nlinks a.nl{display:flex;align-items:center;min-height:48px;font-size:16px;
    border-bottom:1px solid rgba(255,255,255,.05)}
  .nlinks a.nl:last-child{border-bottom:0}
  /* The CTA was given flex:0 0 auto when it started truncating its own label;
     the toggle was left shrinkable, so it became the only thing in the row that
     could absorb an overflow — and it did, collapsing to 24px wide with its
     three bars at ZERO width at 375px. A control must not pay for the layout's
     overspend. */
  .navtog{display:inline-grid;align-content:center;gap:5px;order:3;flex:0 0 auto;
    width:44px;height:44px;padding:0 11px;
    background:transparent;border:1px solid var(--line,#22324F);border-radius:9px;cursor:pointer}
  .navtog span{display:block;height:2px;border-radius:2px;background:#C7D2E0;
    transition:transform .25s ease,opacity .25s ease}
  #nav.open .navtog span:first-child{transform:translateY(3.5px) rotate(45deg)}
  #nav.open .navtog span:last-child{transform:translateY(-3.5px) rotate(-45deg)}
  #nav .sp{display:none}
  /* Exactly ONE auto margin may do the pushing. Flex splits free space evenly
     between every item that has one, so with an auto margin on both the CTA and
     the toggle the CTA sat at the midpoint of the leftover space and drifted as
     the viewport resized. The CTA carries the margin; the toggle rides beside
     it on the row's gap. */
  #nav .btn.sm{order:2;margin-left:auto;font-size:13.5px;padding:11px 14px}
}
/* .btn sets overflow:hidden for its sheen, so a squeezed nav CTA does not
   overflow visibly — it silently cuts its own label. At 430px "Get a Backstage
   Pass" needed 161px and was given 133px. Never let it be the item that
   shrinks, and start the compact sizing before the squeeze begins. */
#nav .btn.sm{flex:0 0 auto}
@media(max-width:470px){
  #nav .btn.sm{font-size:12.5px;padding:10px 11px}
}
/* nothing in the nav may wrap inside itself */
#nav a.nl,#nav .btn.sm,#nav .logo{white-space:nowrap}
/* Between 375 and 411 the wordmark is back but the viewport is still narrow, so
   logo + toggle + CTA + gaps + 28px page gutter wanted 397px inside 375. The nav
   is one dense row and does not need the section gutter at phone widths; giving
   it back 24px is what lets every item keep its real size. 375 and 390 are the
   two commonest phone widths, so this window mattered. */
@media(max-width:430px){
  #nav .wrap{padding:0 16px}
}
@media(max-width:374px){
  /* The mark alone carries the brand only where the wordmark genuinely will not
     fit: the nav's min-content is 349px, so below ~375 it would push the CTA.
     This threshold was 400 when the lockup was a fixed 21px/34px; now that it
     scales, the name survives on the common 375-430px phones. */
  #nav .logo b.wm{display:none}
  #nav .btn.sm{font-size:12.5px;padding:10px 12px}
}
@media(max-width:420px){
  .factor{padding:16px 14px;gap:12px;grid-template-columns:44px minmax(0,1fr)}
  .fraction{padding:18px 16px;font-size:16px}
  .inc .i{padding:14px 14px}
}

/* ── touch targets ────────────────────────────────────────────────────────────
   Measured at 23px (nav + footer links), 32px (tab controls) and 34px (logo) on
   phones — well under the ~44px people can reliably hit. */
@media(max-width:520px){
  #nav .logo,footer .logo{min-height:44px;display:inline-flex;align-items:center}
  /* inline links inside body copy are exempt — forcing 44px there would
     break the line flow of the sentence they sit in */
  #nav .btn.sm{min-height:44px}
  footer nav a{display:inline-flex;align-items:center;min-height:44px}
  .chap,.stab,.gvtab,[role="tab"]{min-height:44px;padding-block:10px}
  .btn,.btn.ghost{min-height:44px}
  .light .inc .i,.inc .i{min-height:auto}
}

/* #hero is a centred grid, so .hgrid was sized to its content rather than the
   viewport, and #ticker's unwrappable mono line (282px min-content) pushed it to
   338px inside a 320px phone. #hero{overflow:hidden} hid the damage by clipping
   it. min-width:0 lets the tracks shrink; the ticker clips inside its own box. */
/* min-width:0 on .hgrid ITSELF is the operative fix: it is a grid item of
   #hero, and a grid item's automatic minimum size overrides width:100%. */
#hero .hgrid{width:100%;min-width:0}
.hgrid > *{min-width:0;max-width:100%}
#ticker{width:100%}
@media(max-width:420px){#ticker{font-size:11px}}

/* ── scrollytelling stages on small screens ───────────────────────────────────
   #problem and #how are pinned stages: a tall .py runway with a
   position:sticky, height:100svh, overflow:hidden stage inside. That works while
   the content fits one viewport. On phones #problem .cols stacks to one column
   and .hsteps stacks too, so the content becomes far taller than 100svh and the
   stage silently CLIPPED it — 931px of the problem section was cut off at 320px.
   Below 900px the stages are unpinned and simply flow. */
/* Unpinning is a question of VERTICAL fit, so it cannot be keyed to width
   alone. The stage content runs 744–778px; on a 1280x640 window (a laptop with
   browser chrome, or a landscape tablet) the pinned 100svh stage clipped ~130px
   of it — the same defect as the narrow case, on a wide screen. Stacking below
   stays width-keyed; only the pinning is height-aware. */
/* Unpin when the stage genuinely cannot fit, not merely when the viewport is
   shortish. Measured, the pinned content needs ~747px once there is room to
   lay out wide, but ~782px between 900 and 1280 where the columns wrap into
   more lines. A single 820px cutoff therefore disabled the scroll animation
   on ordinary laptops — a 1440x900 has about 780px of viewport — while the
   content would have fitted perfectly well. Two thresholds, one per case. */
@media (max-width:900px),(max-height:740px),(max-width:1279px) and (max-height:800px){
  /* Unpinned there is no scrub to move the transcript, so the window must open
     to full height — or only the first beat would ever be visible. This has to
     switch on the SAME condition as the unpin itself, not on width alone. */
  #how .py{height:auto}
  #how .stick{position:static;height:auto;overflow:visible;
    padding:clamp(60px,8vw,88px) 0 clamp(48px,6vw,72px);display:block}
  /* the stage was a centred grid, so .wrap was a grid item shrink-wrapped to its
     content (415px inside a 390px phone). Unpinning removed the overflow:hidden
     that had been masking it, so it began pushing the page sideways. */
  #how .stick > *{min-width:0;width:100%}
  #how .head{margin-bottom:42px}
}
/* .hsteps had NO responsive rule — four 112px rings side by side at 320px.
   Below 900px the stage is unpinned and the steps become a vertical timeline:
   one column, rail down the ring gutter, text beside it.

   This treatment used to start at 520px, which left the whole 521–900px band
   with a two-column grid and no timeline at all — and the rule meant to hide
   the horizontal rail there was silently dropped by a stray brace above it, so
   a connector was drawn between rings 01 and 02 joining nothing. One band, one
   treatment: wherever the steps stack, the rail runs down them. */
@media(max-width:900px){
  .hsteps{grid-template-columns:minmax(0,1fr);gap:26px}
  .hstep{grid-template-columns:74px minmax(0,1fr);column-gap:16px;
    justify-items:start;text-align:left;align-items:start}
  .hstep .n{grid-column:2;grid-row:1;margin-bottom:5px}
  .hstep .ring{grid-column:1;grid-row:1 / span 3;width:74px;height:74px;align-self:start}
  .hstep .ring img{width:60px;height:60px}
  .hstep b{grid-column:2;grid-row:2;margin:0 0 5px}
  .hstep p{grid-column:2;grid-row:3;max-width:62ch}
  /* the rail spans ring-centre to ring-centre, so its bottom offset is the last
     step's height minus half a ring — which grows as the copy wraps */
  #rail{display:block;left:37px;right:auto;margin-left:-2px;
    top:44px;bottom:44px;width:4px;height:auto;
    background:linear-gradient(180deg,#1FD1B2,#FFC857 55%,#FF6B4A);
    transform-origin:top;transform:scaleY(var(--rp,0))}
}
@media(max-width:520px){.hstep p{max-width:34ch}#rail{bottom:96px}}
@media(max-width:560px){.sface{padding:22px}}

/* Kept at the end deliberately: the unpin block above opens .log to full height
   for the desktop row layout, and on a phone the screen must stay a fixed
   window instead. Same specificity, so order is what decides. */
@media(max-width:900px){
  /* One boundary, not two. #problem used to ALSO unpin on short viewports, which
     gave a 1000px-wide window a third behaviour — a plain static list — that
     neither the desktop scrub nor the phone layout resembled. Its stage is a
     small window now, so it fits short screens and does not need the escape. */
  #problem .py{height:auto}
  #problem .stick{position:static;height:auto;overflow:visible;
    padding:clamp(60px,8vw,88px) 0 clamp(48px,6vw,72px)}
  #problem .stick > *{min-width:0;width:100%}
  #problem .log{height:auto;overflow:visible}
  .loginner{transition:none}

  /* Six cropped screenshots stacked down a phone is the mistake the portrait cut
     of the film taught us to avoid: everything present at once, nothing in focus.
     Here the screen is ONE surface that sticks while the verdicts scroll past it
     — one exchange and one verdict in view at a time.

     Written as a single set. The previous pass left the old card rules below the
     new ones in this same block, so white rounded cards with their own shadows
     were being painted inside the white screen. */
  .tscript{max-width:520px;margin:0 auto}

  /* header and screen stick together, the bar a fixed height so the offsets line up */
  /* One sticky unit, not two. A sticky element releases when its container's
     bottom reaches top + ITS OWN height — the screen is 352px tall and the bar
     44px, so the screen let go first while the bar stayed pinned and the two
     came apart on the way out of the section. */
  .device{display:block;position:sticky;top:var(--barTop);z-index:4}
  .tshead{position:static;grid-template-columns:minmax(0,1fr)}
  .tshead .bar{grid-column:1;height:44px;display:flex;flex-direction:column;justify-content:center;
    border-radius:14px 14px 0 0;background:#59606B;color:#fff;padding:0 15px;
    font-size:13.5px;box-shadow:none}
  .tshead .bar small{display:block;color:#C9CDD3;font-size:11px}
  /* Everything was pinned to a fixed 122px with all the slack left at the foot,
     which reads as the composition having slipped upward rather than been
     placed. Centring the whole stack — bar, screen, card — in the space below
     the nav splits that slack evenly, so what is left looks like margin instead
     of a hole. max() keeps it clear of the nav on short viewports. */
  .tscript{--screenH:clamp(258px,44svh,360px);
    --compH:calc(var(--screenH) + 233px);
    --barTop:max(84px, calc(72px + (100svh - 72px - var(--compH)) / 2))}
  #problem .log{position:static;height:var(--screenH);
    overflow:hidden;background:#fff;border-radius:0 0 14px 14px;
    box-shadow:0 22px 46px rgba(0,0,0,.5)}
  #problem .log::before{display:none}
  .cinput{width:100%;height:44px}
  .loginner{transition:transform .5s cubic-bezier(.22,1,.28,1)}

  /* inside the screen there are no cards — it is one continuous conversation */
  .beat{grid-template-columns:minmax(0,1fr);margin:0;padding:0}
  .beat .chat{grid-column:1;background:transparent;box-shadow:none;border-radius:0;
    padding:9px 13px;justify-content:flex-start}
  .beat::before,.beat::after{display:none}
  .bmsg{max-width:88%;font-size:12.6px}

  /* the notes: one screenful apart, so only one is ever being read */
  /* Bare text in a tall spacer left ~226px of dead ground after every point, and
     the lower half of the screen looked empty. Each note is a solid CARD now,
     stuck directly under the screen inside its own runway: as you scroll, the
     next card rides up and covers the last, so the space below the conversation
     is always filled and the points arrive as a stack rather than a list. */
  .tnotes{display:block;margin-top:14px}
  /* Snap spacing, not just breathing room: an ordinary swipe carries roughly
     600px of momentum, so at 44svh (334px between points) one flick advanced
     two points and a firm one advanced four. At 54svh the gap is 410px and a
     normal swipe lands on the next point instead of vaulting over it. Past this
     the curve flattens — 64svh and 76svh skip no less, they only add scrolling. */
  .slot{min-height:54svh}
  /* All six stick to the same line under the screen, and only the live one is
     drawn — so the space below the conversation holds exactly one card at all
     times: no gap between points, and no two of them on screen at once.
     z-index keeps a card that has scrolled past its runway BEHIND the screen
     rather than bleeding out above it. */
  .tnotes .vd{position:sticky;top:calc(var(--barTop) + 44px + var(--screenH) + 14px);z-index:1;
    display:block;margin:0;padding:15px 17px 16px 19px;border-radius:13px;
    background:#152441;border:1px solid #22324F;transform:none;
    opacity:0;visibility:hidden;pointer-events:none;
    transition:opacity .35s ease,visibility .35s;
    box-shadow:0 14px 30px rgba(6,12,24,.5)}
  .tnotes .vd.live{opacity:1;visibility:visible}
  .tnotes .vd::before{content:"";display:block;position:absolute;left:0;top:14px;bottom:14px;
    width:3px;border-radius:3px;background:#2B3C5C;transform:none;transition:background .4s ease}
  .tnotes .vd.live::before{background:var(--coral)}
  .tnotes .vd.live{border-color:rgba(255,107,74,.3)}
  .tnotes .vdn{font-size:11px;margin-bottom:9px;opacity:1}
  .tnotes .vd.live .vdn::after{content:"";display:inline-block;width:26px;height:2px;
    background:var(--coral);vertical-align:middle;margin-left:9px;border-radius:2px}
  /* Capped below the section heading: at 5.9vw it reached 27px while the section
     headline was clamped at 28, so the two were within a pixel and the hierarchy
     read flat. A card heading should never rival the headline it sits under. */
  .tnotes .vd b{font-size:clamp(20px,5.2vw,23px);line-height:1.18;margin-bottom:9px}
  .tnotes .vd p{font-size:15.5px;line-height:1.58;max-width:34ch;color:#93A1B2}
  /* the rail turns what was dead space into a position: how far through the
     failure you are, without needing a caption to say so */
  .vdrail{display:grid;grid-template-columns:repeat(6,1fr);gap:5px;margin-top:15px}
  .vdrail i{height:3px;border-radius:3px;background:#26364F}
  .vdrail i.f{background:var(--coral);opacity:.85}
  .tnotes .vd.live .vdrail i.f:last-of-type{box-shadow:0 0 0 2px rgba(255,107,74,.22)}
}

/* Narrow-width refinements. Must follow the max-width:900px block above,
   which sets --screenH and the card metrics these override. */
@media(max-width:340px){
  /* The squeeze here is the HEADING, not the chat: three lines of headline plus a
     two-line eyebrow eat the budget, and shrinking the chat window to compensate
     left it holding two bubbles above a cramped composer. Take the room from the
     type instead — the heading scrolls away anyway, so it only costs the entry
     view, while the window and cards keep their proper size. */
  #problem .phead{margin-bottom:22px}
  #problem .eyebrow{font-size:11px;letter-spacing:.1em}
  #problem h2.sect{font-size:23px}
  .tscript{--screenH:clamp(230px,36svh,290px)}
  /* narrow enough that the composer needs its own budget */
  .cinput{padding:0 9px;gap:7px}
  .cinput span{font-size:12px;padding:7px 11px}
  .cinput i{width:23px;height:23px}
  .cinput i::after{left:8px;top:7px;border-left-width:6px;border-top-width:4.5px;border-bottom-width:4.5px}
}

/* Squad bench on very narrow screens.
   repeat(2,1fr) is really repeat(2,minmax(auto,1fr)): the 86px avatar plus the
   card's own padding and border sets a min-content floor on the first column,
   and once the viewport drops below ~330px the second column is the only one
   that can give — 121px against 99px at 280px wide, with "The Retention Expert"
   breaking over four lines. Laying the card out sideways removes the floor
   instead of fighting it: one column, avatar beside the copy, left-aligned so
   the ragged centred text stops looking accidental. It also cuts the bench from
   ~1100px of scrolling to roughly half. */
@media(max-width:380px){
  #squad .bench{grid-template-columns:minmax(0,1fr);gap:10px;margin-top:40px}
  /* Avatar and name share the top row; the sentence runs the full width beneath
     them. Keeping the copy beside the avatar instead left it a ~106px column at
     260px wide — three words to a line — and floated the avatar halfway down the
     card, level with the paragraph rather than the name it belongs to. */
  .mem{grid-template-columns:auto minmax(0,1fr);
       grid-template-areas:"img name" "copy copy";
       align-items:center;justify-items:start;gap:10px 13px;padding:15px}
  .mem img{grid-area:img;width:46px;height:46px}
  .mem b{grid-area:name;text-align:left;font-size:15.5px;line-height:1.25}
  .mem p{grid-area:copy;text-align:left;font-size:13px;line-height:1.5}
}


/* "The part nobody else can say" on very narrow screens.
   Nothing here is broken — it is sized for a column that no longer exists. The
   34px headline floor, the 19px sub, 20px of chat padding and an 86% bubble cap
   each cost a little, and together they stretch the section to 1923px at 280px
   wide against 1149px at 600px: the same content over two-thirds again as much
   scrolling, which is what reads as enlarged and off. */
@media(max-width:380px){
  #persona h2.sect{font-size:clamp(24px,7.4vw,30px)}
  #persona .sub{font-size:15.5px;margin-top:12px}
  #persona .pwrap{margin-top:34px;gap:20px}

  /* Three pills of different widths, centred and stacked, read as debris left
     over from a row. Full width makes the same three rows look like the chooser
     they are. Kept stacked rather than side-scrolling: two of the three personas
     would sit off-screen, and trying all three IS the section. */
  .ptabs{flex-direction:column;gap:8px;margin-top:20px}
  .ptab{width:100%;text-align:left;font-size:13.5px;padding:9px 14px}

  /* the name and the attribution cannot share a 244px line; let the attribution
     drop under the name instead of being crushed against it */
  .pchat .hd{flex-wrap:wrap;gap:6px 10px;padding:12px 14px;font-size:14.5px}
  .pchat .hd .hdby{margin-left:0;flex-basis:100%}

  /* The bubbles were the single biggest cost: 20px of padding on each side plus
     an 86% cap left the reply a ~190px measure — eleven lines, with the emoji
     orphaned on a line of its own. */
  .pchat .log{padding:13px;gap:9px}
  .pmsg{max-width:95%;font-size:13.5px;padding:10px 12px;line-height:1.5}

  .pconsole{padding:14px 15px}
  .pchips{gap:6px;margin-bottom:14px}
  /* a 999px radius on a chip that has wrapped to three lines reads as a blob */
  .pchip{font-size:10.5px;padding:4px 9px;border-radius:11px}
  .plead{font-size:13px;padding-top:12px}
  #persona .big-line{margin-top:30px;font-size:19px}
}

/* Section two, fast scrolling.
   One fling carries the page through the whole section — measured 1109 to
   4666px in a single gesture, with points 2, 4 and 5 live for one or two frames
   each and point 3 never becoming live at all. It cannot be fixed in script:
   the compositor scrolls ahead of the main thread (516px between consecutive
   frames in that trace), so a handler cannot light a point the scroll has
   already gone past. The scroll itself has to stop.
   scroll-snap-stop:always is the mechanism for exactly that — a scroll may not
   pass over the snap position, so a fling advances one point and settles. The
   snap offset is built from the same two variables the cards stick at, so a
   settled slot leaves its card parked on the reading line rather than caught
   mid-flight. proximity rather than mandatory because the declaration sits on
   the document scroller: only these slots opt in, and proximity keeps every
   other section free of pull from them. */
@media(max-width:900px){
  html{scroll-snap-type:y proximity}
  .tnotes .slot{scroll-snap-align:start;scroll-snap-stop:always;
    scroll-margin-top:calc(var(--barTop) + var(--screenH))}
}
