/* ==========================================================
   folio.css : the Vaultline site design system (VLP-1224).

   WHAT THIS FILE OWNS
   The whole shared layer: the Archive Console tokens, the reset and base type,
   the section formula, buttons, the pill nav, frames and the window device,
   the grids, the FAQ, the footer, the application modal, the sticky mobile CTA,
   the scrollcraft token rebrand and base-rule restatements, the no-JS and
   ?static=1 fallbacks, and every responsive rule for those selectors.

   WHAT A PAGE MUST PROVIDE
   - Load order: the critical fallback <style>, then /folio.css, then the page's
     own sheet, then /scrollcraft.css last. The engine sheet is last so a page
     can override its tokens; this file restates every engine base rule it
     changes at a higher specificity, which is why the engine sheet coming
     second cannot restyle the page.
   - Its own hero, its own act stages, and any per-page crop values. Everything
     with a page name in it (a .hero-*, an #id seam) belongs to the page sheet.
   - Rename nothing here. Three pages share these names.

   THE FORMULA
   One formula per section, repeated verbatim: mono-caps eyebrow -> serif
   headline (first word italic) -> one grey subhead -> an optional small button
   or mono link. Hierarchy comes from scale, not colour. Signal Blue is reserved
   for links, focus and one search affordance per page; Archive Amber appears at
   most once, on a price.
   ========================================================== */

/* ---------------- tokens ---------------- */
:root{
  --ink:#0B0F14;
  --surface:#151B23;
  --surface-2:#1A2129;
  --text:#F4F1EA;
  --muted:#7D8896;   /* 5.3:1 on --ink */
  --dim:#7A8592;     /* 5.1:1 on --ink */
  --blue:#4C8DFF;
  --amber:#D7A84F;
  --line:rgba(244,241,234,.075);
  --line-2:rgba(244,241,234,.14);
  --sans:"Inter Tight",system-ui,-apple-system,sans-serif;
  --serif:"Instrument Serif",Georgia,"Times New Roman",serif;
  --mono:"Spline Sans Mono",ui-monospace,monospace;
  --pad:32px;
  --rhythm:132px;
  /* The seam between two flow sections, not the pad on one of them. .sec carries this value top
     AND bottom, so two consecutive sections stack twice it: 150px at desktop, 128px at the
     tablet step, 96px on a phone. It used to be --rhythm * .86 = 113.52px a side, which put a
     227px band of unpainted ground between the last line of one chapter and the 11px eyebrow of
     the next on six of the eight pages. A quarter of a 900px viewport with nothing in it reads
     as slides with gaps between them rather than as a page. Every per-page top pad that was
     compensating for that number is now expressed against this one. */
  --sec-pad:75px;
  --nav-h:60px;
  --nav-inset:22px;
  --shadow:0 60px 140px -60px rgba(0,0,0,.95);
  --ease-out:cubic-bezier(.16,.84,.34,1);
}

/* ---------------- the scroll engine ----------------
   /scrollcraft.css loads AFTER this file, so its :root and its element rules would otherwise
   win on order. Two moves keep the page exactly as designed: rebrand the engine's tokens (it
   is about a dozen values), and re-state the handful of base rules it changes at a higher
   specificity. Nothing else in the engine sheet touches markup these pages use. */
html:root{
  --sc-canvas:#0B0F14;
  --sc-surface:#151B23;
  --sc-ink:#F4F1EA;
  --sc-ink-soft:#7D8896;
  --sc-accent:#4C8DFF;          /* Signal Blue owns focus and the caret, as it already did */
  --sc-accent-ink:#0B0F14;
  --sc-font-display:"Instrument Serif",Georgia,"Times New Roman",serif;
  --sc-font-text:"Inter Tight",system-ui,-apple-system,sans-serif;
  --sc-font-mono:"Spline Sans Mono",ui-monospace,monospace;
  --sc-t-base:16px;
  --sc-leading-body:1.55;
  --sc-track-normal:normal;
  --sc-r-sm:4px;
}
html body{
  background:var(--ink);color:var(--text);font-family:var(--sans);
  font-size:16px;line-height:1.55;letter-spacing:normal;
  /* clip, not hidden. overflow-x:hidden turns the body into a scroll container, and a
     position:sticky stage inside one stops pinning against the viewport. */
  overflow-x:clip;
}
html ::selection{background:var(--amber);color:var(--ink)}
/* The engine adds img{max-width:100%}, which collapses a fixed-width wordmark inside an
   inline-flex chip to nothing. */
img.brand-logo,img.ft-logo{max-width:none}
/* The engine's spotlight paints an accent wash. A page that uses the same pointer signal for
   something of its own (the homepage leans its console) turns the wash off. */
[data-sc-spotlight]::after{display:none}

/* ---------------- reset ---------------- */
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%;scroll-behavior:auto}
@media(prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}
body{
  background:var(--ink);color:var(--text);
  font-family:var(--sans);font-size:16px;line-height:1.55;font-synthesis:none;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow-x:hidden;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:none}
ul,ol{list-style:none}
::selection{background:var(--amber);color:var(--ink)}
html{scrollbar-color:rgba(244,241,234,.18) transparent;scrollbar-width:thin}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(244,241,234,.14);border-radius:99px;border:3px solid var(--ink);background-clip:padding-box}
:focus-visible{outline:2px solid var(--blue);outline-offset:3px;border-radius:4px}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.skip-link{position:fixed;top:12px;left:16px;z-index:200;background:var(--text);color:var(--ink);
  font-family:var(--mono);font-size:12px;padding:10px 14px;border-radius:8px;transform:translateY(-200%);transition:transform .2s}
.skip-link:focus{transform:none}
.wrap{max-width:1240px;margin:0 auto;padding:0 var(--pad)}
section[id],header[id]{scroll-margin-top:calc(var(--nav-h) + var(--nav-inset) + 24px)}

/* ---------------- type ----------------
   The serif is display only, and never below ~26px: at small sizes it stops
   reading as a decision and starts reading as an accident. */
.display{font-family:var(--serif);font-weight:400;line-height:1.02;letter-spacing:-.005em;
  color:var(--text);text-wrap:balance}
.display em{font-style:italic}
h1.display{font-size:clamp(38px,5.6vw,78px);max-width:15ch}
h2.display{font-size:clamp(32px,4.4vw,62px);max-width:17ch}
.display-xl{font-size:clamp(34px,4.8vw,66px);max-width:16ch}
h3{font-family:var(--sans);font-size:18px;font-weight:600;letter-spacing:-.02em;line-height:1.25}
.eyebrow{font-family:var(--mono);font-size:11px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--muted);display:inline-flex;align-items:center;gap:10px}
.eyebrow::before{content:"";width:18px;height:1px;background:var(--blue);flex:none}
.num{font-family:var(--mono);font-size:10.5px;letter-spacing:.15em;color:var(--dim);text-transform:uppercase;display:block}
.lead{color:var(--muted);font-size:clamp(16px,1.35vw,18px);line-height:1.6;max-width:52ch;text-wrap:pretty}
.fine{font-family:var(--mono);font-size:10.5px;letter-spacing:.03em;color:var(--dim);line-height:1.6}
.nb{white-space:nowrap}
/* The one amber on a page, and only ever on a price. */
.price{color:var(--amber);font-weight:600}

/* Small, quiet controls. Whitespace and headline scale carry the weight, so a
   button is never allowed to become the loudest thing on a screen. */
.btn{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);font-size:12px;
  letter-spacing:.09em;text-transform:uppercase;font-weight:500;min-height:40px;padding:0 16px;
  border-radius:8px;border:1px solid transparent;cursor:pointer;white-space:nowrap;
  transition:background .18s,border-color .18s,box-shadow .18s,color .18s}
.btn-primary{background:var(--text);color:var(--ink)}
.btn-primary:hover{box-shadow:0 0 0 1px rgba(244,241,234,.35)}
.btn-ghost{border-color:var(--line-2);color:var(--text);background:rgba(244,241,234,.02)}
.btn-ghost:hover{border-color:var(--muted);background:rgba(244,241,234,.05)}
.btn[aria-disabled="true"]{opacity:.62;cursor:default}
.btn[aria-disabled="true"]:hover{box-shadow:none}
.btn .a{font-family:var(--sans)}
.mlink{font-family:var(--mono);font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--blue);display:inline-flex;align-items:center;gap:8px;transition:color .18s}
.mlink:hover{color:var(--text)}
.chip{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--blue);display:inline-flex;align-items:center;padding:7px 13px;border-radius:99px;
  border:1px solid rgba(76,141,255,.28);background:rgba(76,141,255,.07)}

/* ---------------- nav ----------------
   A static floating pill: fixed, inset from the top, translucent, blurred.
   It never shrinks and never restyles on scroll. */
#nav{position:fixed;top:var(--nav-inset);left:0;right:0;z-index:100;padding:0 var(--pad);pointer-events:none}
/* The fill is the page's own ink rather than a lighter grey, at .78: blur alone never stopped
   body copy and product-screenshot chrome reading through the labels, and a lighter fill made
   bright content behind the pill lift the whole bar. Ink at .78 under blur(24px) keeps the pill
   reading as one surface over the brightest thing on the site, and the hairline border is what
   separates it from the ground rather than the fill doing it. */
.nav-pill{pointer-events:auto;max-width:1180px;margin:0 auto;height:var(--nav-h);
  display:flex;align-items:center;gap:20px;padding:0 10px 0 18px;border-radius:99px;
  border:1px solid var(--line-2);background:rgba(11,15,20,.86);
  backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px)}
.brand{display:inline-flex;align-items:center;flex:none}
.brand-logo{width:130px;height:auto}
.nav-links{display:flex;align-items:center;gap:26px;margin:0 auto}
.nav-links a{font-family:var(--mono);font-size:11px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--muted);transition:color .18s}
.nav-links a:hover{color:var(--text)}
.nav-right{display:flex;align-items:center;gap:10px;flex:none}
/* The pill's CTA is a reminder, not the offer. It carries a two-word form of the page's one CTA
   label ("Start", "Download", "Get the guide"), and the full sentence stays on every other
   control. A 291px button inside a 99px pill squares off its own cap and leaves the last nav
   link 70px of room at 1024, which is a nav that has become a banner. */
.nav-toggle{display:none;width:44px;height:44px;border:1px solid var(--line-2);border-radius:99px;
  align-items:center;justify-content:center;cursor:pointer;flex:none}
.bars{display:block;width:15px}
.bars span{display:block;height:1.5px;background:var(--text);border-radius:2px}
.bars span+span{margin-top:4px}
.nav-menu{pointer-events:auto;max-width:1180px;margin:8px auto 0;padding:14px 18px 18px;
  border-radius:20px;border:1px solid var(--line-2);background:rgba(13,18,24,.96);
  backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  display:flex;flex-direction:column;gap:2px}
.nav-menu[hidden]{display:none}
.nav-menu a{font-family:var(--mono);font-size:12px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--muted);display:flex;align-items:center;min-height:44px;padding:4px 2px;
  border-bottom:1px solid var(--line)}
.nav-menu a:hover{color:var(--text)}
/* The CTA sits at the foot of the panel, so on a full-height menu the two ends of the sheet are
   the brand and the ask rather than a stack that stops half way down the hero. */
.nav-menu .menu-cta{margin-top:auto;justify-content:center;min-height:48px}
/* An open menu has to cover the page, not float over it. The panel used to end where its own
   content did, which left the hero's own "Apply for a Free Build" button visible through the
   scrim directly under the menu's identical CTA: two stacked duplicate buttons, one of them
   greyed. Full height, an opaque panel, and a heavier scrim beneath it. */
.nav-menu{position:fixed;left:var(--pad);right:var(--pad);
  top:calc(var(--nav-inset) + var(--nav-h) + 8px);bottom:var(--nav-inset);
  margin:0;overflow-y:auto;background:rgba(11,15,20,.985)}
.nav-scrim{position:fixed;inset:0;z-index:90;background:rgba(5,7,10,.9);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.nav-scrim[hidden]{display:none}
body.nav-open{overflow:hidden}

/* ---------------- section formula ----------------
   Every section opens the same way and in the same place: eyebrow, serif
   headline, one grey subhead, centred in a 720px column. The grids and rows
   below stay full width, so the page reads as a centred spine with wide
   evidence hanging off it rather than as a left-hand column with a hole
   beside it. */
.sec{padding:var(--sec-pad) 0}
.head{max-width:720px;margin-left:auto;margin-right:auto;text-align:center;
  display:flex;flex-direction:column;align-items:center}
.head .display{margin-top:18px}
.head .eyebrow+.display{margin-top:20px}
.head .lead{margin-top:20px}
/* A flow section straight after a pinned act takes reduced padding: the stage needs a whole
   viewport to slide off, and full section padding on top of that gives the reader a second
   near-empty screen before anything arrives. A page states the two or three seams that matter
   most by id as well, so the value survives the act being demoted to flow. */
/* A statement beat is a breather, so it may carry a little more air than a proof beat: a 165px
   seam against the 150px between two proof sections. Never more, though; past about 180px the
   air stops reading as a pause and starts reading as a missing section. */
.sec-statement{padding-top:calc(var(--sec-pad) * 1.2)}
[data-sc-act="pin"]+.sec{padding-top:calc(var(--sec-pad) * .5)}
/* A pinned act is a length, not a box: its padding lives on its own stage. */
.sec[data-sc-act="pin"]{padding:0}

/* ---------------- how a middle pinned act ends ----------------
   Two elements, and between them they answer the two ways a pinned act goes wrong at its close.

   .act-in wraps everything the act's cues own, and fades from --sc-p over the last 6% of the
   travel. Before this, the last cue ramped out over the final fifth of the act: the closing line
   and the button under it were AUTHORED to sit at about 30% opacity for a fifth of the travel,
   which the walk photographed as a dead grey slab, and the argument still had to be dark well
   before the pin released. Now the argument holds to p = 0.94 and leaves with the stage it is
   standing on.

   .act-mark is the act's furniture: an eyebrow that carries no cue at all. A pinned stage is on
   screen for a viewport BEFORE its progress leaves 0 and a viewport AFTER it reaches 1, so an act
   whose every element is cued shows a bare stage across both. The mark is simply there, which is
   what makes the entry and the un-pin slide read as a seam rather than as a blank screen.

   .sc-stage is the class the engine adds to a pinned stage only, so ?static=1, no-JS and a
   demoted act keep the argument at full opacity; an unset --sc-p resolves the same way. */
.act-mark{display:flex;justify-content:center}
.sc-stage .act-in{opacity:clamp(0,calc((1 - var(--sc-p,0)) / .06),1)}

/* ---------------- frames ----------------
   Real product UI in a rounded dark frame with a hairline. Never an
   illustration, never an icon. */
.stage{margin:0;min-width:0}
.frame{position:relative;border:1px solid var(--line-2);border-radius:18px;overflow:hidden;
  background:var(--surface);box-shadow:var(--shadow)}
.frame::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;z-index:2;
  background:linear-gradient(90deg,transparent,rgba(244,241,234,.28) 22%,rgba(244,241,234,.28) 62%,transparent)}
.frame img,.frame video{display:block;width:100%;height:auto}
.stage.wrap{margin-left:auto;margin-right:auto;margin-top:auto;padding-top:52px;perspective:1500px}
.stage-mid{max-width:1100px;margin:52px auto 0}
.cap{margin-top:18px;text-align:center;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--dim)}
/* A still frame cut to its own aspect: the picture fills it and the window is made by the
   scale below rather than by spare height. */
.frame-still{aspect-ratio:16/10}
.frame-still img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}

/* ---------------- grids: the proof cards ----------------
   A card crops its screenshot rather than shrinking it: the UI has to stay
   legible, so the image sits larger than the window and the window shows the
   part that carries the claim. */
.trio{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;margin-top:56px}
.pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px;margin-top:56px}
.card{display:flex;flex-direction:column;min-width:0;background:var(--surface);
  border:1px solid var(--line);border-radius:20px;overflow:hidden;padding:26px 26px 0}
.card h3{color:var(--text)}
.card>p{margin-top:8px;color:var(--muted);font-size:14.5px;line-height:1.55;text-wrap:pretty}
.shot{position:relative;margin:24px -1px -1px;flex:1 1 auto;min-height:0;overflow:hidden;
  border-top:1px solid var(--line);background:var(--ink)}
.shot img{position:absolute;top:var(--sy,0);left:var(--sx,0);width:var(--zoom,180%);max-width:none;height:auto}
/* Each crop is chosen so the claim above it is the thing you can actually see: the
   transcript rows, the "source offline / playing proxy" badge, the project grid. These are
   crops of the shared product screenshots under /free-build/, so every page gets them. */
.shot-search{aspect-ratio:4/3;--zoom:280%;--sx:-50%}
.shot-detail{aspect-ratio:4/3;--zoom:210%;--sx:-100%;--sy:-5%}
.shot-projects{aspect-ratio:4/3;--zoom:230%;--sx:-38%;--sy:-2%}
.shot-library{aspect-ratio:16/10;--zoom:172%;--sx:0;--sy:0}
.card-plain{padding-bottom:26px}
/* The text-only half of a pair. Three labelled rows start directly under the subhead and
   share the panel's spare height between them, so the panel fills the way the screenshot beside
   it does instead of leaving a band of nothing above the rules. The basis is auto, not 0: with a
   zero basis the rows split the height equally and a wrapped row overflowed on phones. */
.rules{margin-top:26px;display:flex;flex-direction:column;flex:1 1 auto;
  border-top:1px solid var(--line)}
.rules li{flex:1 1 auto;padding:20px 0 22px;display:flex;flex-direction:column;
  justify-content:flex-start}
.rules li+li{border-top:1px solid var(--line)}
.rules li:last-child{padding-bottom:0}
.rules .rk{font-family:var(--mono);font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--text);line-height:1.4}
.rules li p{margin-top:9px;color:var(--muted);font-size:14.5px;line-height:1.55;
  max-width:38ch;text-wrap:pretty}

/* ---------------- inline CTA ---------------- */
.inline-cta{display:flex;align-items:center;gap:20px;flex-wrap:wrap;margin-top:44px}
.inline-cta .fine{text-align:left}
.inline-cta-centre{justify-content:center;flex-direction:column;gap:14px}
.inline-cta-centre .fine{text-align:center}

/* ---------------- steps ---------------- */
.steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:32px;margin-top:56px;
  border-top:1px solid var(--line)}
.steps li{padding-top:22px;min-width:0}
.steps b{display:block;margin-top:14px;font-size:19px;font-weight:600;letter-spacing:-.02em;line-height:1.2}
.steps p{margin-top:10px;color:var(--muted);font-size:14.5px;line-height:1.55;text-wrap:pretty}
/* The qualifying line sits back on the section's centred spine, under the grid it qualifies. */
.fit{margin:56px auto 0;padding-top:32px;border-top:1px solid var(--line);color:var(--muted);
  font-size:clamp(17px,1.7vw,21px);line-height:1.5;letter-spacing:-.01em;max-width:60ch;
  text-align:center;text-wrap:pretty}
.fit em{font-style:normal;color:var(--text)}

/* ---------------- more cards ---------------- */
.more{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;margin-top:56px;
  border-top:1px solid var(--line)}
.mcard{padding-top:26px;min-width:0;display:flex;flex-direction:column;align-items:flex-start}
.mcard h3{margin-top:16px}
.mcard p{margin-top:8px;color:var(--muted);font-size:14.5px;line-height:1.55;text-wrap:pretty}
.mcard .mlink{margin-top:auto;padding-top:22px}

/* ---------------- faq ---------------- */
.faq{max-width:720px;margin:48px auto 0}
details.q{border-bottom:1px solid var(--line)}
details.q summary{list-style:none;cursor:pointer;padding:22px 0;display:flex;justify-content:space-between;
  gap:24px;align-items:center;font-size:17px;font-weight:500;letter-spacing:-.015em;
  transition:color .18s}
details.q summary::-webkit-details-marker{display:none}
details.q summary:hover{color:var(--text)}
details.q summary .pm{position:relative;width:15px;height:15px;flex:none}
details.q summary .pm::before,details.q summary .pm::after{content:"";position:absolute;
  background:var(--muted);border-radius:2px;transition:transform .25s,opacity .25s}
details.q summary .pm::before{left:0;right:0;top:7px;height:1.5px}
details.q summary .pm::after{top:0;bottom:0;left:7px;width:1.5px}
details.q[open] summary .pm::after{transform:rotate(90deg);opacity:0}
details.q .ans{color:var(--muted);font-size:15px;line-height:1.6;max-width:66ch;padding:0 0 24px}

/* ---------------- footer ---------------- */
/* The footer's own top pad is half the gap a reader sees between a page's last line and its first
   footer line, so it is part of a page's own last beat rather than the footer's. 44px desktop,
   34px on a phone. align-items:start stops the brand column stretching to the height of a nine
   item link list and painting a column of nothing beside it. */
footer{border-top:1px solid var(--line);padding:44px 0 40px}
.ft-top{display:flex;justify-content:space-between;align-items:start;gap:48px;flex-wrap:wrap}
.ft-brand{max-width:34ch}
.ft-logo{width:120px;height:auto;opacity:.9}
.ft-brand p{margin-top:18px;color:var(--muted);font-size:14.5px;line-height:1.6}
.ft-cols{display:flex;gap:72px;flex-wrap:wrap}
.ft-cols div{display:flex;flex-direction:column;gap:11px}
.ft-h{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);margin-bottom:5px}
.ft-cols a{font-size:14.5px;color:var(--muted);transition:color .18s}
.ft-cols a:hover{color:var(--text)}
.ft-base{margin-top:64px;padding-top:24px;border-top:1px solid var(--line);
  display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;
  font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;color:var(--dim)}

/* ---------------- application modal ---------------- */
.modal{position:fixed;inset:0;z-index:120;display:none;align-items:center;justify-content:center;padding:24px}
.modal.open{display:flex}
.modal-scrim{position:absolute;inset:0;background:rgba(5,7,10,.78);backdrop-filter:blur(6px)}
/* dvh, not vh: on a phone vh is the tallest the viewport ever gets, so a 90vh card in a browser
   showing its own toolbar is already taller than the space it has. The card scrolls inside
   itself, so the submit is always reachable at any height. */
.modal-card{position:relative;z-index:2;width:100%;max-width:560px;
  max-height:min(90vh,calc(100dvh - 48px));overflow-y:auto;overscroll-behavior:contain;
  background:var(--surface);border:1px solid var(--line-2);border-radius:18px;
  box-shadow:0 50px 130px -40px rgba(0,0,0,.95)}
.modal-head{padding:26px 28px 0;position:relative}
.modal-head .mk{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--blue)}
.modal-head h3{font-size:23px;margin:14px 0 8px;letter-spacing:-.02em}
.modal-head p{color:var(--muted);font-size:14.5px;line-height:1.5}
.modal-close{position:absolute;top:20px;right:20px;z-index:3;width:32px;height:32px;
  border:1px solid var(--line-2);border-radius:9px;color:var(--muted);font-size:16px;cursor:pointer;
  transition:color .18s,border-color .18s}
.modal-close:hover{color:var(--text);border-color:var(--muted)}
.mform{padding:22px 28px 28px;display:flex;flex-direction:column;gap:14px}
.hv-step{display:flex;flex-direction:column;gap:14px}
.hv-step[hidden]{display:none}
.frow2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.field{display:flex;flex-direction:column;gap:6px}
.field label{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
.field label .req{color:var(--blue)}
.field input,.field textarea,.field select{background:var(--ink);border:1px solid var(--line-2);
  border-radius:9px;padding:12px 14px;color:var(--text);font-family:var(--sans);font-size:15px;width:100%}
.field textarea{resize:vertical;min-height:84px;line-height:1.5}
/* The native select was the loudest inconsistency on the most important surface on the site: the
   browser's own face, weight and chevron sitting beside a designed text input. Same surface, same
   face, our own chevron. appearance:none only removes the paint; the control stays a real
   <select>, so it keeps its keyboard behaviour, its focus ring and its platform picker. */
.field select{appearance:none;-webkit-appearance:none;-moz-appearance:none;
  padding-right:38px;line-height:1.35;cursor:pointer;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 9' fill='none' stroke='%237D8896' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 7 7.5 13 1.5'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 14px center;background-size:12px 8px}
.field select:hover{border-color:var(--muted)}
.field select::-ms-expand{display:none}
.field select option{background:var(--ink);color:var(--text)}
.field input[aria-invalid="true"],.field select[aria-invalid="true"]{border-color:rgba(201,107,90,.7)}
input[name="website"]{position:absolute;left:-9999px;opacity:0;height:1px;width:1px}
/* The optional half of the application, behind a native disclosure. The form is one step and one
   POST, so these four questions must never look like a gate: a hairline rule and a mono label,
   closed on open, so the submit stays above the fold on a 390x844 phone. */
.mmore{border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.mmore>summary{list-style:none;cursor:pointer;padding:13px 0;display:flex;justify-content:space-between;
  gap:20px;align-items:center;font-family:var(--mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);transition:color .18s}
.mmore>summary::-webkit-details-marker{display:none}
.mmore>summary:hover{color:var(--text)}
.mmore>summary .pm{position:relative;width:13px;height:13px;flex:none}
.mmore>summary .pm::before,.mmore>summary .pm::after{content:"";position:absolute;
  background:var(--muted);border-radius:2px;transition:transform .25s,opacity .25s}
.mmore>summary .pm::before{left:0;right:0;top:6px;height:1.5px}
.mmore>summary .pm::after{top:0;bottom:0;left:6px;width:1.5px}
.mmore[open]>summary .pm::after{transform:rotate(90deg);opacity:0}
.mmore .hv-step{padding:2px 0 18px}
.err{font-family:var(--mono);font-size:11px;color:#d98b7a;display:none;line-height:1.5}
.err.show{display:block}
/* The submit row rides the bottom of the scrolling sheet rather than sitting at the end of it.
   At 844x390 the card is 340px tall and the submit sat at y=381: reachable by scrolling, with
   nothing on screen saying so. Sticky keeps the control and the fine line under it in view at
   every height. The negative margins take the bar out to the card's own edges so its ground
   covers the full width of what passes under it, and the top edge only draws once there really
   is content beneath (folio.js writes .is-lifted), so a form that fits shows no false seam. */
.msticky{position:sticky;bottom:0;z-index:2;display:flex;flex-direction:column;gap:10px;
  margin:4px -28px -28px;padding:14px 28px calc(14px + env(safe-area-inset-bottom));
  background:var(--surface);border-top:1px solid transparent;
  transition:border-color .2s,box-shadow .2s}
.msticky.is-lifted{border-top-color:var(--line);box-shadow:0 -18px 30px -20px rgba(0,0,0,.95)}
.msticky .fine{text-align:center}
.form-actions{display:flex;gap:10px;align-items:center;justify-content:flex-end;margin-top:0}
.form-actions .btn-primary{flex:1;justify-content:center}
.form-actions .btn[hidden]{display:none}
.mform .fine{text-align:center}
.msuccess{padding:44px 32px 40px;text-align:center;display:none}
.msuccess.show{display:block}
.msuccess .chk{width:48px;height:48px;border-radius:50%;border:1px solid rgba(76,141,255,.4);
  color:var(--blue);display:flex;align-items:center;justify-content:center;font-size:22px;margin:0 auto 20px}
.msuccess h3{font-size:22px}
.msuccess p{color:var(--muted);font-size:14.5px;line-height:1.5;max-width:360px;margin:10px auto 22px}
body.modal-lock{overflow:hidden}

/* ---------------- sticky mobile CTA ---------------- */
.hv-sticky{position:fixed;left:0;right:0;bottom:0;z-index:75;
  padding:10px var(--pad) max(10px,env(safe-area-inset-bottom));
  background:rgba(11,15,20,.94);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border-top:1px solid var(--line-2)}
.hv-sticky .btn{width:100%;justify-content:center;min-height:46px}
.hv-sticky[hidden]{display:none}
@media(min-width:761px){.hv-sticky{display:none}}
body.sticky-on{padding-bottom:70px}
@media(min-width:761px){body.sticky-on{padding-bottom:0}}

/* ---------------- motion ----------------
   A page has exactly one reveal system: the engine's. data-sc-in fires once on entry,
   data-sc-cue is keyed to an act's progress. Nothing carries both.

   The engine's reveal easing is close enough to this system's; the only thing worth restating
   is the duration, so an entrance still feels like the rest of the page. */
[data-sc-in],[data-sc-stagger]>*{
  transition-duration:.62s;transition-timing-function:var(--ease-out)}

/* --- the window ---
   A frame is a window onto a taller screenshot, and the screenshot travels inside it as the
   frame crosses the viewport. --w-from and --w-to are the two authored crops in px; folio.js
   clamps both to the real overflow and writes --w-y, so a window that does not overflow simply
   never moves. Transform only, and --w-y unset (reduced motion, no script) leaves the first
   authored crop exactly where it is. */
.shot img,.frame-window img{transform:translate3d(0,var(--w-y,0px),0)}
.frame-window{aspect-ratio:16/8.6}
.frame-window picture{display:block}
.frame-window img{--w-from:0px;--w-to:-84px}
.shot-search{--w-from:0px;--w-to:-104px}
.shot-detail{--w-from:0px;--w-to:-112px}
.shot-projects{--w-from:0px;--w-to:-92px}
.shot-library{--w-from:0px;--w-to:-96px}
/* A screenshot cut to its frame exactly has no spare height, so the window has to be made: a
   small scale gives the picture room to travel without ever showing an edge. The scale costs
   width as well as height, so it stays small; 1.05 still leaves more than thirty pixels of
   vertical travel once both crops are clamped to the real overflow. A page that wants no pan at
   all restates --w-s:1 on its own frame, which removes the overflow the pan needs. */
.frame-still .media{position:absolute;inset:0;transform-origin:50% 50%;
  --w-s:1.05;--w-from:16px;--w-to:-16px;
  transform:translate3d(0,var(--w-y,0px),0) scale(var(--w-s,1))}

/* If the script never runs, or a capture tool asks for the static page, nothing may stay
   invisible and nothing may sit mid-animation. A page adds its own line here for any element
   it moves itself. */
html.no-js [data-sc-cue],html.hv-static [data-sc-cue],
html.no-js [data-sc-in],html.hv-static [data-sc-in],
html.no-js [data-sc-stagger]>*,html.hv-static [data-sc-stagger]>*{
  opacity:1;transform:none;transition:none}
html.no-js [data-sc-reveal],html.hv-static [data-sc-reveal]{clip-path:none}
html.no-js .frame-still .media,html.hv-static .frame-still .media{transform:none}

/* ---------------- reduced motion ----------------
   Fewer and gentler, not zero: the engine keeps the opacity that carries comprehension and
   drops every position change. These are the shared layer's own position changes. */
@media(prefers-reduced-motion:reduce){
  .btn,.mlink,details.q summary,.mmore>summary{transition:none}
  .frame-still .media{transform:none}
}

/* ---------------- responsive ---------------- */
@media(max-width:1120px){
  :root{--rhythm:112px;--sec-pad:64px}
  .nav-links{gap:20px}
  .nav-links a{font-size:10.5px;letter-spacing:.1em}
  .trio,.pair,.more{gap:20px}
}
@media(max-width:940px){
  .nav-links{display:none}
  .nav-toggle{display:flex}
  .nav-pill{padding-right:8px}
  .nav-right .btn{padding:0 14px}
  .steps{grid-template-columns:1fr 1fr;gap:28px 24px}
}
@media(max-width:900px){
  .pair{grid-template-columns:1fr}
  .shot-library{aspect-ratio:16/9;--zoom:150%}
  .card-plain .rules{padding-top:26px}
}
@media(max-width:760px){
  :root{--pad:20px;--rhythm:88px;--sec-pad:48px;--nav-inset:14px;--nav-h:56px}
  /* On a phone the pill holds a wordmark, a CTA and a toggle in 350px. A one-word CTA still fits
     ("Apply", "Start", "Download"); anything longer pushed the toggle to within 2px of the pill's
     own rounded corner, which clipped its hit area. So a longer label stands down here and the
     menu carries the ask in full, one tap away, with the toggle keeping its own margin. */
  .nav-pill{padding-right:12px}
  .nav-right .btn.nav-cta-long{display:none}
  h1.display{max-width:none}
  .stage.wrap{padding-top:38px}
  .stage-mid{margin-top:36px}
  .trio,.more{grid-template-columns:1fr;gap:16px}
  .more{border-top:0;gap:0}
  .mcard{padding:24px 0;border-top:1px solid var(--line)}
  .card{padding:22px 22px 0;border-radius:16px}
  .shot{margin-top:20px}
  /* On a phone a whole 1600px console is unreadable at 335px: a frame crops to the column that
     carries the claim instead of shrinking the entire application to a smudge. */
  .frame-still{aspect-ratio:4/5}
  /* The same 2.2x phone crop the hero takes: a 1600px console scaled into 348px renders every
     label at four or five pixels, so the still frames the results column rather than the whole
     application. */
  .frame-still .media{--w-s:2.2;transform-origin:6% 31%}
  .frame-still img{object-position:50% 0}
  /* transform:none cancels the pan under no-JS, ?static=1 and reduced motion. On a phone it would
     cancel the crop with it, and the crop is legibility rather than motion, so the scale comes
     back here without the pan. */
  html.no-js .frame-still .media,html.hv-static .frame-still .media{transform:scale(var(--w-s,1))}
  .shot-library{aspect-ratio:4/3;--zoom:210%;--sx:-2%;--w-to:-64px}
  .shot-search{--zoom:240%;--sx:-36%;--w-to:-68px}
  .shot-detail{--zoom:190%;--sx:-88%;--w-to:-72px}
  .shot-projects{--zoom:200%;--sx:-28%;--w-to:-60px}
  .frame-window{aspect-ratio:16/9.2}
  .frame-window img{--w-to:-46px}
  .head .display{margin-top:14px}
  .head .lead{margin-top:16px}
  .trio,.pair,.steps,.faq{margin-top:38px}
  .inline-cta{margin-top:32px;flex-direction:column;align-items:flex-start;gap:14px}
  .inline-cta .btn{width:100%;justify-content:center;min-height:48px}
  .inline-cta-centre{align-items:center}
  .fit{margin-top:38px;padding-top:26px}
  .frame,.frame-still{border-radius:12px}
  footer{padding-top:34px}
  .ft-top{gap:34px}
  .ft-cols{gap:44px}
  .ft-base{margin-top:44px;flex-direction:column;gap:8px}
}
@media(max-width:560px){
  .steps{grid-template-columns:1fr;gap:0;border-top:0}
  .steps li{padding:20px 0;border-top:1px solid var(--line)}
  .steps b{margin-top:8px}
  .modal{padding:0;align-items:flex-end}
  .modal-card{max-height:100dvh;border-radius:18px 18px 0 0}
}
/* A short viewport is a phone held sideways, and there the card is taller than the screen at any
   sensible max-height. It becomes a full sheet with its own scroll, so every field and the submit
   are reachable rather than the top four fields being all there is. */
@media(max-height:560px){
  .modal{padding:0;align-items:stretch}
  .modal-card{max-width:none;max-height:100dvh;border-radius:0;border-left:0;border-right:0}
  .modal-head{padding-top:20px}
  .mform{padding-bottom:20px}
  /* the sticky bar bleeds to the sheet's bottom edge, so it cancels whatever padding is under it */
  .msticky{margin-bottom:-20px;padding-top:11px;padding-bottom:calc(11px + env(safe-area-inset-bottom))}
}
@media(max-width:760px) and (prefers-reduced-motion:reduce){
  .frame-still .media{transform:scale(var(--w-s,1))}
}
@media(max-width:460px){
  .frow2{grid-template-columns:1fr}
  .brand-logo{width:108px}
}

/* ---------------- touch targets ----------------
   Every link and button that a finger has to land on gets a 44px hit area, whether or not the
   painted control is that tall. Most of these were between 16 and 22px: .mlink is the "see the
   product" path on every page and its hit box was the height of one line of 11.5px mono, and the
   footer's links were 22px tall rows laid out with 64px of visual spacing, so two thirds of the
   apparent target was dead ground. Padding where the control can grow, a ::before where it
   cannot, and never a change to what the eye sees. */
@media(pointer:coarse),(max-width:760px){
  .btn{min-height:44px}
  .nav-toggle{width:44px;height:44px}
  .brand{min-height:44px}
  .mlink{min-height:44px;padding:0 2px}
  .mcard .mlink{padding-top:22px;min-height:66px}
  .ft-cols div{gap:0}
  .ft-cols a{display:flex;align-items:center;min-height:44px}
  .ft-brand{max-width:none}
  details.q summary{min-height:44px}
  .mmore>summary{min-height:44px;padding:11px 0}
  .modal-close{width:44px;height:44px}
  /* A checkbox is a native control at its own size, so the row carrying it is the target. */
  .check-row{min-height:44px}
}

/* ---------------- type floors on a phone ----------------
   Body copy at 14.5px and captions at 10.5px are the desktop scale shrunk, not a phone scale.
   Body goes to 15.5px, mono captions to 11px, and the fine print, which is the line that carries
   the commercial terms, to 12px. --muted still marks secondary text; only the size moves. */
@media(max-width:760px){
  .card>p,.rules li p,.steps p,.mcard p,.ft-brand p,.ft-cols a,
  .modal-head p,.msuccess p,.cap{font-size:15.5px}
  details.q .ans{font-size:15.5px}
  .num,.ft-h,.ft-base,.modal-head .mk,.field label,.chip{font-size:11px}
  .cap{font-size:11px;letter-spacing:.14em}
  .fine{font-size:12px}
  .field input,.field textarea,.field select{font-size:16px}
}

/* ---------------- the phone crop for a full console frame ----------------
   A .frame-window shows a whole 1600px application, and at 335px wide every label in it renders
   at four or five pixels: an illegible screenshot is worse than no screenshot. .frame-crop opts a
   window into the same crop mechanism .shot already uses, so a page can point the frame at the
   one region that carries its claim. The page authors --zoom / --sx / --sy under 760px; the pan
   keeps working, because the transform is unchanged and folio.js clamps it to the real overflow. */
@media(max-width:760px){
  .frame-crop{aspect-ratio:var(--crop-ar,4/3)}
  .frame-crop picture{position:absolute;inset:0}
  .frame-crop img{position:absolute;top:var(--sy,0);left:var(--sx,0);
    width:var(--zoom,180%);max-width:none;height:auto}
}

/* On a phone the page scrolls under the pill at reading size, so it is nearly solid there. */
@media(max-width:760px){.nav-pill{background:rgba(11,15,20,.94)}}
