/* CrewCaptain — shared site design system + motion layer.
 *
 * WHY THIS FILE EXISTS: the nine pages under web-legal/ each carried their own
 * inline copy of the same palette and the same four components (.eyebrow,
 * .rule, .card, h1). Nine copies drift. This is the single source, and it is
 * the app's own palette from src/constants/theme.ts, not a lookalike.
 *
 * ------------------------------------------------------------------
 * THE MOTION RULE — read before adding anything that moves.
 *
 *   Nothing on this site slides sideways and nothing fades in place.
 *   Everything RISES.
 *
 * One direction, one curve (--ease-rise), one accent that glows. The brand
 * mark is two chevrons pointing up; the pitch is revenue up, crew up, hours
 * accounted for. The motion says the same thing the logo says, so the page
 * reads as one object instead of a pile of effects.
 *
 * Depth (3D) is spent in exactly ONE place: the device frames, where there is
 * real depth to show — a phone is a physical object and the product is what is
 * on it. Everywhere else, depth is a shadow and a hairline.
 *
 * PERFORMANCE: animated properties are transform and opacity only, so every
 * frame stays on the compositor. box-shadow and border-color change on
 * discrete hover states, never on scroll. Continuous scroll-linked motion runs
 * on CSS scroll-driven timelines (animation-timeline: view()) so it never
 * touches the main thread; the IntersectionObserver in site.js only fires the
 * one-shot reveals.
 *
 * REDUCED MOTION: one block at the bottom of this file. It does not merely
 * shorten the animations — it forces every revealed element to its final
 * state, because a reveal that never runs would otherwise leave the page
 * blank for anyone who asked for less motion.
 * ------------------------------------------------------------------ */

/* ---------- Type: the app's own faces, subset and self-hosted ---------- */
/* Barlow Semi Condensed for display (signage character), Barlow for text —
   identical to Font.display / Font.body in src/constants/theme.ts. Subset to
   Latin + the handful of punctuation this site uses: 52KB for all four faces,
   served from our own origin, so there is no third-party request and no
   layout shift beyond the swap. */
@font-face{font-family:'BarlowSC';src:url('/font/bsc-800.woff2') format('woff2');font-weight:800;font-style:normal;font-display:swap}
@font-face{font-family:'BarlowSC';src:url('/font/bsc-700.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:'BarlowUI';src:url('/font/barlow-500.woff2') format('woff2');font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:'BarlowUI';src:url('/font/barlow-700.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap}

:root{
  /* Palette — src/constants/theme.ts, value for value. */
  --bg:#0D141E;        /* Brand.paper   */
  --bg-lift:#111A26;   /* between paper and card, for banded sections */
  --panel:#161F2C;     /* Brand.card    */
  --sunken:#1D2836;    /* Brand2.sunken */
  --line:#2A3648;      /* Brand.border  */
  --ink:#EEF3FA;       /* Brand.ink     */
  --body:#C9D3E0;      /* Brand2.inkSoft*/
  --mut:#9DAABC;       /* Brand.muted   */
  --faint:#66748A;     /* Brand2.faint  */
  --teal:#19B8A6;      /* Brand.amber — THE accent */
  --teal2:#57DCC9;     /* Brand2.accentInk */
  --teal-deep:#0E7566; /* Brand.stripeB */
  --green:#57B26A;     /* Brand.green   */

  --display:'BarlowSC',ui-sans-serif,-apple-system,system-ui,sans-serif;
  --text:'BarlowUI',ui-sans-serif,-apple-system,system-ui,sans-serif;

  /* Motion. One curve does the rising; one does the settling. */
  --ease-rise:cubic-bezier(.16,1,.3,1);
  --ease-soft:cubic-bezier(.22,.61,.36,1);
  --t-fast:180ms;
  --t-mid:420ms;
  --t-rise:760ms;
  --rise:26px;

  --wrap:1080px;
  --gut:clamp(20px,5vw,40px);
}

*{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:var(--text);font-weight:500;font-size:16px;line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:clip;
}
img{max-width:100%;display:block}
a{color:var(--teal2);text-decoration:none}

/* ================================================================
   1. AMBIENT DEPTH — the room the page sits in
   Two teal glows and a faint survey grid on a fixed layer behind
   everything. Fixed + transform-only, so scrolling never repaints it.
   Alpha is deliberately low: this must never cost text contrast.
   ================================================================ */
.sky{position:fixed;inset:0;z-index:-2;pointer-events:none;overflow:hidden;background:var(--bg)}
.sky::before,.sky::after{
  content:'';position:absolute;border-radius:50%;
  filter:blur(90px);opacity:.5;will-change:transform;
}
.sky::before{
  width:70vmax;height:70vmax;top:-28vmax;left:-16vmax;
  background:radial-gradient(circle,rgba(25,184,166,.30),rgba(25,184,166,0) 68%);
  animation:drift-a 34s var(--ease-soft) infinite alternate;
}
.sky::after{
  width:58vmax;height:58vmax;top:34vmax;right:-22vmax;
  background:radial-gradient(circle,rgba(94,142,192,.22),rgba(94,142,192,0) 68%);
  animation:drift-b 44s var(--ease-soft) infinite alternate;
}
@keyframes drift-a{from{transform:translate3d(0,0,0) scale(1)}to{transform:translate3d(6vw,7vh,0) scale(1.14)}}
@keyframes drift-b{from{transform:translate3d(0,0,0) scale(1.08)}to{transform:translate3d(-7vw,-5vh,0) scale(1)}}

/* The survey grid — a field-service page should feel measured out. It fades
   to nothing by mid-page so it never competes with the pricing table. */
.grid-fade{
  position:fixed;inset:0;z-index:-1;pointer-events:none;
  background-image:linear-gradient(rgba(42,54,72,.30) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(42,54,72,.30) 1px,transparent 1px);
  background-size:66px 66px;
  -webkit-mask-image:radial-gradient(ellipse 90% 55% at 50% 0%,#000 0%,transparent 72%);
          mask-image:radial-gradient(ellipse 90% 55% at 50% 0%,#000 0%,transparent 72%);
}

/* ================================================================
   2. SHELL — header, layout, footer
   ================================================================ */
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 var(--gut)}

.topbar{
  position:sticky;top:0;z-index:50;
  border-bottom:1px solid transparent;
  transition:background var(--t-mid) var(--ease-soft),
             border-color var(--t-mid) var(--ease-soft),
             backdrop-filter var(--t-mid) var(--ease-soft);
}
/* Glass only once you have scrolled past the hero. A bar that is frosted from
   the first pixel is decoration; one that materialises is feedback. */
.topbar.stuck{
  background:rgba(13,20,30,.88);
  border-bottom-color:var(--line);
  -webkit-backdrop-filter:blur(16px) saturate(140%);
          backdrop-filter:blur(16px) saturate(140%);
}
.topbar .wrap{display:flex;align-items:center;gap:18px;height:64px}
.brand{display:flex;align-items:center;gap:10px;color:var(--ink);font-family:var(--display);
  font-weight:800;font-size:19px;letter-spacing:.2px}
.brand svg{width:22px;height:22px;flex:0 0 auto}
.nav{margin-left:auto;display:flex;align-items:center;gap:6px}
.nav a{
  position:relative;color:var(--mut);font-size:14.5px;font-weight:700;
  padding:8px 12px;border-radius:8px;transition:color var(--t-fast) var(--ease-soft);
}
.nav a::after{
  content:'';position:absolute;left:12px;right:12px;bottom:4px;height:2px;
  background:var(--teal);border-radius:2px;
  transform:scaleX(0);transform-origin:left;
  transition:transform var(--t-mid) var(--ease-rise);
}
.nav a:hover{color:var(--ink)}
.nav a:hover::after{transform:scaleX(1)}
.nav .btn{padding:9px 16px;font-size:14.5px}
.nav .btn::after{display:none}
@media (max-width:720px){.nav .hide-sm{display:none}}

/* Scroll progress — the one place a width animation is acceptable, because it
   is scaleX on a compositor layer, not a real width. */
.progress{
  position:fixed;top:0;left:0;right:0;height:2px;z-index:60;
  background:linear-gradient(90deg,var(--teal-deep),var(--teal),var(--teal2));
  transform:scaleX(0);transform-origin:0 50%;
}
@supports (animation-timeline:scroll()){
  .progress{animation:fill linear;animation-timeline:scroll(root block)}
  @keyframes fill{to{transform:scaleX(1)}}
}

section{position:relative;padding:clamp(56px,9vw,104px) 0}
.section-head{max-width:640px}
h1,h2,h3{font-family:var(--display);font-weight:800;color:var(--ink);margin:0}
h1{font-size:clamp(38px,7.2vw,68px);line-height:1.04;letter-spacing:-1.4px}
h2{font-size:clamp(27px,4vw,40px);line-height:1.12;letter-spacing:-.7px;margin:10px 0 0}
h3{font-size:17.5px;letter-spacing:-.1px;font-weight:700}
p{margin:0}
.eyebrow{
  display:inline-block;color:var(--teal);text-transform:uppercase;
  letter-spacing:2.4px;font-size:12px;font-weight:700;font-family:var(--text);
}
.rule{
  height:4px;width:132px;border-radius:2px;margin:20px 0;
  background:linear-gradient(90deg,var(--teal),var(--teal2));
  transform-origin:left;
  transition:transform var(--t-rise) var(--ease-rise) 120ms;
}
html.anim .rule{transform:scaleX(0)}
.rule.center{margin-left:auto;margin-right:auto;transform-origin:center}
.lede{color:var(--body);font-size:clamp(17px,2.1vw,20px);max-width:620px;margin-top:6px}
.sub{color:var(--mut);font-size:16px;max-width:660px;margin-top:14px}
.muted{color:var(--mut);font-size:14.5px}

footer{border-top:1px solid var(--line);padding:26px 0 40px;color:var(--mut);font-size:14px}
footer .wrap{display:flex;flex-wrap:wrap;gap:14px;justify-content:space-between;align-items:center}
footer a{color:var(--mut);position:relative}
footer a:hover{color:var(--teal2)}

/* ================================================================
   3. BUTTONS — the only two conversion points on the page, so this is
   where the micro-interaction budget goes.
   ================================================================ */
.btn{
  --mx:0px;--my:0px;
  position:relative;display:inline-flex;align-items:center;gap:9px;
  font-family:var(--text);font-weight:700;font-size:16px;
  padding:14px 24px;border-radius:11px;border:1px solid transparent;
  text-decoration:none;cursor:pointer;isolation:isolate;overflow:hidden;
  transform:translate3d(var(--mx),var(--my),0);
  transition:transform var(--t-mid) var(--ease-soft),
             box-shadow var(--t-mid) var(--ease-soft),
             border-color var(--t-fast) var(--ease-soft);
}
.btn-primary{background:var(--teal);color:#06201C;box-shadow:0 8px 22px -8px rgba(25,184,166,.55)}
.btn-primary:hover{box-shadow:0 16px 40px -10px rgba(25,184,166,.75)}
.btn-outline{border-color:var(--line);color:var(--ink);background:rgba(255,255,255,.02)}
.btn-outline:hover{border-color:var(--teal);color:var(--teal2)}
/* A specular sweep, driven by the same pointer position as the magnet. It is
   opacity + transform on a pseudo-element, so it composites. */
.btn::after{
  content:'';position:absolute;inset:-40%;z-index:-1;opacity:0;
  background:radial-gradient(circle at var(--px,50%) var(--py,50%),
             rgba(255,255,255,.34),rgba(255,255,255,0) 55%);
  transition:opacity var(--t-mid) var(--ease-soft);
}
.btn:hover::after{opacity:1}
.btn:active{transform:translate3d(var(--mx),var(--my),0) scale(.975)}
.btn:focus-visible{outline:2px solid var(--teal2);outline-offset:3px}
.btn .arw{transition:transform var(--t-mid) var(--ease-rise)}
.btn:hover .arw{transform:translateX(4px)}
.cta-row{display:flex;flex-wrap:wrap;gap:14px;margin-top:30px}
/* On a phone the two calls to action wrap to their own lines anyway, and two
   different-width buttons stacked read as ragged. Make them one block. */
@media (max-width:480px){
  .cta-row{flex-direction:column;align-items:stretch}
  .cta-row .btn{justify-content:center}
}

/* ================================================================
   4. SURFACES — card, step, price. One hover language for all three:
   lift, hairline warms to teal, a teal bloom underneath.
   ================================================================ */
.card{
  position:relative;background:var(--panel);border:1px solid var(--line);
  border-radius:16px;padding:22px 22px 24px;
  transition:transform var(--t-mid) var(--ease-rise),
             border-color var(--t-mid) var(--ease-soft),
             box-shadow var(--t-mid) var(--ease-soft);
}
.card:hover{transform:translateY(-6px);border-color:#3A4A62;
  box-shadow:0 22px 44px -22px rgba(0,0,0,.85),0 0 0 1px rgba(25,184,166,.14)}
.card h3{margin-bottom:7px}
.card p{color:var(--body);font-size:15px}
.card .ic{
  width:38px;height:38px;border-radius:10px;margin-bottom:14px;
  display:grid;place-items:center;
  background:rgba(25,184,166,.12);border:1px solid rgba(25,184,166,.28);
  transition:transform var(--t-mid) var(--ease-rise),background var(--t-mid) var(--ease-soft);
}
.card:hover .ic{transform:translateY(-2px) scale(1.06);background:rgba(25,184,166,.2)}
.card .ic svg{width:19px;height:19px;stroke:var(--teal2);fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}

.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(268px,1fr));gap:16px;margin-top:36px}

/* ---- Steps: the teal spine draws itself as the section arrives ---- */
.steps{position:relative;margin-top:36px;display:grid;gap:16px}
.steps::before{
  content:'';position:absolute;left:31px;top:34px;bottom:34px;width:2px;
  background:linear-gradient(180deg,var(--teal),var(--teal-deep));
  border-radius:2px;transform-origin:top;
  transition:transform 1100ms var(--ease-rise) 160ms;
}
html.anim .steps::before{transform:scaleY(0)}
html.anim .steps.in::before{transform:scaleY(1)}
@media (max-width:560px){.steps::before{display:none}}
.step{
  position:relative;background:var(--panel);border:1px solid var(--line);
  border-radius:16px;padding:20px 22px 20px 74px;
  transition:transform var(--t-mid) var(--ease-rise),border-color var(--t-mid) var(--ease-soft);
}
.step:hover{transform:translateX(4px);border-color:#3A4A62}
.step .n{
  position:absolute;left:16px;top:20px;width:32px;height:32px;border-radius:50%;
  display:grid;place-items:center;background:var(--teal);color:#06201C;
  font-family:var(--display);font-weight:800;font-size:16px;
  box-shadow:0 0 0 6px var(--bg),0 0 22px -4px rgba(25,184,166,.9);
}
@media (max-width:560px){.step{padding-left:22px}.step .n{position:static;margin-bottom:12px}}
.step h3{margin-bottom:4px}
.step p{color:var(--body);font-size:15px}

/* ---- Pricing ---- */
.price-wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(258px,1fr));gap:16px;margin-top:36px}
.price{
  position:relative;background:var(--panel);border:1px solid var(--line);
  border-radius:18px;padding:26px 26px 28px;
  transition:transform var(--t-mid) var(--ease-rise),
             border-color var(--t-mid) var(--ease-soft),
             box-shadow var(--t-mid) var(--ease-soft);
}
.price:hover{transform:translateY(-8px);border-color:rgba(25,184,166,.6);
  box-shadow:0 28px 56px -26px rgba(0,0,0,.9)}
.price.feature{border-color:var(--teal);background:linear-gradient(180deg,#192A33,var(--panel) 62%)}
.price.feature::before{
  content:'';position:absolute;inset:-1px;border-radius:19px;z-index:-1;
  background:linear-gradient(180deg,rgba(25,184,166,.5),rgba(25,184,166,0) 70%);
  filter:blur(14px);opacity:.7;animation:breathe 5.5s var(--ease-soft) infinite alternate;
}
@keyframes breathe{from{opacity:.42}to{opacity:.9}}
.badge{
  display:inline-block;background:rgba(25,184,166,.14);color:var(--teal2);
  border:1px solid var(--teal);border-radius:999px;padding:4px 13px;
  font-size:12px;font-weight:700;letter-spacing:1.4px;margin-bottom:12px;
}
.num{font-family:var(--display);font-weight:800;font-size:50px;letter-spacing:-2px;line-height:1}
.per{color:var(--mut);font-size:15px;margin-left:7px}
ul.checks{list-style:none;padding:0;margin:18px 0 0}
ul.checks li{color:var(--body);font-size:15px;padding:5px 0 5px 28px;position:relative}
ul.checks li::before{content:'\2713';position:absolute;left:2px;top:5px;color:var(--green);font-weight:800}

/* ================================================================
   5. THE DEVICE STRIP — the only 3D on the site.
   Real screenshots in real bezels. Each phone starts turned away and
   rotates flat as it crosses the viewport, on a scroll-driven timeline
   so the whole effect costs the main thread nothing.
   ================================================================ */
.screens{position:relative;margin-top:40px}
.rail{
  display:flex;gap:clamp(18px,3vw,34px);overflow-x:auto;
  padding:34px max(var(--gut),calc((100vw - var(--wrap))/2)) 42px;scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;
  perspective:1600px;
}
.rail::-webkit-scrollbar{display:none}
.phone{flex:0 0 auto;scroll-snap-align:center;text-align:center;transform-style:preserve-3d}
.phone .body{
  position:relative;width:238px;padding:9px;border-radius:36px;
  background:linear-gradient(160deg,#3A4A62,#141C28 42%,#0A1017);
  box-shadow:0 34px 62px -26px rgba(0,0,0,.95),0 0 0 1px rgba(255,255,255,.05) inset;
  transform:rotateY(14deg) rotateX(3deg) translateZ(0);
  transition:transform 600ms var(--ease-rise),box-shadow 600ms var(--ease-soft);
  will-change:transform;
}
.phone .body::after{ /* screen glare, tied to the same tilt */
  content:'';position:absolute;inset:9px;border-radius:28px;pointer-events:none;
  background:linear-gradient(122deg,rgba(255,255,255,.16),rgba(255,255,255,0) 42%);
  opacity:.85;
}
.phone .body img{border-radius:28px;width:100%;height:auto;background:var(--bg)}
.phone .notch{
  position:absolute;top:16px;left:50%;transform:translateX(-50%);
  width:74px;height:19px;border-radius:11px;background:#05090E;z-index:2;
}
.phone span{display:block;margin-top:16px;color:var(--mut);font-size:13.5px;font-weight:700;
  letter-spacing:1.2px;text-transform:uppercase}
.phone:hover .body{transform:rotateY(0deg) rotateX(0deg) translateY(-10px) scale(1.02);
  box-shadow:0 46px 80px -28px rgba(0,0,0,1),0 0 46px -14px rgba(25,184,166,.45)}
/* Alternate the resting turn so the row reads as a fanned deck, not a queue. */
.phone:nth-child(even) .body{transform:rotateY(-14deg) rotateX(3deg)}

@supports (animation-timeline:view()){
  .phone .body{
    animation:swing linear both;
    animation-timeline:view(inline);
    animation-range:entry 0% exit 100%;
  }
  @keyframes swing{
    0%  {transform:rotateY(26deg)  rotateX(5deg) translateY(16px) scale(.94)}
    50% {transform:rotateY(0deg)   rotateX(0deg) translateY(-10px) scale(1)}
    100%{transform:rotateY(-26deg) rotateX(5deg) translateY(16px) scale(.94)}
  }
  .phone:hover .body{animation-play-state:paused}
}
.rail-hint{color:var(--faint);font-size:13px;text-align:center;margin-top:-14px}

/* ================================================================
   6. REVEAL — the one-shot rise, fired by IntersectionObserver.
   --d is the per-item stagger, set inline or by site.js.
   ================================================================ */
/* NOTE the `html.anim` prefix on every hidden-start rule below. It is set by a
   two-line inline script in each page's <head>, and it is the difference
   between "the reveal did not run" and "the page is blank".

   opacity:0 in plain CSS means the content depends on JavaScript to become
   visible. If site.js 404s, throws, is blocked, or — as happened while
   verifying this — the document is never rendered so IntersectionObserver
   never delivers a callback, the visitor gets an empty page and no error.
   Gating on a class that only JS can add inverts that: no JS, no hiding. */
html.anim .rise{
  opacity:0;transform:translate3d(0,var(--rise),0);
  transition:opacity var(--t-rise) var(--ease-rise) var(--d,0ms),
             transform var(--t-rise) var(--ease-rise) var(--d,0ms);
  will-change:transform,opacity;
}
html.anim .rise.in{opacity:1;transform:none;will-change:auto}
html.anim .rise.in .rule{transform:scaleX(1)}

/* Hero entrance: same rise, but on load rather than on scroll — the fold is
   already in view, so waiting for an observer would show a blank hero. */
html.anim .lift{opacity:0;transform:translate3d(0,var(--rise),0);
  animation:lift var(--t-rise) var(--ease-rise) var(--d,0ms) forwards}
@keyframes lift{to{opacity:1;transform:none}}

/* ================================================================
   7. CROSS-PAGE TRANSITION
   Real cross-document view transitions where supported; the .lift
   entrance is the graceful degradation everywhere else.
   ================================================================ */
@view-transition{navigation:auto}
::view-transition-old(root){animation:vt-out 200ms var(--ease-soft) both}
::view-transition-new(root){animation:vt-in 420ms var(--ease-rise) both}
@keyframes vt-out{to{opacity:0;transform:translateY(-10px)}}
@keyframes vt-in{from{opacity:0;transform:translateY(18px)}}

/* ================================================================
   8. LEGAL / UTILITY PAGE LAYOUTS (terms, privacy, the Stripe and
   QuickBooks return pages) — same shell, no marketing furniture.
   ================================================================ */
.doc{max-width:760px;margin:0 auto;padding:36px var(--gut) 72px}
.doc h1{font-size:clamp(32px,5vw,44px)}
.doc h2{font-size:21px;margin:34px 0 8px}
.doc p,.doc li{color:var(--body);font-size:15.5px}
.doc p{margin-bottom:14px}
.doc ul{margin:0 0 14px;padding-left:22px}
.doc li{margin-bottom:4px}
.doc .card{margin-top:24px;padding:8px 24px 24px}

/* A notice page is a dead end someone was bounced to by Stripe or Intuit
   mid-task. It gets the shell so it is recognisably the same product, and
   nothing else — no nav to wander into, no marketing. The headline is sized
   down from the landing page's: a 68px "That link expired" is the site
   shouting at someone it just inconvenienced. */
.notice{min-height:76svh;display:grid;place-items:center;padding:48px var(--gut)}
.notice .inner{max-width:540px;text-align:center}
.notice h1{font-size:clamp(29px,4.4vw,40px);letter-spacing:-.6px}
.notice p{color:var(--body);font-size:16.5px}
.notice p + p{margin-top:16px}
.notice .mark{width:50px;height:auto;margin:0 auto 26px}

/* ================================================================
   9. REDUCED MOTION — asked for less, gets less. Everything that
   animates in is forced to its final state, not merely sped up.
   ================================================================ */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;
  }
  html.anim .rise,html.anim .lift{opacity:1!important;transform:none!important}
  .rule{transform:scaleX(1)!important}
  .steps::before{transform:scaleY(1)!important}
  .phone .body,.phone:nth-child(even) .body{transform:none!important;animation:none!important}
  .sky::before,.sky::after{animation:none!important}
  .progress{display:none}
  @view-transition{navigation:none}
}

/* ==================================================================
   10. THE PINNED STAGE — the centrepiece
   ------------------------------------------------------------------
   The phone holds still and the APP moves inside it. Five real screens
   cross-dissolve as you scroll, each with its own line of copy, so the
   scroll gesture is doing the same thing a thumb does — which is the
   only reason a pin like this earns its length. A pinned section that
   just parallaxes a decoration is a toll booth.

   PROGRESSIVE ENHANCEMENT, and this direction is deliberate. The MARKUP
   is five self-contained slides — phone frame, screen, caption — that
   lay out as a horizontal rail with no CSS and no JS at all. site.js
   then UPGRADES that into the stage by lifting the four other screens
   into the first slide's frame, so there is exactly one bezel, one
   shadow and one notch on screen instead of five stacked copies.

   If the script never runs, nobody gets a broken pin — they get the
   rail, which is what shipped this morning and is fine. Same rule as
   the reveals: the enhanced state is the extra, never the baseline.
   ================================================================== */

/* ---- Baseline: the rail. No .staged class, no JS, narrow window. ---- */
.stage-wrap{position:relative}
.stage{
  display:flex;gap:clamp(18px,3vw,34px);overflow-x:auto;
  padding:34px max(var(--gut),calc((100vw - var(--wrap))/2)) 42px;
  scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:none;
  perspective:1600px;
}
.stage::-webkit-scrollbar{display:none}
.slide{flex:0 0 auto;width:238px;scroll-snap-align:center}
.slide .beat{margin-top:16px;text-align:center}
.slide .beat h3{font-size:16px;margin-bottom:5px}
.slide .beat p{color:var(--mut);font-size:14px;margin:0}

/* ---- Upgraded: the pin. Only ever added by site.js. ---- */
.staged .stage-wrap{height:calc(var(--beats,5) * 88svh)}
.staged .stage{
  position:sticky;top:0;height:100svh;
  display:grid;place-items:center;
  overflow:visible;padding:0 var(--gut);scroll-snap-type:none;
}
.staged .stage-inner{
  width:min(var(--wrap),100%);
  display:grid;grid-template-columns:1fr auto;align-items:center;
  gap:clamp(28px,5vw,72px);
}
/* Every caption occupies the SAME grid cell, so they cross-dissolve in
   place instead of the column changing height under the reader. */
.staged .copy{display:grid}
.staged .slide{
  grid-area:1/1;width:auto;
  opacity:var(--o,0);
  transform:translate3d(0,var(--y,0px),0);
  will-change:transform,opacity;
  pointer-events:none;
}
.staged .slide[data-live]{pointer-events:auto}
.staged .slide:first-child{--o:1}
.staged .slide .beat{margin:0;text-align:left}
.staged .slide .beat .n{
  display:inline-block;font-family:var(--display);font-weight:800;font-size:12px;
  letter-spacing:2px;color:var(--teal);margin-bottom:10px;
}
.staged .slide .beat h3{font-size:clamp(24px,3.2vw,36px);letter-spacing:-.6px;margin-bottom:10px}
.staged .slide .beat p{color:var(--body);font-size:clamp(15px,1.7vw,17.5px);max-width:30ch}
.staged .slide .phone{display:none}   /* the frames are folded into one */

/* The single surviving phone, and the screens JS moved into it. */
.stage-phone{display:none}
.staged .stage-phone{display:block;position:relative}
.staged .stage-phone .body{
  position:relative;width:clamp(230px,26vw,278px);padding:9px;border-radius:38px;
  background:linear-gradient(160deg,#3A4A62,#141C28 42%,#0A1017);
  box-shadow:0 40px 80px -30px rgba(0,0,0,.95),0 0 0 1px rgba(255,255,255,.05) inset;
  transform:perspective(1400px) rotateY(var(--tilt,-8deg)) rotateX(2deg);
  transform-style:preserve-3d;
}
.staged .stage-phone .screens{position:relative;border-radius:29px;overflow:hidden;background:var(--bg)}
/* The screens PUSH, they do not dissolve. A cross-fade between two
   text-heavy dark screenshots gives you both sets of numbers at half
   strength on top of each other — it reads as a rendering fault, and it
   looked like one. An opaque slide-up is also simply what a phone does,
   which is the whole point of the frame being there.
   The one being covered drops back and dims, so the stack has depth. */
.staged .stage-phone .screens img{
  width:100%;height:auto;display:block;grid-area:1/1;
  transform:translate3d(0,var(--ty,100%),0) scale(var(--s,1));
  transform-origin:50% 0%;
  filter:brightness(var(--b,1));
  will-change:transform,filter;
}
.staged .stage-phone .screens{display:grid}
.staged .stage-phone .screens img:first-child{--ty:0%}
/* Specular glare that SLIDES with the scroll — the one cue that sells a
   pane of glass rather than a rounded rectangle. Its position is driven
   from the same progress value as everything else. */
.staged .stage-phone .body::after{
  content:'';position:absolute;inset:9px;border-radius:29px;pointer-events:none;z-index:3;
  background:linear-gradient(122deg,rgba(255,255,255,0) 28%,rgba(255,255,255,.20) 46%,rgba(255,255,255,0) 62%);
  transform:translate3d(var(--glare,0px),0,0);
}
.staged .stage-phone .notch{
  position:absolute;top:16px;left:50%;transform:translateX(-50%);
  width:78px;height:20px;border-radius:11px;background:#05090E;z-index:4;
}
/* The beat pips — where you are, and a tap target to jump. */
.staged .pips{display:flex;gap:9px;margin-top:26px}
.pips{display:none}
.staged .pips button{
  width:26px;height:4px;border:0;padding:0;border-radius:2px;cursor:pointer;
  background:var(--line);transition:background var(--t-mid) var(--ease-soft),transform var(--t-mid) var(--ease-rise);
}
.staged .pips button[aria-current="true"]{background:var(--teal);transform:scaleX(1.5);transform-origin:left}
.staged .pips button:focus-visible{outline:2px solid var(--teal2);outline-offset:3px}

@media (max-width:860px){
  .staged .stage-inner{grid-template-columns:1fr;justify-items:center;gap:26px}
  .staged .slide .beat{text-align:center}
  .staged .slide .beat p{max-width:34ch;margin-inline:auto}
  .staged .stage-phone{order:-1}
}

/* ==================================================================
   11. THE ROUTE LINE
   A dashed survey line down the left gutter that DRAWS as you scroll,
   with a pin that lights at each section. It is the one ornament on the
   page that is about this product specifically: a route with stops on
   it, which is what the app is. Pure stroke-dashoffset on one SVG.
   ================================================================== */
.routeline{
  position:fixed;left:max(14px,calc((100vw - var(--wrap))/2 - 46px));top:0;
  width:26px;height:100svh;z-index:2;pointer-events:none;display:none;
}
.staged .routeline,html.anim .routeline{display:block}
@media (max-width:1180px){.routeline{display:none!important}}
.routeline .track{stroke:var(--line);stroke-width:2;fill:none;stroke-dasharray:6 8}
.routeline .live{
  stroke:var(--teal);stroke-width:2;fill:none;stroke-linecap:round;
  stroke-dasharray:var(--len,1000);
  stroke-dashoffset:calc(var(--len,1000) * (1 - var(--road,0)));
  filter:drop-shadow(0 0 6px rgba(25,184,166,.6));
}
.routeline .dot{fill:var(--teal2);filter:drop-shadow(0 0 7px rgba(87,220,201,.9))}
.routeline .stop{
  fill:var(--bg);stroke:var(--line);stroke-width:2;
  transition:fill var(--t-mid) var(--ease-soft),stroke var(--t-mid) var(--ease-soft),r var(--t-mid) var(--ease-rise);
}
.routeline .stop.on{fill:var(--teal);stroke:var(--teal2);r:4.5}

/* ==================================================================
   12. DEPTH ON ENTRY + CURSOR SPOTLIGHT
   Cards come up off their back edge rather than sliding flat. It is a
   small rotation — 7 degrees — because the point is weight, not a
   flourish; anything larger reads as a slot machine.
   ================================================================== */
html.anim .grid,html.anim .price-wrap{perspective:1200px}
html.anim .grid .rise,html.anim .price-wrap .rise{
  transform:translate3d(0,var(--rise),0) rotateX(7deg);
  transform-origin:50% 100%;
}
html.anim .grid .rise.in,html.anim .price-wrap .rise.in{transform:none}

/* A light that follows the cursor across a card. Fine pointers only —
   site.js never attaches the listener on a touch screen. */
.card,.price{--sx:50%;--sy:50%}
.card::before,.price::before{
  content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  opacity:0;transition:opacity var(--t-mid) var(--ease-soft);
  background:radial-gradient(340px circle at var(--sx) var(--sy),rgba(25,184,166,.13),transparent 62%);
}
.card:hover::before,.price:hover::before{opacity:1}
.price.feature::before{opacity:0}          /* it already has its own bloom */
.price.feature:hover::before{opacity:1}

/* ==================================================================
   13. WORD-BY-WORD HEADLINES
   site.js wraps each word; the mask is the word's own line box, so the
   words rise out of nothing rather than fading on the spot. Consistent
   with the one rule. Falls back to a whole-block rise if the split does
   not happen.
   ================================================================== */
/* padding+negative margin so overflow:hidden clips the RISE and not the
   descenders — without it every g, y and p in a headline loses its tail. */
.w{display:inline-block;overflow:hidden;vertical-align:top;
   padding-bottom:.14em;margin-bottom:-.14em}
.w > i{display:inline-block;font-style:inherit;
   transition:transform 820ms var(--ease-rise) var(--wd,0ms)}
/* Hidden ONLY while the gate class is on and the words have not been
   released. Same contract as the reveals: no JS, nothing hidden. */
html.anim .split .w > i{transform:translate3d(0,110%,0)}
html.anim .split.go .w > i{transform:none}
/* THE GUARANTEE. `.go` sets the transition's END value, which is correct
   and still not enough: a transition only reaches its end value if time
   advances for it, and in a document that is never rendered it does not.
   Measured on the terms page — 2.5 seconds after release, the last word
   was still sitting at translateY(36px), clipped, forever.
   `.done` drops the transition entirely, so the computed value snaps to
   final whether or not a single frame was ever painted. site.js adds it
   after the animation would have finished. */
html.anim .split.done .w > i{transition:none;transform:none}

@media (prefers-reduced-motion:reduce){
  .staged .stage-wrap{height:auto!important}
  .staged .stage{position:static!important;height:auto!important}
  .staged .slide{opacity:1!important;transform:none!important}
  .staged .stage-phone .screens img{transform:none!important}
  .routeline{display:none!important}
  .split .w > i{transform:none!important}
  html.anim .grid .rise,html.anim .price-wrap .rise{transform:none!important}
}

/* ==================================================================
   14. THE PLAYABLE OPTIMISER
   COMPETITIVE_AUDIT.md: Housecall Pro has no automatic route planning
   at all, and FieldRoutes rebuilds routes overnight in a batch and
   concedes that makes same-day changes expensive. We reorder on demand.
   That is a hard thing to say in a sentence and an easy thing to show,
   so the visitor gets the button.
   ================================================================== */
.opt{
  display:grid;grid-template-columns:1fr 300px;gap:clamp(20px,3vw,36px);
  align-items:center;margin-top:34px;
  background:var(--panel);border:1px solid var(--line);border-radius:20px;
  padding:clamp(18px,2.4vw,28px);
}
.opt-map{background:var(--sunken);border:1px solid var(--line);border-radius:14px;overflow:hidden}
.opt-map svg{display:block;width:100%;height:auto}
.opt-path{
  fill:none;stroke:var(--teal);stroke-width:2.5;stroke-linejoin:round;stroke-linecap:round;
  filter:drop-shadow(0 0 7px rgba(25,184,166,.45));
}
.opt-pin{fill:var(--bg);stroke:var(--teal2);stroke-width:2.5}
.opt-pin.home{fill:var(--teal);stroke:var(--teal2)}
.opt-num{fill:var(--ink);font-family:var(--display);font-weight:800;font-size:13px;text-anchor:middle}
.opt-stat .label{
  display:block;color:var(--mut);font-size:12px;font-weight:700;
  letter-spacing:1.4px;text-transform:uppercase;margin-bottom:6px;
}
.opt-miles{font-size:clamp(38px,4.4vw,52px);color:var(--ink)}
.opt-saved{margin-top:6px;color:var(--green);font-weight:700;min-height:1.4em}
.opt-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:20px}
.opt-actions .btn{font-size:15px;padding:12px 18px}
.opt-note{margin-top:16px;font-size:13.5px;color:var(--faint)}
.opt-run[disabled]{opacity:.5;pointer-events:none}
@media (max-width:820px){.opt{grid-template-columns:1fr}}

/* ==================================================================
   15. CINEMATIC GROUND — grain and vignette
   A completely static pair of overlays. No animation, no per-frame
   cost, and it is what stops a flat dark page reading as "black div":
   real footage has grain and real lenses fall off at the edges.
   ================================================================== */
.grain,.vig{position:fixed;inset:0;pointer-events:none;z-index:1}
.vig{background:radial-gradient(ellipse 120% 85% at 50% 40%,transparent 45%,rgba(0,0,0,.55) 100%)}
.grain{
  opacity:.038;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* The bar, the route line and the content all sit above them. */
.topbar{z-index:50}
main,footer{position:relative;z-index:2}

/* ==================================================================
   16. CARDS THAT TURN TOWARD THE CURSOR
   The lift and the spotlight were already there; this adds the axis
   they were missing. Kept to ±5deg — enough to feel like an object,
   not enough to make anyone seasick reading the text on it.
   ================================================================== */
html.anim .card,html.anim .price{
  transform:perspective(900px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg));
}
html.anim .card:hover,html.anim .price:hover{
  transform:perspective(900px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)) translateY(-6px);
}
html.anim .price:hover{transform:perspective(900px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)) translateY(-8px)}
/* The entrance still owns the transform while it is running. */
html.anim .grid .rise:not(.in),html.anim .price-wrap .rise:not(.in){
  transform:translate3d(0,var(--rise),0) rotateX(7deg)!important;
}

@media (prefers-reduced-motion:reduce){
  .grain{display:none}
  html.anim .card,html.anim .price,
  html.anim .card:hover,html.anim .price:hover{transform:none!important}
}

/* ==================================================================
   17. TRADE PICKER
   The page already claims the app "reshapes itself around how your
   business works". This is that claim made checkable — the chips are
   the real INDUSTRIES list from src/lib/industries.ts, and the panel
   shows the actual starter services, the actual vocabulary word and
   the extras that actually switch on.
   ================================================================== */
.trades{margin-top:34px}
.chips{display:flex;flex-wrap:wrap;gap:9px}
.chip{
  font-family:var(--text);font-weight:700;font-size:14.5px;
  padding:9px 16px;border-radius:999px;cursor:pointer;
  background:var(--panel);color:var(--mut);border:1px solid var(--line);
  transition:color var(--t-fast) var(--ease-soft),border-color var(--t-fast) var(--ease-soft),
             background var(--t-fast) var(--ease-soft),transform var(--t-mid) var(--ease-rise);
}
.chip:hover{color:var(--ink);border-color:#3A4A62;transform:translateY(-2px)}
.chip.on{
  background:rgba(25,184,166,.14);border-color:var(--teal);color:var(--teal2);
  box-shadow:0 0 22px -8px rgba(25,184,166,.8);
}
.chip:focus-visible{outline:2px solid var(--teal2);outline-offset:3px}
.trade-panel{
  margin-top:20px;background:var(--panel);border:1px solid var(--line);border-radius:18px;
  padding:clamp(20px,2.6vw,30px);
  display:grid;grid-template-columns:1.1fr 1fr;gap:clamp(20px,3vw,44px);
}
@media (max-width:760px){.trade-panel{grid-template-columns:1fr;gap:22px}}
.trade-col .label{
  display:block;color:var(--mut);font-size:12px;font-weight:700;
  letter-spacing:1.4px;text-transform:uppercase;margin-bottom:10px;
}
.trade-col .label + .label,.vocab + .label{margin-top:24px}
.vocab{margin-bottom:0}
.svc,.feat{list-style:none;padding:0;margin:0}
.svc li,.feat li{
  color:var(--body);font-size:15.5px;padding:7px 0 7px 26px;position:relative;
  opacity:0;transform:translate3d(0,10px,0);
  animation:tradein 460ms var(--ease-rise) var(--wd,0ms) forwards;
}
html:not(.anim) .svc li,html:not(.anim) .feat li{opacity:1;transform:none;animation:none}
@keyframes tradein{to{opacity:1;transform:none}}
.svc li::before{content:'';position:absolute;left:2px;top:15px;width:9px;height:9px;
  border-radius:2px;background:var(--teal);opacity:.85}
.feat li::before{content:'\2713';position:absolute;left:2px;top:7px;color:var(--green);font-weight:800}
.feat li.none::before{content:'\2014';color:var(--faint)}
.vocab{font-family:var(--display);font-weight:800;font-size:26px;color:var(--teal2);letter-spacing:-.4px}
.trade-ex{margin-top:20px;font-size:14px;color:var(--faint)}

/* ==================================================================
   18. FAQ
   Not decoration — the questions a one-truck owner actually stalls on.
   <details> so it works with no JS and is keyboard- and
   screen-reader-correct for free; the height animation is added on top.
   ================================================================== */
.faq{margin-top:32px;display:grid;gap:10px}
.faq details{
  background:var(--panel);border:1px solid var(--line);border-radius:14px;
  transition:border-color var(--t-mid) var(--ease-soft),background var(--t-mid) var(--ease-soft);
}
.faq details[open]{border-color:#3A4A62;background:linear-gradient(180deg,#192A33,var(--panel) 70%)}
.faq summary{
  list-style:none;cursor:pointer;padding:17px 52px 17px 20px;position:relative;
  font-family:var(--display);font-weight:700;font-size:17px;color:var(--ink);
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:'';position:absolute;right:22px;top:50%;width:11px;height:11px;
  border-right:2px solid var(--teal);border-bottom:2px solid var(--teal);
  transform:translateY(-70%) rotate(45deg);
  transition:transform var(--t-mid) var(--ease-rise);
}
.faq details[open] summary::after{transform:translateY(-25%) rotate(-135deg)}
.faq summary:focus-visible{outline:2px solid var(--teal2);outline-offset:-2px;border-radius:14px}
.faq .a{padding:0 20px 20px;color:var(--body);font-size:15.5px}
.faq .a p{margin:0 0 10px}
.faq .a p:last-child{margin:0}
html.anim .faq details[open] .a{animation:faqin 380ms var(--ease-rise) both}
@keyframes faqin{from{opacity:0;transform:translate3d(0,-8px,0)}}

/* A skip link, because the pinned stage puts a lot of scroll between the
   top of the page and the content for anyone tabbing through. */
.skip{
  position:absolute;left:-9999px;top:8px;z-index:100;
  background:var(--teal);color:#06201C;font-weight:700;padding:10px 16px;border-radius:9px;
}
.skip:focus{left:12px}

@media (prefers-reduced-motion:reduce){
  .svc li,.feat li{opacity:1!important;transform:none!important;animation:none!important}
  html.anim .faq details[open] .a{animation:none!important}
}

/* ==================================================================
   19. THE OPENER — a curtain that lifts off the page
   ------------------------------------------------------------------
   WHY IT IS BUILT THE WAY IT IS, which matters more than how it looks:

   A full-screen overlay is the single most dangerous thing on this site.
   It covers EVERYTHING. If anything it depends on fails to happen, the
   visitor gets a blank navy rectangle and no product. This page has
   already produced five separate "invisible until something that might
   not happen, happens" bugs, so the opener is built to be structurally
   incapable of becoming the sixth:

   1. It is TWO PSEUDO-ELEMENTS ON <html>, not injected markup. There is
      no DOM to insert, so it cannot race the parser, cannot depend on
      <body> existing, and cannot flash the hero before covering it —
      the class that summons it is set by the inline <head> script,
      before first paint.
   2. It only ever exists while `html.intro` is set, and the same inline
      script that adds that class also schedules its removal. site.js is
      not involved at all: if site.js 404s, is blocked, or throws, the
      curtain still lifts on time.
   3. Removal is a TIMER, never an animationend listener. A document that
      never paints runs no animations and fires no animationend, and the
      whole point is that the page underneath must appear regardless.
   4. pointer-events: none throughout, so nothing is ever trapped behind
      it even for the second and a half it is up.

   The look: the mark rises into focus, a specular sweep crosses the
   panel, then the whole sheet lifts out of frame trailing a lit teal
   edge and a real drop shadow — so it reads as a physical sheet with
   thickness being pulled away, not a rectangle fading out. Same rule as
   everything else on this page: it RISES.
   ================================================================== */
html.intro{overflow:hidden}          /* only while the curtain is up */

html.intro::before{
  content:'';position:fixed;inset:0;z-index:200;pointer-events:none;
  background-color:var(--bg);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='228 250 568 516'%3E%3Cpath d='M280 518L512 322L744 518' stroke='%2357DCC9' stroke-width='104' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M280 714L512 518L744 714' stroke='%2319B8A6' stroke-width='104' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"),
    linear-gradient(rgba(42,54,72,.34) 1px,transparent 1px),
    linear-gradient(90deg,rgba(42,54,72,.34) 1px,transparent 1px),
    radial-gradient(circle at 50% 44%,rgba(25,184,166,.20),rgba(25,184,166,0) 58%);
  background-repeat:no-repeat,repeat,repeat,no-repeat;
  background-position:center 46%,0 0,0 0,center;
  background-size:96px auto,66px 66px,66px 66px,cover;
  /* One box-shadow does the whole bottom edge: a lit teal hairline, its
     bloom, and the shadow the sheet casts on the page underneath. */
  box-shadow:0 2px 0 0 var(--teal2),0 8px 34px 0 rgba(25,184,166,.5),
             0 34px 64px -12px rgba(0,0,0,.95);
  animation:curtain 1750ms cubic-bezier(.16,1,.3,1) both;
}
@keyframes curtain{
  0%   {transform:translate3d(0,0,0);background-size:80px auto,66px 66px,66px 66px,cover;opacity:1}
  10%  {background-size:80px auto,66px 66px,66px 66px,cover}
  42%  {transform:translate3d(0,0,0);background-size:96px auto,66px 66px,66px 66px,cover}
  60%  {transform:translate3d(0,0,0)}
  100% {transform:translate3d(0,-101%,0)}
}

/* The specular sweep — a bar of light crossing the sheet, gone before
   the lift begins so the two never fight for attention. */
html.intro::after{
  content:'';position:fixed;inset:-30% -60%;z-index:201;pointer-events:none;
  background:linear-gradient(104deg,rgba(255,255,255,0) 42%,rgba(255,255,255,.13) 50%,rgba(255,255,255,0) 58%);
  animation:sweep 1150ms cubic-bezier(.22,.61,.36,1) both;
}
@keyframes sweep{
  0%,18% {transform:translate3d(-55%,0,0);opacity:0}
  40%    {opacity:1}
  85%,100%{transform:translate3d(55%,0,0);opacity:0}
}

/* Asked for less motion, or the curtain was already seen this visit:
   the inline script never adds the class, so none of the above exists. */
@media (prefers-reduced-motion:reduce){
  html.intro{overflow:auto}
  html.intro::before,html.intro::after{display:none}
}

/* ==================================================================
   20. THE HERO SCENE
   ------------------------------------------------------------------
   The first screen was a column of text with the right half of the
   viewport empty, and everything worth looking at was below the fold.
   This is the fix: a real screenshot in a real bezel, held in actual
   3D, with two cards floating in front of it at their own depth.

   It is genuinely three-dimensional rather than a picture of depth —
   the phone and the cards live on separate Z planes inside one
   perspective, so they part and converge as the scene turns, which is
   the thing a flat mock-up cannot fake. Turning it is pointer-driven,
   with a slow idle float underneath so it is alive even when the
   cursor is somewhere else.

   TRANSFORM OWNERSHIP: the float is an ANIMATION and the tilt is a
   TRANSFORM, and an animation on the transform property wins outright.
   They are therefore on separate elements — .art-float animates, its
   child .art-phone is turned. Putting both on one element silently
   drops the tilt, which is the sort of thing that looks like a broken
   pointer listener for an hour.
   ================================================================== */
.hero{display:grid;grid-template-columns:1.02fr .98fr;gap:clamp(20px,3vw,56px);align-items:center}
/* MOBILE GETS THE SCENE TOO — and hiding it was the wrong call.
   The reasoning was "the phone rail is directly beneath, a second phone
   here is just a taller page". That is a desktop argument. On a phone the
   rail is BELOW the fold too, so hiding this left the entire first screen
   as a headline and two buttons — which is exactly what it was reported
   as: flat. The first screen is the one everybody sees and most of them
   see it on a phone. */
@media (max-width:920px){
  .hero{grid-template-columns:1fr;gap:0}
  .hero-art{
    min-height:clamp(290px,72vw,400px);
    margin-top:clamp(22px,5vw,34px);
    perspective:1000px;          /* nearer camera = stronger turn on a small screen */
  }
  .art-phone{width:clamp(168px,43vw,206px)}
  /* One card at this size. Two crowd a 375px screen and neither reads. */
  .art-chip.c2{display:none}
  /* Sits over the BEZEL, not the screen. At 12% it landed straight across
     the revenue tile — the same mistake as the desktop pass, made again in
     a narrower box. Over the status-bar strip it occludes nothing, and
     left:3% with a smaller Z keeps the rounded corner inside the viewport
     instead of bleeding off it, which read as clipping rather than depth. */
  .art-chip.c1{--z:58px;left:5%;top:-3%;padding:10px 13px}
  .art-chip b{font-size:13.5px}
  .art-chip i{font-size:11.5px}
  /* Wrapping rather than nowrap: a fixed-width card at 375px is an
     overflow waiting to happen, and body{overflow-x:clip} would hide the
     evidence instead of the cause. */
  .art-chip{white-space:normal;max-width:min(74vw,290px)}
}

.hero-art{
  position:relative;min-height:clamp(420px,46vw,560px);
  perspective:1500px;perspective-origin:50% 44%;
  --rx:6deg;--ry:-15deg;
}
.art-glow{
  position:absolute;left:50%;top:52%;width:118%;aspect-ratio:1;
  transform:translate(-50%,-50%);pointer-events:none;
  background:radial-gradient(circle,rgba(87,220,201,.30) 0%,rgba(25,184,166,.34) 26%,rgba(25,184,166,0) 64%);
  filter:blur(28px);
  animation:artpulse 6.5s var(--ease-soft) infinite alternate;
}
@keyframes artpulse{from{opacity:.55;transform:translate(-50%,-50%) scale(.94)}
                    to  {opacity:1;transform:translate(-50%,-50%) scale(1.06)}}

.art-float{
  position:relative;height:100%;display:grid;place-items:center;
  transform-style:preserve-3d;
  animation:float 7.5s var(--ease-soft) infinite alternate;
}
@keyframes float{from{transform:translate3d(0,-10px,0)}to{transform:translate3d(0,12px,0)}}

.art-phone{
  position:relative;width:clamp(212px,19vw,258px);
  transform-style:preserve-3d;
  transform:rotateY(var(--ry)) rotateX(var(--rx));
  transition:transform 500ms var(--ease-soft);
  will-change:transform;
}
.art-phone .body{
  position:relative;padding:9px;border-radius:38px;
  background:linear-gradient(160deg,#42546E,#141C28 44%,#080D14);
  box-shadow:0 50px 90px -30px rgba(0,0,0,.98),
             0 0 0 1px rgba(255,255,255,.06) inset,
             0 0 90px -14px rgba(25,184,166,.62);
  /* A real reflection under the glass. One line, GPU-composited, and
     supported on iOS Safari — which is where this was reported flat. */
  -webkit-box-reflect:below 10px linear-gradient(transparent 62%,rgba(255,255,255,.18));
}
.art-phone img{width:100%;height:auto;display:block;border-radius:29px;background:var(--bg)}
.art-phone .notch{
  position:absolute;top:16px;left:50%;transform:translateX(-50%);
  width:78px;height:20px;border-radius:11px;background:#05090E;z-index:3;
}
/* The sheen tracks the turn, so the glass catches light from a fixed
   source instead of carrying a highlight painted onto it. */
.art-phone .body::after{
  content:'';position:absolute;inset:9px;border-radius:29px;pointer-events:none;
  background:linear-gradient(115deg,rgba(255,255,255,.22),rgba(255,255,255,.04) 34%,rgba(255,255,255,0) 56%);
  opacity:calc(.55 + (var(--ry) / -40deg) * .45);
}

/* The cards sit in FRONT of the phone on their own Z planes, so they
   swing wider than it does as the scene turns — the parallax between
   them is what sells the depth. */
.art-chip{
  position:absolute;display:grid;grid-template-columns:auto 1fr;gap:2px 10px;
  align-items:center;padding:12px 16px;border-radius:14px;
  background:rgba(22,31,44,.86);border:1px solid var(--line);
  -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
  box-shadow:0 26px 50px -22px rgba(0,0,0,.95);
  transform:translate3d(0,0,var(--z,90px)) rotateY(var(--ry)) rotateX(var(--rx));
  transition:transform 500ms var(--ease-soft);
  white-space:nowrap;
}
.art-chip b{grid-column:2;font-family:var(--display);font-weight:700;font-size:14.5px;color:var(--ink)}
.art-chip i{grid-column:2;font-style:normal;font-size:12.5px;color:var(--mut)}
.art-chip .tick,.art-chip .dot{grid-row:1/3;display:grid;place-items:center;
  width:26px;height:26px;border-radius:8px;font-size:14px;font-weight:800}
.art-chip .tick{background:rgba(87,178,106,.18);color:var(--green);border:1px solid rgba(87,178,106,.4)}
.art-chip .dot{background:rgba(25,184,166,.16);border:1px solid rgba(25,184,166,.45)}
.art-chip .dot::after{content:'';width:8px;height:8px;border-radius:50%;background:var(--teal2)}
/* Positioned to CLEAR the screen, not decorate it. The first pass put c1
   straight over the revenue headline — the single most persuasive number in
   the whole screenshot — which is a card obscuring the reason it exists. */
.art-chip.c1{--z:120px;left:-15%;top:19%;animation:chipfloat 6s var(--ease-soft) infinite alternate}
.art-chip.c2{--z:96px;right:-15%;bottom:17%;animation:chipfloat 7.2s var(--ease-soft) .8s infinite alternate-reverse}
@keyframes chipfloat{from{margin-top:-7px}to{margin-top:9px}}

@media (prefers-reduced-motion:reduce){
  .art-float,.art-glow,.art-chip{animation:none!important}
  .art-phone,.art-chip{transition:none!important}
}

/* ==================================================================
   21. MOBILE FIRST-SCREEN BUDGET
   ------------------------------------------------------------------
   Showing the scene on mobile was necessary and not sufficient: on a
   375x812 phone the headline, five lines of lede, two stacked
   full-width buttons and the trust line consumed the entire viewport,
   leaving about 130px of phone poking in at the bottom. A sliver reads
   as "cut off", not as depth.

   So this is a budget, not a restyle. Every rule below buys vertical
   space back — a few points of type here, a few pixels of margin there
   — and together they hand roughly 110px to the scene, which is the
   difference between a sliver and an object. No copy is cut: every word
   on the desktop hero is still on the phone.
   ================================================================== */
@media (max-width:920px){
  #top{padding-top:clamp(16px,3.5vw,28px)!important}
  h1{font-size:clamp(33px,8.4vw,44px);line-height:1.06}
  .hero-copy .rule{margin:14px 0}
  .hero-copy .lede{font-size:16.5px;line-height:1.5}
  .hero-copy .cta-row{margin-top:22px;gap:10px}
  .hero-copy .cta-row .btn{padding:13px 20px}
  .hero-copy .muted{font-size:13.5px;margin-top:14px!important}
  .hero-art{margin-top:10px;min-height:clamp(300px,76vw,420px)}
  /* Pull the phone to the top of its box so the maximum amount of it is
     above the fold rather than centred in space that is off-screen. */
  .art-float{place-items:start center;padding-top:48px}

  /* A phone is held in daylight. The ambient layers were tuned on a big
     dark desktop panel and simply are not there on a bright screen. */
  .sky::before{opacity:.72}
  .sky::after{opacity:.62}
  .art-glow{filter:blur(22px)}
}
