/* ============================================================
   Torvane — The AI document agent · marketing site
   Design system. One CSS variable owns the brand gradient.
   ============================================================ */
:root{
  --bg:#0B101E; --bg-raised:#131A2A; --panel:#182234; --line:#2A3448;
  --text:#eef2f7; --muted:#93a0b4;
  --accent:#3B6FD4; --accent-2:#8FB4F0;
  --brand-gradient:linear-gradient(92deg,#3B6FD4,#8FB4F0);
  --emerald:#34c98e; --violet:#a78bfa; --amber:#f5b93e; --sky:#38bdf8; --teal:#2dd4bf;
  --maxw:1440px;
  --radius:16px;
  --font:'Inter','Inter Fallback',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --mono:ui-monospace,'SF Mono','Cascadia Code',monospace;

  /* ---- phase 15: one easing, one rhythm, one set of surfaces ----
     Every transition on this site uses --ease (or --ease-out for hovers).
     Nothing bounces; motion decelerates into place. */
  --ease:cubic-bezier(.16,1,.3,1);
  --ease-out:cubic-bezier(.22,.61,.36,1);
  --dur-rise:620ms;      /* scroll reveals */
  --dur-ui:280ms;        /* nav / panel state changes */
  --dur-micro:180ms;     /* hover, press */

  /* depth over borders: layered surfaces instead of flat 1px boxes */
  --surface:linear-gradient(180deg,rgba(255,255,255,.035),rgba(255,255,255,0) 46%),var(--panel);
  --surface-raised:linear-gradient(180deg,rgba(255,255,255,.055),rgba(255,255,255,0) 52%),var(--bg-raised);
  --hairline:inset 0 1px 0 rgba(255,255,255,.07);
  --shadow-sm:0 1px 2px rgba(0,0,0,.35),0 8px 24px -14px rgba(0,0,0,.7);
  --shadow-md:0 2px 6px rgba(0,0,0,.35),0 24px 60px -28px rgba(0,0,0,.85);
  --shadow-lg:0 40px 100px -44px rgba(0,0,0,.9),0 8px 24px -12px rgba(0,0,0,.5);

  /* ---- one top offset below the fixed nav, shared by EVERY page ----
     .hero (landing, pricing, security, signup, login, auth pages) and .legal
     (privacy, cookies, terms, dpa, trust) both consume this, so the pages
     cannot drift apart. Tightened from the 19A value (108-168px) so the first
     content — and on the landing page both CTAs — is on screen immediately at
     1366x768 and larger; the space the hero gave up is added back BELOW the
     CTAs (the showpiece margin) so the stage stays under the fold. */
  --page-top:clamp(168px,17vh,204px);
}
*{box-sizing:border-box}
/* ---------- horizontal-overflow guard (19A) ----------
   Measured from truely.uk, which holds scrollWidth === innerWidth at every
   width even while carrying elements wider than the viewport. Three parts,
   and all three are needed:

   1. `overflow-x: clip` on BOTH html and body. `clip` rather than `hidden`
      because `hidden` turns the element into a scroll container, which breaks
      `position: sticky` descendants and can hijack scroll anchoring. `clip`
      just clips.
   2. `max-width: 100vw` on body, so a wide child cannot stretch the box
      itself — clipping alone does not stop the layout box growing.
   3. `min-width: 0` on the flex/grid children that would otherwise refuse to
      shrink below their content (see .hero__lede and the legal grid).

   The point is NOT to hide content: everything visible stays reachable. It is
   to stop a deliberately oversized decorative element — the hero showpiece,
   the aurora, the final-CTA glow — from producing a horizontal scrollbar. */
/* text-size-adjust:100% stops iOS Safari and Chrome-on-Android applying their
   own font "boosting" — they inflate type inside blocks they judge too narrow,
   which is the classic cause of a mobile page that reads as zoomed in while the
   viewport meta is perfectly correct. Absent until now; the desktop emulator
   never applies boosting, which is why it never showed up in testing here. */
html{scroll-behavior:smooth;scroll-padding-top:90px;overflow-x:hidden;overflow-x:clip;max-width:100vw;-webkit-text-size-adjust:100%;text-size-adjust:100%}
body{margin:0;background:var(--bg);color:var(--text);font-family:var(--font);-webkit-font-smoothing:antialiased;line-height:1.5;
  overflow-x:clip;max-width:100vw;position:relative}
h1,h2,h3{letter-spacing:-.02em;margin:0}
p{margin:0}
a{color:var(--accent);text-decoration:none;transition:color .2s ease}
a:hover{color:#7ea8ff}
code{font-family:var(--mono)}
::selection{background:rgba(143,180,240,.35)}
img{max-width:100%;display:block}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 clamp(20px,3.4vw,40px)}
.grad-text{background:var(--brand-gradient);-webkit-background-clip:text;background-clip:text;color:transparent}
.mono{font-family:var(--mono)}

/* ---------- ambient atmosphere ---------- */
.dotgrid{position:fixed;inset:0;z-index:0;pointer-events:none;background-image:radial-gradient(rgba(147,160,180,.14) 1px,transparent 1px);background-size:34px 34px;mask-image:radial-gradient(ellipse 80% 60% at 50% 0%,#000,transparent 75%);-webkit-mask-image:radial-gradient(ellipse 80% 60% at 50% 0%,#000,transparent 75%)}

/* ---------- nav (floating glass capsule that condenses on scroll) ----------
   Two states. At rest the pill is roomy and nearly transparent; once scrolled
   it tightens (less outer padding, less inner height, tighter radius), the
   glass darkens so content passing underneath stays legible, and a hairline
   divider fades in. Everything moves on the one easing curve. */
.nav{position:fixed;top:0;left:0;right:0;z-index:50;padding:18px 18px;transition:padding var(--dur-ui) var(--ease)}
/* The consent banner sits at z-200 and should stay above ordinary page content.
   But an explicitly-opened mobile menu is a deliberate act, so it outranks the
   banner for as long as it is open — otherwise the sheet opens underneath it. */
.nav.open{z-index:210}
.nav__in{position:relative;max-width:1440px;margin:0 auto;display:flex;align-items:center;gap:var(--space-5);height:60px;padding:0 10px 0 22px;border-radius:20px;border:1px solid rgba(255,255,255,.07);background:rgba(16,21,29,.44);backdrop-filter:saturate(180%) blur(22px);-webkit-backdrop-filter:saturate(180%) blur(22px);box-shadow:var(--shadow-sm),var(--hairline);transition:height var(--dur-ui) var(--ease),background var(--dur-ui) var(--ease),box-shadow var(--dur-ui) var(--ease),border-color var(--dur-ui) var(--ease),border-radius var(--dur-ui) var(--ease)}
/* the divider: a hairline under the pill, revealed only once scrolled */
.nav__in::after{content:"";position:absolute;left:14px;right:14px;bottom:-1px;height:1px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.13),transparent);opacity:0;transition:opacity var(--dur-ui) var(--ease)}
.nav.scrolled{padding:10px 18px}
.nav.scrolled .nav__in{height:52px;border-radius:17px;background:rgba(10,13,18,.78);border-color:rgba(255,255,255,.09);box-shadow:var(--shadow-md),var(--hairline)}
.nav.scrolled .nav__in::after{opacity:1}

.brandmark{font-weight:750;font-size:var(--text-lead);letter-spacing:-.035em;color:var(--text);display:flex;align-items:center;z-index:2;transition:opacity var(--dur-micro) var(--ease-out)}
.brandmark:hover{opacity:.78}

.nav__links{position:absolute;left:50%;transform:translateX(-50%);display:flex;gap:var(--space-1)}
/* crisp active state: a soft pill, not an underline. Hover fills the same
   shape at lower strength so the two states read as one system. */
.navlink{position:relative;color:var(--muted);font-size:14.5px;font-weight:500;letter-spacing:-.011em;padding:var(--space-2) 14px;border-radius:var(--radius-ctl-sm);background:rgba(255,255,255,0);transition:color var(--dur-micro) var(--ease-out),background-color var(--dur-micro) var(--ease-out)}
.navlink:hover{color:var(--text);background:rgba(255,255,255,.06)}
.navlink[aria-current="page"]{color:var(--text);font-weight:560;background:rgba(255,255,255,.075);box-shadow:var(--hairline)}
/* a 2px gradient tick under the current page — the one bit of accent up here */
.navlink[aria-current="page"]::after{content:"";position:absolute;left:50%;transform:translateX(-50%);bottom:2px;width:16px;height:2px;border-radius:2px;background:var(--brand-gradient)}

.nav__cta{margin-left:auto;display:flex;gap:var(--space-2);align-items:center;z-index:2}
.nav__toggle{display:none;position:relative;width:40px;height:40px;background:none;border:0;cursor:pointer;padding:0;margin-left:auto;z-index:60;border-radius:11px;transition:background var(--dur-micro) var(--ease-out)}
.nav__toggle:hover{background:rgba(255,255,255,.06)}
.nav__toggle span{position:absolute;left:11px;display:block;width:18px;height:1.5px;background:var(--text);border-radius:2px;transition:transform var(--dur-ui) var(--ease),opacity 120ms linear}
.nav__toggle span:nth-child(1){top:14px}
.nav__toggle span:nth-child(2){top:19.25px}
.nav__toggle span:nth-child(3){top:24.5px}
/* burger → X */
.nav.open .nav__toggle span:nth-child(1){transform:translateY(5.25px) rotate(45deg)}
.nav.open .nav__toggle span:nth-child(2){opacity:0}
.nav.open .nav__toggle span:nth-child(3){transform:translateY(-5.25px) rotate(-45deg)}

/* ---------- buttons ---------- */
.btn{font-size:15.5px;font-weight:600;letter-spacing:-.01em;border-radius:var(--radius-pill);padding:13px 26px;display:inline-flex;align-items:center;gap:9px;cursor:pointer;
  transition:transform var(--dur-micro) var(--ease-out),border-color var(--dur-micro) var(--ease-out),box-shadow var(--dur-micro) var(--ease-out),background var(--dur-micro) var(--ease-out);white-space:nowrap}
.btn:active{transform:translateY(1px) scale(.99)}
/* background-origin:border-box is load-bearing, not decoration. The border is
   transparent so primary and ghost buttons measure the same, but a gradient
   is ORIGINED to the padding box while being CLIPPED to the border box, and
   outside its origin box it repeats — so the 1px border tiled the light end
   of the gradient and every blue button wore a pale sliver down its left
   edge. Reported 2 Sep 2026. */
.btn--primary{color:#fff;background:var(--brand-gradient);background-origin:border-box;
  border:1px solid transparent;box-shadow:0 0 0 rgba(143,180,240,0)}
.btn--primary:hover{color:#fff;transform:translateY(-1px);box-shadow:0 12px 38px rgba(143,180,240,.42),inset 0 1px rgba(255,255,255,.28)}
.btn--ghost{color:var(--text);background:var(--surface);border:1px solid rgba(255,255,255,.09);box-shadow:var(--hairline)}
.btn--ghost:hover{color:var(--text);transform:translateY(-1px);border-color:rgba(255,255,255,.16);box-shadow:var(--hairline),var(--shadow-sm)}
.btn--text{color:var(--text);background:none;border:0;padding:14px 10px;gap:6px}
.btn--text .arw{transition:transform .2s ease;color:var(--accent)}
.btn--text:hover{color:#fff}
.btn--text:hover .arw{transform:translateX(4px)}
.btn--sm{font-size:var(--text-meta-lg);padding:9px 17px}
.btn--block{width:100%;justify-content:center}

/* ---------- generic ---------- */
/* purposeful rhythm: ~120px desktop, clamping down on mobile. The divider is
   a soft gradient rather than a hard 1px rule — depth over borders. */
.section{padding:clamp(56px,6vw,88px) 0;position:relative;z-index:1;border-top:1px solid transparent;
  border-image:linear-gradient(90deg,transparent,rgba(255,255,255,.09),transparent) 1}
.eyebrow{display:inline-block;font-family:var(--mono);font-size:var(--text-small);font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--accent)}
.eyebrow--pill{font-family:var(--mono);border:1px solid rgba(255,255,255,.09);background:var(--surface);
  box-shadow:var(--hairline);padding:7px 15px;border-radius:var(--radius-pill);letter-spacing:.18em}
.h-section{font-size:clamp(28px,3.1vw,42px);font-weight:760;line-height:1.07;letter-spacing:-.03em;max-width:900px;text-wrap:balance}
.intro{font-size:var(--text-body-lg);line-height:1.6;color:var(--muted);max-width:620px;margin-top:var(--space-4);text-wrap:pretty}
.card{border:1px solid rgba(255,255,255,.065);background:var(--surface);border-radius:var(--radius);
  box-shadow:var(--hairline),var(--shadow-sm);padding:28px}
.card--lift{transition:transform var(--dur-ui) var(--ease),box-shadow var(--dur-ui) var(--ease),border-color var(--dur-ui) var(--ease)}
.card--lift:hover{transform:translateY(-3px);border-color:rgba(255,255,255,.11);box-shadow:var(--hairline),var(--shadow-md)}
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-5)}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-5)}

/* ---------- hero (centered, Apple-style) ---------- */
/* The fold is the brief here: eyebrow + headline + subhead + BOTH CTAs must be
   visible without scrolling at 1366x768 (the tightest common laptop) and above.
   Top padding is viewport-relative rather than a flat 190px — the nav pill ends
   at ~78px, so the floor of 112px is deliberate breathing room, not a gap. */
/* ---------- hero (19A) ----------
   Vertical rhythm modelled on truely.uk, measured rather than guessed: its nav
   ends at ~78px (the same as ours) and its headline starts at 218px — about
   140px of clear air below the nav — and it pushes content DOWN further on
   mobile (238px), not up. That generous top space is what makes the headline
   feel placed rather than crammed under the nav.

   The whole hero — eyebrow, headline, subhead and BOTH CTAs — must clear the
   fold at 1366x768. The showpiece then starts BELOW it: the stage animation is
   driven by an IntersectionObserver, so keeping it off the first screen means
   a visitor always sees it play from the beginning rather than arriving at a
   half-finished frame. */
.hero{position:relative;padding:var(--page-top) 24px clamp(56px,6vh,88px);z-index:1;text-align:center;
  overflow:clip}
/* The split hero is the HOMEPAGE only. `class="hero"` is on eleven pages —
   pricing, security, login, signup, the error pages — and all of them are
   centred single-column headers that must stay that way. */
.hero--split{text-align:left;padding-bottom:clamp(48px,6vh,80px);--page-top:clamp(120px,13vh,150px)}
/* Asymmetric two-column (20D). Was a centred stack with the showpiece BELOW
   the fold by design — the note further down explains the old reasoning, which
   was that the scroll-triggered animation should always play from frame one.
   That trade cost more than it bought: measured at 1280x800 only 20% of the
   visual cleared the fold, and 34% at 900px, so the one piece of concrete
   product evidence on the page was a sliver. The animation now fires on load
   instead, which the observer already handles because the stage is in view at
   first paint. */
.hero__grid{max-width:1440px;margin:0 auto;position:relative;z-index:1;
  display:grid;grid-template-columns:minmax(0,0.88fr) minmax(0,1.12fr);
  gap:clamp(32px,4vw,64px);align-items:center}
.hero__aurora{position:absolute;top:-200px;left:50%;transform:translateX(-50%);width:1200px;height:900px;max-width:140vw;z-index:0;pointer-events:none;opacity:.55;filter:blur(70px);border-radius:50%;background:conic-gradient(from 90deg at 50% 50%,rgba(59,111,212,.3),rgba(143,180,240,.32),rgba(56,189,248,.24),rgba(143,180,240,.32),rgba(59,111,212,.3));animation:aurora 30s linear infinite}
/* 1160px, not 1060: at 98px the measure was capped by the container rather
   than by the 23ch rule, tipping 1920 into a FOURTH line while 1440 held three.
   Sizing the container to the type keeps the wrap consistent across desktops. */
.hero__lede{max-width:1440px;margin:0 auto;position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;
  min-width:0}
.hero--split .hero__lede{max-width:none;margin:0;align-items:flex-start}
/* Bigger and more confident. The measure is widened to 23ch so the extra size
   does not simply buy more lines — at 19ch this headline wrapped to four,
   which ate the fold and read as shouting. Ratios follow truely.uk:
   line-height 1.03, letter-spacing -0.035em. */
/* Sized for a COLUMN now, not a full-bleed centred measure. The old
   clamp(40,4.6vw,68) assumed the headline had the whole page to wrap in; in a
   ~46% column that produced six lines and pushed the CTAs past the fold. */
.hero h1{font-size:clamp(40px,4.6vw,68px);font-weight:780;line-height:1.03;letter-spacing:-.035em;
  margin:18px auto 0;max-width:24ch;text-wrap:balance}
.hero--split h1{font-size:clamp(38px,3.9vw,58px);line-height:1.04;letter-spacing:-.033em;margin:var(--space-4) 0 0;max-width:17ch}
.hero__sub{font-size:clamp(16px,1.1vw,19px);font-weight:400;color:var(--muted);line-height:1.55;max-width:600px;
  margin:var(--space-5) auto var(--space-6);text-wrap:pretty}
.hero--split .hero__sub{font-size:clamp(16.5px,1.3vw,19px);max-width:44ch;margin:18px 0 var(--space-6)}
.hero__ctas{display:flex;gap:var(--space-3);flex-wrap:wrap;justify-content:center;align-items:center}
.hero--split .hero__ctas{justify-content:flex-start}
/* Below the fold on a laptop, by design — see the note above. max-width is
   capped against the viewport so the oversized frame CLIPS instead of pushing
   the page sideways.

   The top margin is STRONGLY viewport-relative (34vh), because the taller the
   screen the more of the stage would otherwise show below the fixed hero text:
   at 1080px a 15vh gap left ~210px of the messy pile peeking, which reads as a
   half-shown panel rather than an inviting edge. 34vh (raised from 30vh when
   --page-top pulled the hero up ~30-50px) keeps the peek to a clean top edge
   (or just below the fold) from 1200px tall up to 1080p, while the floor stops
   it collapsing on shorter windows. Crucially, this margin sits BELOW the
   CTAs, so growing it never pushes them off-screen. */
.hero__showpiece{width:100%;max-width:100%;margin:0;position:relative;z-index:1;min-width:0}
/* A fixed min-height, so the card can never be squeezed by a short viewport,
   with the upper bound keeping it inside the fold at 800px tall. */
.hero__showpiece .stage{height:auto;min-height:clamp(430px,52vh,520px)}

/* chaos → order stage */
/* ONE bounded component (20D). The before and after used to be two
   independent floating groups — a pile absolutely positioned on the left, a
   panel absolutely positioned on the right — with nothing binding them. They
   are now two cells of one grid inside a single card, with an explicit
   transition device between them, so the story reads as one object. */
.stage{position:relative;height:auto;min-height:430px;border:1px solid var(--line);border-radius:22px;background:linear-gradient(180deg,var(--bg-raised),var(--panel));box-shadow:inset 0 1px rgba(255,255,255,.06),0 60px 120px -50px rgba(143,180,240,.5),0 30px 70px -40px rgba(0,0,0,.8);overflow:hidden;display:flex;flex-direction:column}
.stage__body{flex:1;display:grid;grid-template-columns:minmax(0,.82fr) auto minmax(0,1.34fr);
  gap:clamp(10px,1.4vw,18px);align-items:center;padding:clamp(16px,1.6vw,22px)}
/* the transition device: what actually connects before to after */
.stage__arrow{display:flex;flex-direction:column;align-items:center;gap:7px;color:var(--accent);
  font-family:var(--mono);font-size:var(--text-caption);letter-spacing:.14em;text-transform:uppercase;
  line-height:1.3;text-align:center;opacity:.9}
.stage__arrow svg{opacity:.85}
.stage__arrow span{color:var(--muted)}
/* the beam: a wide soft glow with a bright core, rather than a flat bar.
   It sweeps only the messy pile on the left — it is reading those files. */
.stage__beam{position:absolute;top:-8%;bottom:-8%;width:260px;left:-300px;pointer-events:none;z-index:3;opacity:.92;
  background:linear-gradient(90deg,transparent,rgba(143,180,240,.10) 28%,rgba(143,180,240,.30) 46%,rgba(150,180,255,.60) 50%,rgba(59,111,212,.30) 54%,rgba(59,111,212,.10) 72%,transparent);
  filter:blur(6px)}
/* messy document cards (left) */
/* position:static — the inline left/top coordinates in the markup are now
   inert, which is deliberate: they were tuned for a 1100px-wide stage and were
   what pushed ten of fourteen cards outside the card on any narrow viewport.
   The inline rotate() still applies, so the pile keeps its scatter. */
.stage__messy{display:flex;flex-direction:column;gap:clamp(7px,.9vw,11px);min-width:0}
.stage__messy .mcard:nth-of-type(n+6){display:none}
/* Four folders, not six. The card has to clear the fold at 800px tall and the
   library is the part that grows; four rows still reads as "a real filing
   system" and buys ~150px. */
.tree__row:nth-of-type(n+5){display:none}
.tree__scroll{justify-content:flex-start}
.mcard{position:static;width:100%;max-width:230px;display:flex;align-items:center;gap:10px;font-family:var(--mono);font-size:var(--text-caption);color:var(--muted);background:linear-gradient(180deg,var(--bg-raised),#0d1219);border:1px solid var(--line);border-radius:var(--radius-ctl-sm);padding:10px var(--space-3);box-shadow:var(--shadow-sm);will-change:transform,opacity;transition:transform 1.25s var(--ease),opacity .62s var(--ease-out);z-index:2}
.mcard .ic{flex:none;width:26px;height:32px;border-radius:4px;background:#1b2230;border:1px solid #2c3546;position:relative;overflow:hidden}
.mcard .ic::before{content:"";position:absolute;left:5px;right:5px;top:7px;height:2px;border-radius:2px;background:#3a4557;box-shadow:0 5px 0 #333f4f,0 10px 0 #2f3948,0 15px 0 #2a3240}
.mcard .ic::after{content:"";position:absolute;top:0;right:0;border-width:0 8px 8px 0;border-style:solid;border-color:transparent var(--panel) transparent transparent}
.mcard .nm{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* organized folder panel (right) */
.tree{position:static;width:auto;max-width:none;background:rgba(11,15,21,.66);border:1px solid var(--line);border-radius:var(--radius-card);padding:var(--space-4) 18px;z-index:2;box-shadow:0 30px 70px -40px #000,inset 0 1px rgba(255,255,255,.05);display:flex;flex-direction:column;overflow:hidden}
.tree__head{display:flex;align-items:center;gap:var(--space-2);padding-bottom:var(--space-3);margin-bottom:6px;border-bottom:1px solid var(--line);font-size:var(--text-small);font-weight:650;letter-spacing:.02em;white-space:nowrap;min-width:0}
.tree__head .count{white-space:nowrap}
.tree__head .dot{width:8px;height:8px;border-radius:50%;background:var(--brand-gradient)}
.tree__head .count{margin-left:auto;font-family:var(--mono);font-size:var(--text-caption);color:var(--muted)}
.tree__scroll{flex:1;overflow:hidden;display:flex;flex-direction:column;justify-content:space-between}
/* rows settle in place rather than sliding sideways — cleaner arrival */
.tree__row{margin-top:var(--space-3);opacity:0;transform:translateY(8px) scale(.985);transition:opacity var(--dur-rise) var(--ease),transform var(--dur-rise) var(--ease)}
.tree__folder{display:flex;align-items:center;gap:var(--space-2);font-size:var(--text-meta);font-weight:700;letter-spacing:.01em;margin-bottom:6px}
.foldic{flex:none;width:20px;height:15px;border-radius:2px 3px 3px 3px;background:currentColor;position:relative;box-shadow:0 2px 6px rgba(0,0,0,.4)}
.foldic::before{content:"";position:absolute;top:-4px;left:0;width:9px;height:5px;border-radius:3px 3px 0 0;background:currentColor}
.foldic::after{content:"";position:absolute;left:0;right:0;top:4px;height:1px;background:rgba(0,0,0,.28)}
.tree__folder .fcount{margin-left:auto;font-family:var(--mono);font-size:var(--text-caption);font-weight:500;color:var(--muted)}
.tree__file{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-1) 0 var(--space-1) var(--space-6);font-family:var(--mono);font-size:var(--text-caption);color:var(--muted)}
.tree__file .docic{flex:none;width:11px;height:14px;border-radius:2px;background:#2a3444;position:relative}
.tree__file .docic::after{content:"";position:absolute;top:0;right:0;border-width:0 4px 4px 0;border-style:solid;border-color:transparent var(--panel) transparent transparent}
.tree__file .fn{color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tree__file .fmeta{margin-left:auto;flex:none;font-size:var(--text-caption);color:var(--muted)}
.tree__row[data-accent="emerald"] .tree__folder{color:var(--emerald)}
.tree__row[data-accent="violet"] .tree__folder{color:var(--violet)}
.tree__row[data-accent="amber"] .tree__folder{color:var(--amber)}
.tree__row[data-accent="sky"] .tree__folder{color:var(--sky)}
.tree__row[data-accent="teal"] .tree__folder{color:var(--teal)}
.dupchip{opacity:0;margin-top:var(--space-3);display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-small);font-weight:600;color:var(--emerald);background:rgba(52,201,142,.1);border:1px solid rgba(52,201,142,.3);padding:6px var(--space-3);border-radius:var(--radius-pill);transition:opacity .5s ease;align-self:flex-start}
.dupchip i{width:7px;height:7px;border-radius:50%;background:var(--emerald)}
.stage__status{opacity:0;position:static;z-index:4;padding:13px clamp(16px,1.6vw,22px);font-size:var(--text-meta);font-weight:600;color:var(--text);background:rgba(9,12,17,.55);border-top:1px solid var(--line);display:flex;align-items:center;gap:10px;transition:opacity .6s ease}
.stage__status i{width:9px;height:9px;border-radius:50%;background:var(--emerald);box-shadow:0 0 12px var(--emerald)}

/* counters */
.counters{max-width:var(--maxw);margin:var(--space-16) auto 0;position:relative;z-index:1;display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-5)}
.counters--2{grid-template-columns:repeat(2,1fr)}
.counter .num{font-size:clamp(30px,3.4vw,44px);font-weight:800;font-variant-numeric:tabular-nums;line-height:1}
.counter .lbl{font-size:var(--text-body-lg);color:var(--muted);margin-top:var(--space-2);text-wrap:pretty}

/* ---------- problem ---------- */
.pain__k{font-size:var(--text-body-lg);font-weight:650;margin-bottom:10px}
.pain p{font-size:var(--text-body-lg);color:var(--muted);text-wrap:pretty}

/* ---------- transformation ---------- */
.diff{padding:28px}
.diff__k{font-size:var(--text-meta-lg);font-weight:650;letter-spacing:.08em;margin-bottom:18px}
.diff__list{display:flex;flex-direction:column;gap:9px;font-family:var(--mono);font-size:var(--text-body)}
.meter{margin-top:var(--space-5);padding:30px}
.meter__head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:var(--space-4);flex-wrap:wrap;gap:var(--space-2)}
.meter__num{font-size:38px;font-weight:800;font-variant-numeric:tabular-nums}
.meter__track{height:14px;border-radius:var(--radius-pill);background:var(--bg);overflow:hidden;border:1px solid var(--line)}
.meter__fill{height:100%;width:100%;border-radius:var(--radius-pill);background:#ff5a5a;transition:width 1.9s cubic-bezier(.4,0,.2,1),background 1.9s ease}
.meter__scale{display:flex;justify-content:space-between;font-size:var(--text-body);color:var(--muted);margin-top:10px}

/* ---------- how it works ---------- */
.how{position:relative}
.how__line{position:absolute;left:23px;top:24px;height:calc(100% - 70px);overflow:visible}
.how__line line{stroke-dasharray:100;stroke-dashoffset:100;transition:stroke-dashoffset 1.4s ease}
.steps{display:flex;flex-direction:column;gap:44px}
.step{display:grid;grid-template-columns:48px 1fr;gap:22px;align-items:start}
.step__icon{width:48px;height:48px;border-radius:13px;background:var(--surface-raised);border:1px solid rgba(255,255,255,.07);
  box-shadow:var(--hairline),var(--shadow-sm);display:flex;align-items:center;justify-content:center;font-weight:700;color:var(--accent);position:relative;z-index:1}
.step__icon.pulse{animation:iconpulse .7s ease}
.step h3{font-size:24px;font-weight:800;margin-bottom:var(--space-2)}
.step p{font-size:18px;color:var(--muted);max-width:640px;text-wrap:pretty}

/* ---------- product tour ---------- */
.tour{display:flex;flex-direction:column;gap:clamp(52px,6.5vw,84px)}
.tour__row{display:grid;grid-template-columns:1fr 1.12fr;gap:var(--space-14);align-items:center}
.tour__row.rev{grid-template-columns:1.12fr 1fr}
.tour__k{font-size:var(--text-meta-lg);font-weight:650;letter-spacing:.1em;text-transform:uppercase;color:var(--accent);margin-bottom:14px}
.tour h2,.tour h3{font-size:clamp(24px,2.7vw,34px);font-weight:800;line-height:1.07;margin-bottom:14px}
.tour p{font-size:18px;color:var(--muted);max-width:520px;text-wrap:pretty}
/* browser-chrome screenshot frame */
/* device frame — layered depth rather than a flat outline, and it lifts a
   little as it scrolls into view alongside its copy */
.frame{border:1px solid rgba(255,255,255,.07);border-radius:15px;overflow:hidden;background:var(--surface-raised);
  box-shadow:var(--shadow-lg),var(--hairline);transition:transform var(--dur-ui) var(--ease),box-shadow var(--dur-ui) var(--ease)}
.frame:hover{transform:translateY(-3px);box-shadow:0 52px 120px -46px rgba(0,0,0,.92),0 10px 30px -14px rgba(0,0,0,.55),var(--hairline)}
.frame__bar{display:flex;align-items:center;gap:7px;padding:11px 14px;border-bottom:1px solid rgba(255,255,255,.06);background:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,0)),var(--panel)}
.frame__bar i{width:11px;height:11px;border-radius:50%;background:#2f3947}
.frame__bar .url{margin-left:var(--space-3);font-family:var(--mono);font-size:var(--text-small);color:var(--muted)}
/* aspect-ratio reserves the space before the image arrives, so a lazily-loaded
   shot can never shift the page. The hatch shows only while it loads. */
.shot{aspect-ratio:16/10;position:relative;overflow:hidden;background:
  repeating-linear-gradient(135deg,rgba(147,160,180,.05) 0 12px,transparent 12px 24px),
  linear-gradient(180deg,var(--bg-raised),var(--panel));display:flex;align-items:center;justify-content:center}
.shot span{font-family:var(--mono);font-size:var(--text-meta);color:var(--muted);border:1px dashed var(--line);padding:var(--space-2) 14px;border-radius:8px;background:rgba(10,13,18,.5)}
.shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top center;
  opacity:0;transition:opacity .5s var(--ease)}
.shot img.loaded{opacity:1}

/* ask AIDE demo */
.ask{margin-top:22px;padding:18px}
.ask__bar{display:flex;align-items:center;gap:10px;border:1px solid var(--line);background:var(--bg);border-radius:var(--radius-ctl-sm);padding:var(--space-3) 14px}
.ask__bar .dot{width:8px;height:8px;border-radius:50%;background:var(--brand-gradient);flex:none}
.ask__text{font-size:var(--text-ui);color:var(--text);min-height:20px}
.ask__caret{width:2px;height:18px;background:var(--accent);display:inline-block;animation:blink 1s step-end infinite}
.ask__think{opacity:0;max-height:0;overflow:hidden;font-size:var(--text-body);color:var(--muted);margin-top:0;transition:opacity .3s ease,max-height .3s ease,margin-top .3s ease}
.ask__think.on{opacity:1;max-height:40px;margin-top:var(--space-3)}
.ask__think .shim{background:linear-gradient(90deg,var(--muted),#fff,var(--muted));background-size:200% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:gradientmove 1.4s linear infinite}
.ask__results{margin-top:var(--space-3);display:flex;flex-direction:column;gap:var(--space-2)}
.ask__row{opacity:0;transform:translateY(8px);display:flex;justify-content:space-between;gap:var(--space-3);font-size:14.5px;padding:10px var(--space-3);border:1px solid rgba(255,255,255,.065);border-radius:9px;background:var(--surface);box-shadow:var(--hairline);transition:opacity var(--dur-rise) var(--ease),transform var(--dur-rise) var(--ease)}
.ask__row .due{color:var(--amber);font-variant-numeric:tabular-nums}
.ask__cite{opacity:0;font-size:var(--text-meta-lg);color:var(--muted);margin-top:var(--space-3);transition:opacity .5s ease}
.ask__cite b{color:var(--text);font-weight:600}

/* ---------- intelligence ---------- */
.learn{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-14);align-items:center}
.rule{border:1px solid rgba(255,255,255,.065);background:var(--surface);box-shadow:var(--hairline);border-radius:11px;padding:14px var(--space-4);font-family:var(--mono);font-size:13.5px;display:flex;align-items:center;gap:var(--space-3);margin-bottom:10px}
.rule .tag{font-size:var(--text-caption);font-weight:700;letter-spacing:.05em;padding:3px var(--space-2);border-radius:6px;flex:none}
.rule .tag--fix{color:var(--amber);background:rgba(245,185,62,.12)}
.rule .tag--rule{color:var(--emerald);background:rgba(52,201,142,.12)}

/* ---------- security ---------- */
.sec-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-4)}
.sec-card{border:1px solid rgba(255,255,255,.065);background:var(--surface-raised);border-radius:var(--radius-ctl-lg);padding:var(--space-6);
  box-shadow:var(--hairline),var(--shadow-sm);transition:transform var(--dur-ui) var(--ease),box-shadow var(--dur-ui) var(--ease),border-color var(--dur-ui) var(--ease)}
.sec-card:hover{transform:translateY(-3px);border-color:rgba(255,255,255,.11);box-shadow:var(--hairline),var(--shadow-md)}
.sec-card h4{font-size:var(--text-body-lg);font-weight:700;margin-bottom:var(--space-2);display:flex;align-items:center;gap:11px}
.sec-card p{font-size:var(--text-body);color:var(--muted);text-wrap:pretty}
.sec-card .ic{flex:none;width:9px;height:9px;border-radius:2px;background:var(--brand-gradient);box-shadow:0 0 10px rgba(143,180,240,.5)}
.sec-card--wide{grid-column:span 2}
/* prompt injection demo */
.inject{border:1px solid var(--line);background:var(--bg);border-radius:var(--radius-ctl);padding:18px;font-family:var(--mono);font-size:var(--text-meta);line-height:1.7}
.inject .ln{color:var(--muted)}
.inject .bad{position:relative;color:#ff8a8a;transition:background .4s ease}
.inject .bad.flagged{background:rgba(255,90,90,.1);border-radius:4px}
.inject__flag{opacity:0;transform:translateY(4px);margin-top:14px;display:inline-flex;align-items:center;gap:var(--space-2);font-family:var(--font);font-size:var(--text-meta);font-weight:600;color:#ff8a8a;background:rgba(255,90,90,.1);border:1px solid rgba(255,90,90,.3);padding:6px var(--space-3);border-radius:var(--radius-pill);transition:opacity .5s ease,transform .5s ease}
.inject__flag i{width:7px;height:7px;border-radius:50%;background:#ff5a5a}

/* ---------- pricing ---------- */
.price-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;align-items:start}
.tier{position:relative;border:1px solid rgba(255,255,255,.065);background:var(--surface);border-radius:var(--radius);padding:28px 25px;display:flex;flex-direction:column;
  box-shadow:var(--hairline),var(--shadow-sm);transition:transform var(--dur-ui) var(--ease),box-shadow var(--dur-ui) var(--ease),border-color var(--dur-ui) var(--ease)}
.tier:hover{transform:translateY(-3px);border-color:rgba(255,255,255,.11);box-shadow:var(--hairline),var(--shadow-md)}
.tier--pop{position:relative;border:0;padding:2px;overflow:visible;border-radius:var(--radius-card-lg);background:linear-gradient(120deg,var(--accent),var(--accent-2),var(--sky),var(--accent-2),var(--accent));background-size:300% 300%;animation:borderflow 7s ease infinite;box-shadow:0 24px 60px -30px rgba(143,180,240,.6)}
.tier--pop .tier__in{position:relative;background:var(--surface);border-radius:var(--radius-card);padding:var(--space-8) 25px 28px;margin:0;height:100%;display:flex;flex-direction:column;z-index:1;box-shadow:var(--hairline)}
.tier--pop::before{content:none}
.tier__badge{position:absolute;top:-11px;left:50%;transform:translateX(-50%);z-index:2;font-size:var(--text-small);font-weight:700;letter-spacing:.06em;color:#fff;background:var(--brand-gradient);padding:5px 14px;border-radius:var(--radius-pill);white-space:nowrap}
.tier__name{font-size:var(--text-body);font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
.tier__price{font-size:40px;font-weight:740;letter-spacing:-.03em;margin:14px 0 2px;font-variant-numeric:tabular-nums}
.tier__price small{font-size:var(--text-ui);font-weight:500;color:var(--muted)}
.tier__meta{font-size:var(--text-body);color:var(--muted);margin-bottom:22px}
.tier__feat{list-style:none;padding:0;margin:0 0 26px;display:flex;flex-direction:column;gap:11px;flex:1}
.tier__feat li{font-size:var(--text-body);color:var(--text);display:flex;gap:10px;align-items:flex-start}
.tier__feat li svg{flex:none;margin-top:3px;color:var(--emerald)}
/* The li is a flex row (tick + text) with a 10px gap. A <span data-brand> in
   the middle of the text becomes its OWN flex item, so the brand name gets the
   row gap on both sides and can wrap onto a line by itself — "Ask / Torvane /
   with cited answers". display:contents removes the span's box so its text
   rejoins the surrounding run as a single anonymous flex item; the span still
   exists in the DOM, so app.js can still swap the brand name into it. */
.tier__feat li [data-brand]{display:contents}

/* ---------- faq ---------- */
.faq{max-width:820px;margin:0 auto}
.faq__item{border-bottom:1px solid rgba(255,255,255,.07)}
.faq__q{width:100%;text-align:left;background:none;border:0;color:var(--text);font-family:var(--font);font-size:var(--text-body-lg);font-weight:600;letter-spacing:-.015em;padding:22px 0;cursor:pointer;display:flex;justify-content:space-between;gap:var(--space-5);align-items:center;
  transition:color var(--dur-micro) var(--ease-out)}
.faq__q:hover{color:#fff}
.faq__q:hover{color:#fff}
.faq__q .pm{flex:none;width:22px;height:22px;position:relative;transition:transform .3s ease}
.faq__q .pm::before,.faq__q .pm::after{content:"";position:absolute;background:var(--accent);border-radius:2px}
.faq__q .pm::before{left:0;right:0;top:10px;height:2px}
.faq__q .pm::after{top:0;bottom:0;left:10px;width:2px;transition:transform .3s ease}
.faq__item.open .pm::after{transform:scaleY(0)}
.faq__a{max-height:0;overflow:hidden;transition:max-height .35s ease}
.faq__a p{font-size:var(--text-body-lg);color:var(--muted);padding:0 0 var(--space-6);max-width:720px;text-wrap:pretty}

/* ---------- final cta ---------- */
.finalcta{position:relative;text-align:center;padding:clamp(64px,9vw,116px) 24px;overflow:hidden}
.finalcta__glow{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:900px;height:520px;max-width:120vw;background:radial-gradient(closest-side,rgba(143,180,240,.22),rgba(59,111,212,.12) 50%,transparent 74%);filter:blur(30px);pointer-events:none;z-index:0}
.finalcta h2{position:relative;z-index:1;font-size:clamp(30px,3.9vw,52px);font-weight:800;line-height:1.04;max-width:940px;margin:0 auto 30px}

/* ---------- footer ---------- */
.footer{border-top:1px solid transparent;border-image:linear-gradient(90deg,transparent,rgba(255,255,255,.09),transparent) 1;padding:var(--space-14) var(--space-6) var(--space-10);position:relative;z-index:1}
.footer__in{max-width:var(--maxw);margin:0 auto;display:flex;justify-content:space-between;gap:var(--space-8);flex-wrap:wrap;align-items:center}
.footer__links{display:flex;gap:var(--space-6);flex-wrap:wrap}
.footer__links a{color:var(--muted);font-size:var(--text-body)}
.footer__links a:hover{color:var(--text)}
.footer__copy{font-size:var(--text-body);color:var(--muted)}

/* ---------- reveal (progressive enhancement: hidden ONLY once JS arms it) ---------- */
[data-reveal]{transition:opacity var(--dur-rise) var(--ease),transform var(--dur-rise) var(--ease)}
html.reveal-on [data-reveal]{opacity:0;transform:translateY(18px)}
html.reveal-on [data-reveal].in{opacity:1;transform:none}

/* ---------- keyframes ---------- */
@keyframes aurora{0%{transform:translate(-50%,0) rotate(0deg) scale(1.05)}50%{transform:translate(-50%,4%) rotate(180deg) scale(1.22)}100%{transform:translate(-50%,0) rotate(360deg) scale(1.05)}}
@keyframes iconpulse{0%{transform:scale(1)}45%{transform:scale(1.18)}100%{transform:scale(1)}}
@keyframes gradientmove{0%{background-position:0% 50%}100%{background-position:200% 50%}}
@keyframes blink{0%,49%{opacity:1}50%,100%{opacity:0}}
@keyframes spinborder{to{transform:rotate(360deg)}}

/* ---------- responsive ---------- */
@media (max-width:960px){
  .hero__grid,.tour__row,.tour__row.rev,.learn{grid-template-columns:1fr}
  .tour__row.rev .frame{order:-1}
  .grid-3,.counters,.sec-grid{grid-template-columns:1fr 1fr}
  .price-grid{grid-template-columns:1fr 1fr}
  .sec-card--wide{grid-column:span 2}
  .stage{height:420px}
}
@media (max-width:720px){
  /* ---- mobile: a full-screen sheet, not a dropdown ----
     Built from the existing .nav__links / .nav__cta so no page markup
     changes. The pill (brandmark + X) floats above the sheet. */
  .nav__toggle{display:block}
  .brandmark{z-index:60}

  /* backdrop-filter makes .nav__in a containing block for fixed descendants,
     which would trap the sheet inside the pill. Dropping the glass while open
     both frees the sheet to fill the viewport and leaves just the wordmark
     and the X floating over it — which is the look we want anyway. */
  .nav.open .nav__in{backdrop-filter:none;-webkit-backdrop-filter:none;background:transparent;border-color:transparent;box-shadow:none}
  /* .nav.scrolled shrinks the pill (padding 18->10, height 60->52) as you move
     down the page. With the sheet open that made the wordmark and the X visibly
     jump up and down while the page scrolled behind them. The scroll lock below
     is the primary fix; pinning the geometry here means the header cannot move
     even if the lock is ever bypassed. */
  .nav.open,.nav.open.scrolled{padding:18px}
  .nav.open .nav__in,.nav.open.scrolled .nav__in{height:60px;border-radius:20px}
  .nav.open .nav__in::after{opacity:0}

  /* padding, not bare centring: .nav__cta is a SIBLING pinned to the foot of
     the sheet at z-index 56, one above these links. Centring the stack in the
     full viewport meant that on a short screen the last link ran underneath the
     Sign in / Get started buttons and they painted over it — measured at
     390x568, a 23px overlap, and only 3px of clearance left at 390x620, which
     any phone reaches the moment its URL bar is showing. Reserving the CTA's
     space here makes the collision impossible instead of unlikely. overflow-y
     is the backstop: if the stack still cannot fit, it scrolls rather than
     colliding. */
  .nav__links{position:fixed;inset:0;left:0;right:0;transform:none;display:flex;flex-direction:column;justify-content:center;gap:2px;padding:84px 30px 164px;z-index:55;overflow-y:auto;overscroll-behavior:contain;
    /* .97, not .9: at 10% transmission the hero's blue gradient CTA sitting
       underneath read straight through the sheet as a ghost button. The
       backdrop blur still supplies the depth. */
    background:radial-gradient(ellipse 110% 60% at 50% 0%,rgba(30,24,60,.55),transparent 70%),rgba(8,10,14,.97);
    backdrop-filter:saturate(160%) blur(26px);-webkit-backdrop-filter:saturate(160%) blur(26px);
    opacity:0;visibility:hidden;transition:opacity var(--dur-ui) var(--ease),visibility 0s linear var(--dur-ui)}
  .nav.open .nav__links{opacity:1;visibility:visible;transition:opacity var(--dur-ui) var(--ease),visibility 0s}

  /* each row rises into place, staggered */
  .nav__links .navlink{font-size:30px;font-weight:640;letter-spacing:-.028em;color:var(--text);padding:15px var(--space-1);border-radius:var(--radius-ctl);
    opacity:0;transform:translateY(14px);transition:opacity var(--dur-ui) var(--ease),transform var(--dur-ui) var(--ease)}
  .nav__links .navlink::before,.nav__links .navlink::after{display:none}
  .nav.open .nav__links .navlink{opacity:1;transform:none}
  .nav.open .nav__links .navlink:nth-child(1){transition-delay:60ms}
  .nav.open .nav__links .navlink:nth-child(2){transition-delay:110ms}
  .nav.open .nav__links .navlink:nth-child(3){transition-delay:160ms}
  .nav.open .nav__links .navlink:nth-child(4){transition-delay:210ms}
  /* a hairline between rows keeps the stack readable without boxing it */
  .nav__links .navlink+.navlink{border-top:1px solid rgba(255,255,255,.07)}
  .nav__links .navlink[aria-current="page"]{color:var(--accent)}

  /* CTAs pinned to the foot of the sheet, both visible, full width */
  .nav__cta{position:fixed;left:30px;right:30px;bottom:max(34px,env(safe-area-inset-bottom));z-index:56;flex-direction:column-reverse;gap:10px;
    opacity:0;visibility:hidden;transform:translateY(14px);transition:opacity var(--dur-ui) var(--ease),transform var(--dur-ui) var(--ease),visibility 0s linear var(--dur-ui)}
  .nav.open .nav__cta{opacity:1;visibility:visible;transform:none;transition:opacity var(--dur-ui) var(--ease) 240ms,transform var(--dur-ui) var(--ease) 240ms,visibility 0s}
  .nav__cta .btn{width:100%;justify-content:center;font-size:var(--text-ui);padding:14px 22px}
  /* Scroll lock. `body{overflow:hidden}` alone did nothing here: html carries
     overflow-x:clip, which computes overflow-y to auto and makes <html> the
     scrolling element, so the page kept scrolling behind the open sheet —
     measured scrollY 0 -> 900 with the menu open. The lock has to be on the
     element that actually scrolls. */
  html.nav-locked,body.nav-locked{overflow:hidden}
  .grid-3,.counters,.grid-2,.sec-grid,.price-grid,.diff-cols{grid-template-columns:1fr}
  .sec-card--wide{grid-column:auto}
  .section{padding:clamp(72px,16vw,120px) 0}
  :root{--page-top:110px}   /* ~28px clear of the nav pill; was 148px = a 52px hole */
  /* .hero--split sets --page-top on ITSELF, which shadows :root — without
     this the homepage kept the desktop 120px and the gap never shrank. */
  .hero--split{--page-top:110px}
  /* The desktop scroll-padding of 90px is measured against the desktop nav. The
     mobile nav pill is taller relative to the viewport, so an anchor jump was
     landing a section heading underneath it. */
  html{scroll-padding-top:104px}
  .hero{padding:var(--page-top) 20px 40px}
  .hero__ctas .btn--primary{flex:1}

  /* ---- mobile overflow hardening (20F) ----
     Reported as: the page pans sideways on a real phone and looks zoomed in.
     It does not reproduce in Chromium at any width, which is the tell — the
     cause is `overflow: clip`, which Safari only shipped in 16. On older iOS it
     is ignored outright, so the decorative glows (1200px and 900px wide, capped
     at 140vw and 120vw) escape their containers, widen the layout viewport, and
     the page both pans and renders scaled down to fit. truely.uk sets
     overflow-x on BOTH html and body; Torvane only had it on html.

     Three layers, so no single one has to hold:
       1. overflow-x on body as well, hidden first as the fallback;
       2. the oversized decorations capped to the viewport on mobile, so there
          is nothing to escape even where clipping is ignored;
       3. a hard max-width on the root. */
  body{overflow-x:hidden;overflow-x:clip;max-width:100%}
  .hero__aurora{left:0;right:0;width:auto;max-width:none;transform:none;animation:none}
  .finalcta__glow{left:0;right:0;width:auto;max-width:none;transform:translateY(-50%)}
  .stage__beam{max-width:100%}
  /* truely.uk runs a 28px gutter on a phone; Torvane was on 20px, which is part
     of why the type read as crowding the edges. */
  .wrap{padding:0 28px}
  .hero{padding-left:28px;padding-right:28px}

  /* ---- hero stage on a phone (19C) ----
     The desktop composition is side by side: the messy pile on the left, the
     organized tree overlaid on the right at 46% width. Below 720px that stops
     being a composition and becomes a collision. 46% of a 350px stage is a
     160px column, narrow enough that "Company Library" and "218 files ·
     organized" render on top of each other; and the cards are placed in fixed
     pixels that reach x=516 and y=590, so ten of the fourteen were being cut
     off by the stage's overflow:hidden. Measured, not guessed.

     Stacking is the only arrangement that fits: pile in the top third, tree
     full width underneath. The JS fly-in reads the tree's real position, so
     the choreography follows this layout without knowing about it. */
  /* Single column below 720px: the card stacks pile -> arrow -> library.
     Same one-frame story, rotated. */
  .hero__grid{grid-template-columns:1fr;gap:30px}
  .hero__lede{align-items:flex-start}
  .hero__showpiece .stage{min-height:0}
  .stage{min-height:0}
  .stage__body{grid-template-columns:1fr;gap:var(--space-3);padding:14px}
  .stage__arrow{flex-direction:row;gap:9px;justify-self:center}
  .stage__arrow svg{transform:rotate(90deg)}
  .stage__arrow span br{display:none}
  .mcard{max-width:none;font-size:var(--text-caption);padding:var(--space-2) 10px;gap:var(--space-2)}
  .mcard .ic{width:19px;height:24px}
  .stage__messy .mcard:nth-of-type(n+4){display:none}
  .tree__head{font-size:11.5px;padding-bottom:10px}
  .tree__head .count{font-size:var(--text-caption)}
  /* hug the rows: flex:1 stretched the panel to the card height, leaving a
     large empty box under three folders */
  .tree__scroll{justify-content:flex-start;flex:none}
  .tree__row:nth-of-type(n+4){display:none}
  .tree__row{margin-top:11px}
  .tree__folder{font-size:var(--text-small);margin-bottom:var(--space-1)}
  .tree__file{font-size:var(--text-caption);padding:3px 0 3px 18px}
  .tree__file .fmeta{font-size:var(--text-caption)}
  .dupchip{display:none}
  .stage__status{font-size:var(--text-small);padding:var(--space-3) 14px}
}

/* ---- short phones: keep the whole sheet on screen ----
   With the CTA space now reserved, a 4-link stack at 30px needs ~310px of the
   remaining box. Below ~640px tall that stops fitting, so the type steps down
   rather than the sheet starting to scroll. */
@media (max-width:720px) and (max-height:640px){
  .nav__links{padding:74px 30px 150px}
  .nav__links .navlink{font-size:24px;padding:11px var(--space-1)}
}

/* ---- short laptops (19A) ----
   Keyed off HEIGHT, because the constraint here is vertical, not horizontal: a
   1280x720 screen has plenty of width but only 720px to fit nav + hero + the
   consent bar. The threshold is 740px deliberately — it catches 720 and leaves
   1366x768 alone, which clears comfortably at the full display size. Scoping it
   this tightly is what lets the headline stay big where there is room for it. */
@media (min-width:721px) and (max-height:740px){
  :root{--page-top:clamp(140px,18vh,168px)}
  .hero h1{font-size:clamp(38px,4.4vw,54px);margin-top:14px}
  .hero__sub{font-size:clamp(16px,1.35vw,18px);margin:var(--space-4) auto var(--space-5)}
  /* On a short screen the CTAs sit higher (smaller text, less top padding), so
     there is room to keep the stage below the fold with a bigger gap than the
     6vh this used to use — 6vh of 720 was only ~43px, which left a quarter of
     the stage peeking. 29vh lands it just under the fold (raised from 27vh
     when --page-top pulled the hero up). */
  .hero__showpiece{margin-top:clamp(56px,12vh,110px)}
}

/* ---- narrow phones (19A) ----
   Below ~480px the clamp floor of 48px does real damage: at a 342px measure
   this headline wrapped to SIX lines, dominating the screen and pushing the
   subhead and CTAs into the last sliver of it. Stepping the display size down
   here is what "degrades gracefully" means — it still reads as the headline,
   in four lines instead of six. truely.uk lands on 40px at this width for the
   same reason. Full-width CTAs because a 342px row cannot hold two side by
   side without one of them looking like an afterthought. */
@media (max-width:480px){
  :root{--page-top:clamp(104px,12vh,116px)}
  .hero--split{--page-top:clamp(104px,12vh,116px)}
  .hero h1{font-size:clamp(32px,8.6vw,40px);line-height:1.06;letter-spacing:-.028em}
  .hero__sub{font-size:16.5px;line-height:1.55;margin:var(--space-5) auto 26px}
  .hero__ctas{gap:10px;width:100%}
  .hero__ctas .btn{width:100%;justify-content:center}
  /* enough to leave only a clean top edge of the stage below the fold on a
     phone, rather than a fifth of the panel — see the desktop note. */
  .hero__showpiece{margin-top:clamp(36px,8vh,96px)}
}

/* ---------- how-it-works mini list (20E) ----------
   The four full steps now live on /how-it-works; the homepage keeps their
   titles as a single compact row so the shape of the process is still legible
   without spending 1.4 mobile screens restating it. */
.howmini{list-style:none;margin:34px 0 0;padding:0;display:grid;grid-template-columns:repeat(5,1fr);gap:14px;counter-reset:none}
.howmini li{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-ctl-lg);padding:18px 18px;
  font-size:var(--text-body);box-shadow:var(--hairline)}
.howmini li strong{font-weight:650;letter-spacing:-.01em}
@media (max-width:860px){.howmini{grid-template-columns:1fr 1fr}}
@media (max-width:480px){.howmini{grid-template-columns:1fr;gap:10px}.howmini li{padding:14px var(--space-4)}}

/* ---------- pricing band (20E) ----------
   The homepage used to restate the whole four-tier grid — 11 of its 12 feature
   lines were byte-identical to /pricing, at a cost of 2.4 mobile screens. This
   keeps the part that actually does work on a homepage (the entry price, what
   it includes, and a way in) and sends anyone comparing plans to the page that
   exists for it. */
.priceband{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:clamp(24px,4vw,56px);
  align-items:center;margin-top:34px}
.priceband__card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-card-lg);
  padding:26px var(--space-6);box-shadow:var(--hairline),var(--shadow-sm)}
.priceband__from{font-size:clamp(30px,3vw,40px);font-weight:750;letter-spacing:-.03em;color:var(--text);line-height:1}
.priceband__from strong{background:var(--brand-gradient);-webkit-background-clip:text;background-clip:text;color:transparent}
.priceband__from small{font-size:var(--text-body);font-weight:500;color:var(--muted);margin-left:2px}
.priceband__meta{margin-top:10px;font-family:var(--mono);font-size:12.5px;color:var(--muted)}
.priceband__cta{margin-top:var(--space-5);display:flex;flex-direction:column;gap:9px}
.priceband__note{margin-top:var(--space-3);font-size:12.5px;color:var(--muted);text-align:center}
@media (max-width:860px){
  .priceband{grid-template-columns:1fr;gap:26px}
}

/* ---------- back to top ---------- */
/* Mobile only. The page is long enough on a phone that returning to the nav is
   a real chore; on a desktop the whole nav is one flick of a wheel away, and a
   floating button there would be clutter rather than help. Shown by JS above
   600px of scroll — see app.js. */
.totop{position:fixed;right:16px;bottom:calc(16px + env(safe-area-inset-bottom));
  z-index:150;display:none;width:46px;height:46px;align-items:center;justify-content:center;
  border-radius:50%;cursor:pointer;color:var(--text);
  background:rgba(24,34,52,.82);border:1px solid var(--line);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  box-shadow:0 10px 30px -12px rgba(0,0,0,.85);
  opacity:0;transform:translateY(10px) scale(.9);
  transition:opacity .22s var(--ease-out),transform .22s var(--ease-out),
             background var(--dur-micro) var(--ease-out)}
.totop.on{opacity:1;transform:none}
.totop:hover,.totop:focus-visible{background:rgba(42,52,72,.92)}
.totop:active{transform:scale(.94)}
@media (max-width:720px){
  .totop{display:flex}
  /* The sheet covers the page at z-index 210; a button floating over it would
     sit on top of the menu the user just opened. */
  body.nav-locked .totop{opacity:0;pointer-events:none}
}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important}
  [data-reveal]{opacity:1;transform:none}
  .hero__aurora{animation:none}
  /* Was opacity:0 — correct for the old full-width stage, where the reduced
     state was simply "already sorted". In the bounded card the pile IS the
     "before" half, so hiding it empties a third of the frame and the
     before/after story disappears. Shown, static, no motion. */
  .mcard{opacity:1;transform:none !important}
  .tree__row,.dupchip,.stage__status,.ask__cite{opacity:1 !important;transform:none !important}
  .ask__row{opacity:1 !important;transform:none !important}
  .meter__fill{width:0 !important;background:var(--emerald) !important;transition:none}
  .how__line line{stroke-dashoffset:0 !important;transition:none}
  .inject__flag{opacity:1 !important;transform:none !important}
  .inject .bad{background:rgba(255,90,90,.1);border-radius:4px}
  .ask__caret{animation:none;opacity:0}
}

/* ---------- focus: visible, on-brand, never a default outline ---------- */
:where(a,button,input,select,textarea,[tabindex]):focus-visible{
  outline:2px solid var(--accent);outline-offset:3px;border-radius:8px}
.btn:focus-visible{outline-offset:4px}

/* honour reduced motion for everything added in phase 15 */
@media (prefers-reduced-motion:reduce){
  .card--lift:hover,.sec-card:hover,.tier:hover,.frame:hover{transform:none}
  .shot img{opacity:1;transition:none}
  .nav__links .navlink{opacity:1;transform:none;transition:none}
}


/* ---------- Torvane mark in the nav (20 Aug 2026) ----------
   The wordmark alone carried the brand; the mark now sits beside it at the
   wordmark's own optical size. `flex:none` so a narrow bar shrinks the text
   rather than distorting the logo. */
.brandmark__logo{width:26px;height:26px;flex:none;margin-right:9px;display:block}
@media (max-width:640px){.brandmark__logo{width:23px;height:23px;margin-right:7px}}

/* ---- touch targets (responsive audit, 23 Sep 2026; tests/test_responsive.py) ----
   Every control a thumb can hit is at least --hit-min on touch widths. The
   box grows; the glyphs do not. */
@media (max-width:720px){
  .nav__toggle{width:var(--hit-min);height:var(--hit-min)}
  .nav__toggle span{left:13px}
  .nav__toggle span:nth-child(1){top:16px}
  .nav__toggle span:nth-child(2){top:21.25px}
  .nav__toggle span:nth-child(3){top:26.5px}
  .nav__in .brandmark,.footer__in .brandmark{min-height:var(--hit-min)}
  .footer__links a{display:inline-flex;align-items:center;min-height:var(--hit-min);min-width:var(--hit-min)}
  .btn{min-height:var(--hit-min)}
}

/* ---- the agent, today (homepage, 24 Sep 2026) ---- */
.agent-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--space-4)}
.agent-cap{padding:var(--space-5)}
.agent-cap__n{font-family:var(--mono);font-size:var(--text-small);color:var(--accent-2);letter-spacing:.08em}
.agent-cap h3{font-size:var(--text-body-lg);font-weight:650;margin:10px 0 8px}
.agent-cap p{font-size:var(--text-meta-lg);color:var(--muted);line-height:1.55;margin:0}
.agent-cap a{color:var(--text);text-decoration:underline;text-underline-offset:3px}
/* ---- learns from corrections: a real run ---- */
.learnrun{margin-top:var(--space-14);border:1px solid rgba(255,255,255,.065);background:var(--surface);
  border-radius:var(--radius);padding:var(--space-8)}
.learnrun__head{display:flex;flex-wrap:wrap;gap:var(--space-3) var(--space-6);align-items:baseline;
  justify-content:space-between}
.learnrun__head .small{color:var(--muted);margin:0;max-width:560px}
.learnrun__steps{list-style:none;padding:0;margin:var(--space-6) 0 0;display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--space-4)}
.lstep{border-left:2px solid rgba(255,255,255,.12);padding:0 0 0 var(--space-4);min-width:0}
.lstep--after{border-left-color:var(--emerald)}
.lstep__k{font-family:var(--mono);font-size:var(--text-small);text-transform:uppercase;
  letter-spacing:.08em;color:var(--amber)}
.lstep--after .lstep__k{color:var(--emerald)}
.lstep h3{font-size:var(--text-body);font-weight:650;margin:8px 0;line-height:1.4}
.lstep p{font-size:var(--text-meta-lg);color:var(--muted);line-height:1.55;margin:0 0 10px}
.lstep code{font-size:.92em;overflow-wrap:anywhere}
.lstep__quote{margin:0 0 10px;padding:10px 12px;border-radius:10px;background:rgba(255,255,255,.035);
  font-family:var(--mono);font-size:var(--text-small);line-height:1.55;color:var(--text);
  overflow-wrap:anywhere}
.learnrun__limits{margin:var(--space-6) 0 0;color:var(--muted)}
@media (max-width:1100px){
  .agent-grid,.learnrun__steps{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:560px){
  .agent-grid,.learnrun__steps{grid-template-columns:1fr}
  .learnrun{padding:var(--space-5)}
}
.btn--wrap{white-space:normal;text-align:left;max-width:100%}
/* ---- hero promise + trust lines (owner hero decision, 24 Sep 2026) ---- */
.hero__promise{font-size:var(--text-body-lg);font-weight:650;color:var(--text);margin:0 0 var(--space-6);max-width:44ch}
.hero__trust{font-size:var(--text-meta-lg);color:var(--muted);line-height:1.55;margin:var(--space-5) 0 0;max-width:52ch}
/* ---- watch it work: a real run as a step log ---- */
.section--watch{padding-top:var(--space-10)}
.workrun{margin-top:var(--space-8)}
.wlog{list-style:none;padding:0;margin:var(--space-6) 0 0;display:grid;gap:var(--space-4)}
.wlog__row{display:grid;grid-template-columns:32px minmax(0,1fr);gap:var(--space-3);align-items:start;min-width:0}
.wlog__m{width:28px;height:28px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;
  font-weight:700;font-size:var(--text-meta-lg);background:rgba(255,255,255,.06);color:var(--muted)}
.wlog__row--ok .wlog__m{background:rgba(52,201,142,.12);color:var(--emerald)}
.wlog__row--ask .wlog__m{background:rgba(245,185,62,.12);color:var(--amber)}
.wlog__row--you .wlog__m{background:rgba(59,111,212,.18);color:var(--accent-2)}
.wlog__row h3{font-size:var(--text-body);font-weight:650;margin:3px 0 8px;line-height:1.4}
.wlog__row p{font-size:var(--text-meta-lg);color:var(--muted);line-height:1.55;margin:0 0 10px}
.wlog__row code{font-size:.92em;overflow-wrap:anywhere}
.wlog__after{margin:var(--space-6) 0 10px;color:var(--muted);font-size:var(--text-meta-lg);line-height:1.55}
.wlog__after strong{color:var(--text)}
.lesscards .card{min-width:0}
@media (max-width:760px){ .lesscards{grid-template-columns:1fr} }

