/* ===========================================================================
   Pillow Client — landing composition
   Reference: assets-src/reference.jpg (2741x1530). Stage: 1512x844 viewport,
   reference x 0.551623. Every length below is a measured stage pixel; the
   stage is laid out absolutely once, then scaled to the viewport, and the
   composition is re-authored (not shrunk) at <=1023px and <=640px.

   Owned by the frontend task. tokens.css/base.css load first and are shared.
   =========================================================================== */

/* The landing's tokens belong to the landing *layout*, not to one page: every page
   that uses it (start, status, legal) wears the same topbar, sections and footer,
   and the topbar sits outside the page's own markup, so they have to be on <body>
   level either way. `body.page` is what the build puts on every page. */
body.page {
  --lp-accent:      #a855f7;
  --lp-accent-deep: #7c3aed;
  --lp-ink:         #ffffff;
  --lp-ink-soft:    #f1eef8;
  --lp-stage-w:     1512;
  --lp-stage-h:     844;

  /* The stage is fitted to the window like a picture in a frame: contain, centred,
     never cropped, and scaled UP on a big screen as well as down. At exactly
     1512x844 the scale is min(1, 1) = 1, so the reference frame still renders at
     matrix(1,0,0,1,0,0) and every diff number is taken at 1:1.

     Two corrections against earlier versions of this line, both measured:
     - the height term is a plain ratio, NOT multiplied by the width term: a
       product is an area ratio, which under-scaled the stage and left a 447px dead
       band at 1024x768 (verify task-2, M8);
     - there is no `, 1` cap and no 844px ceiling on the fitted height. That cap
       meant a 2552px-wide window still showed a 1512x844 island in the middle of a
       black screen. */
  --lp-vh:          100vh;
  --lp-fitted-h:    max(320px, var(--lp-vh));
  --lp-scale:       min(calc(100vw / (var(--lp-stage-w) * 1px)), calc(var(--lp-fitted-h) / (var(--lp-stage-h) * 1px)));
  --lp-render-h:    calc(var(--lp-stage-h) * var(--lp-scale) * 1px);

  /* A stage wider than the viewport must stay centred: margin:auto cannot do
     that, it pins the box to the left once it overflows. */
  --lp-shift:       calc((100% - var(--lp-stage-w) * 1px) / 2);

  overflow-x: clip;
  /* The visible backdrop is the shard field (.lp-bg, section 1a) over black. */
  background: #000000;
}

/* ===========================================================================
   1. stage shell
   =========================================================================== */

.lp {
  position: relative;
  width: 100%;
  /* Both axes: at >=1024px the stage is transformed and its own box is only the
     painted height, so the clipping that keeps the composition inside the frame
     has to happen here instead of on the stage (see section 1b). On the reflowed
     layout this is a no-op, the stage is in normal flow and nothing overflows. */
  overflow: clip;
  /* The hero backdrop is its own fixed layer (.lp-bg, section 1a); this is only
     the base colour underneath it so there is never an unpainted flash. */
  background: #000000;
  color: var(--lp-ink-soft);
}

/* Desktop only: the shell is exactly one viewport tall, so what the stage paints
   below the fold is cropped instead of turning into a scrollbar (the wide-screen
   case in section 1c scales the composition past the viewport on purpose). */
@media (min-width: 1024px) {
  .lp { height: 100vh; }
}

/* 1a. hero backdrop — black, with the AeroShards field over it
   ---------------------------------------------------------------------------
   Plain black. The pixel mesh that used to sit here (three dark pinks, 16px
   blocks, 4.032 deg/s) was removed on request on 2026-10-16; the shard field in
   aero-shards.js is now the whole backdrop, and a browser without WebGPU simply
   gets the black. */
.lp-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  background: #000000;
  isolation: isolate;
}

/* The shard field fills the layer; it is inert to the pointer (`.lp-bg` has
   `pointer-events: none`), so the hero's controls stay clickable and the
   component's own window listeners still see the cursor. */
.lp-bg__shards,
.aero-shards {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.aero-shards-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* Scrim between the shard field (z 0) and the composition (z 3+). Elliptical and
   fully transparent at the edges, so it only lifts the contrast of the type
   without turning into a visible panel. Removable in one line. */
.lp-scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(48% 40% at 50% 45%, rgba(0, 0, 0, 0.62) 0%, rgba(0, 0, 0, 0.34) 52%, rgba(0, 0, 0, 0) 100%);
}

.lp__stage {
  position: relative;
  width: calc(var(--lp-stage-w) * 1px);
  height: calc(var(--lp-stage-h) * 1px);
  margin-inline: auto;
  overflow: hidden;
  isolation: isolate;
}

/* Scaled presentation: the stage keeps its authored 1512x844 pixel grid at
   >=1024px and is fitted to the window, so at 1512x844 it renders 1:1 and the
   reference geometry can be checked against the stage box directly. The
   rendered height is written back as the layout height so the document scroll
   height matches what is on screen. */
/* The two lines must share one cap height. Measured at a single size, line 1 ran
   106 tall against line 2's 98; the reference has both at 96. Line 2 is
   calibrated to the reference (96 cap, ~571 wide), so line 1 is sized down to
   meet it and its width is recovered with tracking, not a letterform swap.
   Desktop-only: at <=1023px the clamped font-size in section 8 takes over. */
@media (min-width: 1024px) {
  /* Cap height is the primary read, so the type is sized for the reference's
     96px cap (QA measured my glyphs at 108/109). Geist Bold is ~12% narrower
     than the reference face at equal cap, so the width is restored with a
     horizontal scale rather than by growing the letters. That also moves the
     stroke weight toward the reference's measured 0.31-0.34 x cap, which no
     Geist weight reaches. transform is not owned by the motion layer here. */
  .lp__display-line { transform: scaleX(1.05); transform-origin: left center; }
  .lp__display-line:first-child { font-size: 130px; letter-spacing: 0.058em; transform: scaleX(1.073); }
  .lp__display-line:last-child  { font-size: 129px; transform: scaleX(1.137); }
}

/* 1b. scaled presentation (>=1024px)
   ---------------------------------------------------------------------------
   The stage keeps its authored 1512x844 pixel grid and is fitted to the window,
   so at 1512x844 it renders 1:1 and reference geometry can be read off the box.

   Two traps live in these lines, both found by measurement, so both are written
   down here:

   1. `height` must be the PAINTED height (844 * scale). If it stays 844 the flow
      reserves a full stage height, a vertical scrollbar appears, 100vw becomes
      wider than the client area and the composition loses 7.5px on each side
      (verify task-2, N1: 76/44px of vertical overflow at 1024x768 / 1280x800,
      clientWidth 1009/1265 against innerWidth 1024/1280).
   2. `overflow` must NOT be `hidden` here. A clip applies before the transform,
      so children below 844*scale px were cut in layout space: at 1024x768 that
      removed 199.6px of composition including the whole CTA row, which made both
      buttons unclickable (verify task-2, H1). Clipping moves up to .lp.

   The stage paints no background of its own: the screen-wide gradient lives on
   .lp, which is what keeps the area outside the composition from reading as an
   empty band. */
@media (min-width: 1024px) {
  .lp__stage {
    transform-origin: top center;
    transform: scale(var(--lp-scale));
    height: var(--lp-render-h);
    overflow: visible;
    margin-inline: var(--lp-shift);
  }
}

/* 1c. wider than the composition (>=1024px)
   ---------------------------------------------------------------------------
   The composition is 1512:844. On anything wider, a plain contain fit leaves a
   band on the left and right; on a 2552x1300 window that was still 112px of
   screen per side. So on wide screens the fit fills the width AND the height, and
   crops the excess off the bottom. The crop boundary is y=775 of the 844 stage,
   which is exactly where the content ends (the deck's bottom edge) — everything
   below it is the foreground pillow, whose whole job is to bleed off the bottom.
   Nothing readable or interactive is ever inside the crop.

   At the reference aspect this query matches with scale min(1, 1.089) = 1, so the
   measured frame is untouched. On screens wider than ~1.95:1 the crop would start
   eating the deck, so the scale stops there and the remainder is background. */
@media (min-width: 1024px) and (min-aspect-ratio: 1512/844) {
  .page--index {
    --lp-scale: min(calc(100vw / (var(--lp-stage-w) * 1px)), calc(var(--lp-vh) / 775px));
  }
}

/* The faint vertical grid and the local pink aura that used to sit here were
   removed by request on 2026-10-16: the hero backdrop is the 21st.dev gradient
   (section 1a) and nothing else. The leftovers of that stack are gone with them
   — the grid's background-size rule and the magenta corner glows that the
   reflowed layout still painted over the gradient below 1024px. The reference
   geometry is in the backup zip and qa/diff/ if it is ever wanted back. */

/* 1d. the topbar — the header as a fixed layer
   ---------------------------------------------------------------------------
   The header has to stay on screen while the page scrolls, and it cannot be
   `position: fixed` inside `.lp__stage`: that box carries `transform: scale()`,
   and a transformed ancestor becomes the containing block for fixed children, so
   the header would stick to the stage and scroll away with it. It lives in its own
   fixed layer instead, which carries a copy of the stage's authored 1512px grid and
   the same `--lp-scale`, so every coordinate the header already had still means
   what it meant.

   `is-scrolled` on <html> (toggled by scroll-motion.js, at 24px) shrinks the bar as
   a whole — mark, nav pills and CTA together — and fades in a gradient behind it so
   it reads as a surface once it is floating over content. The transition is the only
   animated part and it is switched off under prefers-reduced-motion; the state
   itself still switches, because it is a state and not a decoration. */

.lp-topbar {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  display: grid;
  justify-items: center;
  pointer-events: none;
}

.lp-topbar__stage {
  position: relative;
  width: calc(var(--lp-stage-w) * 1px);
  height: 0;
  transform-origin: top center;
  transform: scale(var(--lp-scale));
  transition: transform 240ms var(--ease, ease);
  pointer-events: none;
}

.lp-head { pointer-events: auto; }

.lp-topbar::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.86) 0%, rgba(0, 0, 0, 0.55) 55%, rgba(0, 0, 0, 0) 100%);
  opacity: 0;
  transition: opacity 240ms var(--ease, ease), height 240ms var(--ease, ease);
}

html.is-scrolled .lp-topbar__stage { transform: scale(calc(var(--lp-scale) * 0.86)); }
html.is-scrolled .lp-topbar::before { opacity: 1; height: 92px; }

@media (max-width: 1023px) {
  /* below 1024px the stage is in normal flow at full width, so the wrapper is too */
  .lp-topbar__stage { width: 100%; transform: none; }
  html.is-scrolled .lp-topbar__stage { transform: scale(0.94); }
  html.is-scrolled .lp-topbar::before { height: 78px; }
}

@media (prefers-reduced-motion: reduce) {
  .lp-topbar__stage,
  .lp-topbar::before { transition: none; }
}

/* ===========================================================================
   3. header — wordmark / centred pill nav / outlined CTA
   =========================================================================== */

.lp-head {
  position: absolute;
  left: 178px;
  right: 168px;
  top: 34px;
  height: 36px;
  z-index: 8;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

/* The brand: the P mark, then the rest of the name faded. It reads as one
   wordmark, so the gap is small and the mark is taller than the text. */
.lp-brand {
  display: flex;
  align-items: center;
  gap: 5px;
}

.lp-brand__mark {
  display: block;
  width: auto;
  height: 34px;
}

.lp-brand__word {
  font-family: 'Sora', var(--font);
  font-weight: 200;
  font-size: 15px;
  line-height: 1;
  letter-spacing: 0.01em;
  color: rgba(241, 238, 248, 0.5);
  white-space: nowrap;
}

/* The three static pills are gone: the header nav is the GooeyNav host, styled in
   section 12 (and scripted in src/scripts/gooey-nav.js). */

.lp-nav__cta {
  display: flex;
  align-items: center;
  height: 27px;
  padding: 0 15px;
  border: 1px solid rgba(255, 255, 255, 0.42);
  border-radius: var(--r-pill, 999px);
  color: #fff;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: -0.01em;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--dur-fast, 130ms) var(--ease, ease), border-color var(--dur-fast, 130ms) var(--ease, ease);
}

.lp-nav__cta:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.62); }

/* ===========================================================================
   4. headline + support copy
   =========================================================================== */

.lp-intro {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: 3;
}

/* Two plain text lines, white on the field, no outline: the pink ring (and with
   it the ::before layer and the data-text attributes) was removed on request on
   2026-10-16. Contrast is carried by the type itself and the scrim behind it.

   Line-height is the one thing to keep an eye on: at Sora's cap height, anything
   below ~1.04 x the font size makes the two lines touch, which is exactly what
   the screenshot showed. */
.lp__display {
  position: absolute;
  left: 50%;
  top: 248px;
  transform: translateX(-50%);
  text-align: center;
  margin: 0;
  /* Sora 200 (OFL, self-hosted, 14.8 KB) — requested on 2026-10-16 as thin, modern
     and not the usual display face. It replaced Russo One, which was a measured
     match to the reference mockup in task-1 (docs/fontlab/AB_REPORT.md keeps that
     table); that comparison stopped applying when the composition stopped being a
     recreation of the reference, on the same date. Weight 200 is a real instance
     of the variable face, not a synthesis. */
  font-family: 'Sora', var(--font);
  font-size: 96px;
  line-height: 100px;
  font-weight: 200;
  letter-spacing: 0.02em;
  white-space: nowrap;
  text-transform: uppercase;
  color: #fff;
}

.lp__display-line {
  display: block;
  white-space: nowrap;
}



/* Support copy: line 1 cap top y534.6, baseline pitch 34.8, cap 18.8 */
.lp__support {
  position: absolute;
  left: 50%;
  top: 468px;
  transform: translateX(-50%);
  text-align: center;
  margin: 0;
  font-family: var(--font);
  font-size: 21px;
  line-height: 28px;
  font-weight: 400;
  letter-spacing: -0.005em;
  color: #cdc6dc;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.5);
}

/* ===========================================================================
   8. <=1023px — the stage stops scaling and re-composes in normal flow
   =========================================================================== */

@media (max-width: 1023px) {
  body.page { --lp-pad-x: clamp(18px, 4.4vw, 40px); }

  /* No backdrop of its own: the shard field is a fixed layer (.lp-bg, section 1a)
     at every width and this is black underneath it. The hero fills the viewport so
     the composition reads as one screen instead of a short block on a long page —
     below 1024px the stage is in normal flow, so `min-height` is what does it.

     A phone holds the composition in the middle of the screen, not at the top:
     the stage centres its column and the header goes out of flow onto the top
     edge, so what is centred is the headline, not the headline plus the nav. The
     top padding is what puts the *headline* on the centre line — the support copy
     hangs below it, and centring the block instead would lift the title by half
     the height of that copy. */
  .lp__stage {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: auto;
    min-height: 100vh;
    min-height: 100svh;
    padding: clamp(84px, 12vh, 128px) 0 46px;
    overflow: visible;
  }

  .lp-head {
    position: absolute;
    inset: 0 0 auto;
    width: auto;
    height: auto;
    margin: 0 var(--lp-pad-x);
    padding: 16px 0 0;
    gap: 12px;
    flex-wrap: wrap;
  }

  .lp-nav__cta { height: 25px; padding: 0 11px; font-size: 11.5px; }

  .lp-intro { position: static; width: auto; margin: 0 var(--lp-pad-x); text-align: center; }

  .lp__display {
    position: static;
    transform: none;
    font-size: clamp(46px, 14.6vw, 82px);
    line-height: 1.06;
    text-shadow: 0 2px 18px rgba(0, 0, 0, 0.55);
  }

  .lp__support {
    position: static;
    transform: none;
    margin-top: 16px;
    font-size: clamp(15px, 3.6vw, 21px);
    line-height: 1.5;
  }
}

/* ===========================================================================
   9. <=640px — single column, quieter chrome
   =========================================================================== */

@media (max-width: 640px) {
}

/* ===========================================================================
   10. motion preferences — landing-motion.css owns animation; this only makes
   the authored resting state the final state.
   =========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .lp-nav__cta { transition: none; }
}

/* ===========================================================================
   11. headline face — Sora 200

   Requested on 2026-10-16: thin, modern, and not the usual display face. Sora is
   a geometric sans with a real ExtraLight (200) instance — self-hosted latin
   subset, 14.8 KB, OFL. It replaces Russo One, which was picked in task-1 by a
   measured A/B against the reference mockup (docs/fontlab/AB_REPORT.md keeps
   that table); that comparison no longer applies, because the composition stopped
   being a recreation of the reference on the same date.

   Weight stays 200: Sora ships a variable font, so any other weight is a
   deliberate change, not a synthesis fallback.
   =========================================================================== */

@font-face {
  font-family: 'Sora';
  src: url('/fonts/sora-latin-200-normal.woff2') format('woff2');
  font-weight: 200;
  font-style: normal;
  font-display: swap;
}

@media (min-width: 1024px) {
  /* One size, one tracking, centred. The per-line widths and the scaleX that
     used to pin the two lines to the reference mockup's 554/571px are gone: the
     composition is a redesign as of 2026-10-16 (Modules card removed, headline
     centred), so there is no reference width left to match. */
  .lp__display {
    left: 50%;
    top: 250px;
    font-size: 88px;
    line-height: 92px;
    font-weight: 200;
    letter-spacing: 0.02em;
  }

  .lp__display-line { transform-origin: center center; }
}

/* ===========================================================================
   12. header nav — GooeyNav (React Bits, ported in src/scripts/gooey-nav.js)

   The component's own stylesheet, unchanged except for the last block below: the
   frame is tinted to this site's violet-dark and the size variables are pulled in
   so the bar sits at roughly the old header height. The host is centred in
   .lp-head; the canvas around it is deliberately not clipped by the header, so the
   droplets have room to fly.
   =========================================================================== */

/* Scoped to .lp-head so it wins over the component's own \`position: relative\`:
   the host is taken out of the header's flex row and centred in it. */
.lp-head .lp-nav-host {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.gooey-nav {
  --gn-pad: 6px;
  --gn-gap: 4px;
  --gn-font: 14px;
  --gn-x: 16px;
  --gn-y: 9px;
  position: relative;
  display: inline-flex;
  max-width: 100%;
  font-size: var(--gn-font);
  line-height: 1.2;
}

.gooey-nav--sm {
  --gn-pad: 5px;
  --gn-gap: 2px;
  --gn-font: 13px;
  --gn-x: 13px;
  --gn-y: 7px;
}

.gooey-nav--lg {
  --gn-pad: 7px;
  --gn-gap: 6px;
  --gn-font: 16px;
  --gn-x: 20px;
  --gn-y: 11px;
}

.gooey-nav--framed {
  padding: var(--gn-pad);
  border-radius: 999px;
  background: var(--gn-frame);
  box-shadow: var(--gn-frame-shadow);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  backdrop-filter: blur(20px) saturate(1.6);
}

.gooey-nav__stage {
  position: relative;
  max-width: 100%;
}

.gooey-nav__canvas {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
}

.gooey-nav__fallback {
  position: absolute;
  top: 0;
  left: 0;
  display: none;
  border-radius: 999px;
  background: var(--gn-pill);
  pointer-events: none;
}

.gooey-nav__list {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: var(--gn-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

.gooey-nav__list--active {
  position: absolute;
  inset: 0;
  color: var(--gn-active-ink);
  visibility: hidden;
  clip-path: inset(100%);
  pointer-events: none;
}

.gooey-nav__item {
  display: flex;
}

.gooey-nav__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: var(--gn-y) var(--gn-x);
  border: none;
  border-radius: 999px;
  background: none;
  color: var(--gn-muted);
  font: inherit;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.2s ease;
  -webkit-tap-highlight-color: transparent;
}

.gooey-nav__list--active .gooey-nav__link {
  color: inherit;
}

.gooey-nav__item[data-active] .gooey-nav__link {
  color: var(--gn-ink);
}

.gooey-nav__link:focus-visible {
  outline: 2px solid var(--gn-ink);
  outline-offset: 2px;
}

.gooey-nav__icon {
  display: inline-flex;
  flex: none;
}

.gooey-nav__icon svg {
  width: 1.15em;
  height: 1.15em;
}

.gooey-nav__rim {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow:
    inset 0 0 0 1px var(--gn-frame-edge),
    inset 0 1px 0 var(--gn-frame-highlight);
  pointer-events: none;
}

@media (hover: hover) {
  .gooey-nav__link:hover {
    color: var(--gn-ink);
  }
}

/* this site: two sizes below the component's md, violet-tinted frame.
   Note the compound selector — the host carries both classes, so a descendant
   selector like `.lp-nav-host .gooey-nav` would never match. */
.lp-nav-host.gooey-nav {
  --gn-pad: 3px;
  --gn-gap: 2px;
  --gn-font: 11.5px;
  --gn-x: 10px;
  --gn-y: 4px;
  --gn-frame: rgba(22, 19, 30, 0.6);
  --gn-frame-shadow: 0 18px 36px -22px rgba(0, 0, 0, 0.85), 0 4px 12px -8px rgba(0, 0, 0, 0.5);
}

@media (max-width: 1023px) {
  .lp-head .lp-nav-host {
    position: static;
    transform: none;
    order: 3;
    flex: 1 0 100%;
    display: flex;
    justify-content: center;
    margin: 8px 0 0;
    /* The nav is built by script, so before it mounts this box is empty and the
       header is 18.6px shorter — a visible jump on load and a size that changes
       under the bar. 25px is the measured height of the mounted host at
       --gn-y: 3px, reserved here so the header is the same height with or without
       the module (5.6px of the jump was still left at 19px, which is only the list). */
    min-height: 25px;
  }

  .lp-nav-host.gooey-nav { --gn-font: 10.5px; --gn-x: 9px; --gn-y: 3px; }

  .lp-brand__mark { height: 28px; }
  .lp-brand__word { font-size: 13px; }
}

/* ===========================================================================
   13. the section after the hero

   Its own black slab that starts transparent and settles into #000, so the shard
   field behind the page carries into it and then gives way. Text is the same Sora
   200 as the headline, one step down; the numbers are Geist Mono, like the rest of
   the site's data.
   =========================================================================== */

.lp-next {
  position: relative;
  z-index: 1;
  padding: clamp(72px, 13vh, 150px) 0 clamp(88px, 16vh, 180px);
  background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.55) 14%, #000 46%);
  color: var(--lp-ink-soft);
}

.lp-next__inner {
  width: min(1120px, 100% - 2 * clamp(18px, 4.4vw, 40px));
  margin-inline: auto;
}

.lp-next__eyebrow {
  margin: 0 0 20px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.lp-next__title {
  margin: 0;
  max-width: 24ch;
  font-family: 'Sora', var(--font);
  font-weight: 200;
  font-size: clamp(26px, 4.4vw, 52px);
  line-height: 1.14;
  letter-spacing: 0.01em;
  color: #fff;
}

.lp-next__body {
  margin: 18px 0 0;
  max-width: 48ch;
  font-size: clamp(14px, 1.6vw, 17px);
  line-height: 1.6;
  color: #cdc6dc;
}

.lp-next__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 38px;
  margin: 44px 0 0;
  padding: 0;
  list-style: none;
}

.lp-next__facts li { display: flex; flex-direction: column; gap: 3px; }

.lp-next__facts b {
  font-family: 'Sora', var(--font);
  font-weight: 200;
  font-size: clamp(20px, 2.6vw, 30px);
  color: #fff;
}

.lp-next__facts span {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.46);
}

/* the reveal: opacity and a small lift, one class, no keyframes */
.lp-next__inner > * {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.7s var(--ease, ease), transform 0.7s var(--ease, ease);
}

[data-reveal-section].is-in .lp-next__inner > * { opacity: 1; transform: none; }
.lp-next__inner > *:nth-child(2) { transition-delay: 0.07s; }
.lp-next__inner > *:nth-child(3) { transition-delay: 0.14s; }
.lp-next__inner > *:nth-child(4) { transition-delay: 0.21s; }

/* a hairline between the hero and the section, in the shard accent */
.lp-next::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: min(1120px, 100% - 2 * clamp(18px, 4.4vw, 40px));
  height: 1px;
  transform: translateX(-50%);
  background: linear-gradient(90deg, rgba(168, 85, 247, 0) 0%, rgba(168, 85, 247, 0.42) 50%, rgba(168, 85, 247, 0) 100%);
}

@media (prefers-reduced-motion: reduce) {
  .lp-next__inner > * { opacity: 1; transform: none; transition: none; }
}

/* ===========================================================================
   14. the sections below the hero — why / globe / pricing / halloween

   All four use the section shell from 13 (.lp-next__inner, eyebrow, title, body)
   and the same reveal, now keyed on [data-reveal-section] above.
   =========================================================================== */

.lp-why,
.lp-globe,
.lp-vouch,
.lp-pricing,
.lp-halloween {
  position: relative;
  z-index: 1;
  color: var(--lp-ink-soft);
}

.lp-why,
.lp-pricing { background: #000; }
.lp-why { padding: clamp(64px, 10vh, 120px) 0; }
.lp-pricing { padding: clamp(64px, 10vh, 130px) 0; }
.lp-globe {
  padding: clamp(64px, 10vh, 130px) 0;
  background: linear-gradient(180deg, #000 0%, #07040d 50%, #000 100%);
}

/* --- why ------------------------------------------------------------------- */
.why__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 2vw, 24px);
  margin-top: clamp(28px, 4vh, 44px);
}

.why__card {
  padding: 26px 24px 28px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.022);
  transition: border-color 240ms var(--ease, ease), background 240ms var(--ease, ease), transform 240ms var(--ease, ease);
}

@media (hover: hover) {
  .why__card:hover {
    border-color: rgba(168, 85, 247, 0.35);
    background: rgba(168, 85, 247, 0.05);
    transform: translateY(-2px);
  }
}

.why__kicker {
  margin: 0 0 16px;
  font-size: 11px;
  letter-spacing: 0.18em;
  color: rgba(255, 255, 255, 0.42);
}

.why__title {
  margin: 0 0 10px;
  font-family: 'Sora', var(--font);
  font-weight: 200;
  font-size: clamp(19px, 1.9vw, 24px);
  line-height: 1.25;
  color: #fff;
}

.why__body {
  margin: 0;
  font-size: 14.5px;
  line-height: 1.62;
  color: #cdc6dc;
}

/* --- globe ----------------------------------------------------------------- */
.lp-globe__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(22px, 4.5vw, 60px);
  align-items: center;
}

.lp-globe__totals {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 34px;
  margin: 34px 0 0;
  padding: 0;
  list-style: none;
}

.lp-globe__totals li { display: flex; flex-direction: column; gap: 3px; }
.lp-globe__totals b { font-size: clamp(20px, 2.3vw, 28px); color: #fff; font-weight: 400; }
.lp-globe__totals span {
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.46);
}

.lp-globe__chip {
  display: inline-block;
  margin: 22px 0 0;
  padding: 5px 11px;
  border: 1px dashed rgba(168, 85, 247, 0.45);
  border-radius: 999px;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(206, 178, 255, 0.82);
}

.lp-globe__stage {
  position: relative;
  width: 100%;
  max-width: 540px;
  margin-inline: auto;
  aspect-ratio: 1 / 1;
}

/* --- pricing --------------------------------------------------------------- */
.pricing__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(14px, 2vw, 24px);
  margin-top: clamp(28px, 4vh, 44px);
}

.pricing__card { height: clamp(290px, 30vw, 360px); }

.pricing__inner {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  height: 100%;
  padding: 30px 28px 28px;
  text-align: left;
}

.pricing__name {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-size: 11.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.pricing__chip {
  padding: 3px 8px;
  border: 1px solid rgba(168, 85, 247, 0.42);
  border-radius: 999px;
  background: rgba(168, 85, 247, 0.18);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: #dcc7ff;
}

.pricing__chip:empty { display: none; }

.pricing__price {
  display: flex;
  align-items: baseline;
  gap: 9px;
  margin: auto 0 0;
}

.pricing__price b {
  font-family: 'Sora', var(--font);
  font-weight: 200;
  font-size: clamp(32px, 3.6vw, 48px);
  line-height: 1;
  color: #fff;
}

.pricing__was { font-size: 15px; color: rgba(255, 255, 255, 0.36); text-decoration: line-through; }
.pricing__was:empty { display: none; }
.pricing__per { font-size: 13px; color: rgba(255, 255, 255, 0.5); }
.pricing__note { margin: 0; font-size: 14px; color: #cdc6dc; }

.pricing__cta {
  margin-top: 16px;
  padding: 11px 18px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: rgba(168, 85, 247, 0.16);
  font-size: 13px;
  color: #fff;
}

.pricing__fine { margin: 24px 0 0; font-size: 12.5px; color: rgba(255, 255, 255, 0.42); }

/* --- halloween -------------------------------------------------------------
   The only orange on the site, and only while the section is shipped at all.
   Everything here is drawn: the web is SVG, the pumpkins are CSS. */

.lp-halloween {
  overflow: hidden;
  padding: clamp(104px, 20vh, 200px) 0 clamp(120px, 21vh, 210px);
  background:
    radial-gradient(110% 70% at 50% 0%, rgba(255, 122, 26, 0.16) 0%, rgba(255, 122, 26, 0) 62%),
    linear-gradient(180deg, #0d0602 0%, #050201 60%, #000 100%);
}

/* the clock's digit size is a variable so the four units always fit: the counter
   port passes a non-numeric font-size straight through to CSS */
.lp-halloween { --clock-size: 72px; }

.lp-halloween .lp-next__eyebrow { color: #ffb066; }
.lp-halloween .lp-next__title { margin-inline: auto; max-width: 18ch; color: #ffe8d1; }
.halloween__inner { position: relative; text-align: center; }

.halloween__clock {
  display: flex;
  justify-content: center;
  gap: clamp(12px, 4vw, 46px);
  margin: clamp(26px, 4.5vh, 48px) 0 0;
}

.halloween__unit { display: flex; flex-direction: column; align-items: center; gap: 8px; }

.halloween__label {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 176, 102, 0.72);
}

/* --- the supplied icons ----------------------------------------------------
   Bands, so nothing overlaps: LEFT the web (top) and the ghost (mid), RIGHT the
   bat (top), skull (upper) and scythe (lower), BOTTOM the pumpkin (left), the
   cauldron (centre) and the hat (right).
   Eight assets from the client's zip (orange artwork on pure black), keyed to
   transparency and stored in static/assets/halloween/. They are placed around the
   edges so the countdown keeps the middle, sized in clamp() so nothing collides,
   and they only breathe — a 10px bob with a slight extra tilt, nothing that
   competes with the clock. Every one is decorative. */

.halloween__art {
  position: absolute;
  height: auto;
  pointer-events: none;
  user-select: none;
  filter: drop-shadow(0 16px 34px rgba(245, 124, 23, 0.26));
  animation: halloween-bob 9s ease-in-out infinite;
}

@keyframes halloween-bob {
  0%,
  100% { transform: translate3d(0, 0, 0) rotate(var(--art-tilt, 0deg)); }
  50% { transform: translate3d(0, -10px, 0) rotate(calc(var(--art-tilt, 0deg) + 1.4deg)); }
}

.halloween__art--web {
  top: -4%;
  left: -5%;
  width: clamp(150px, 16vw, 250px);
  opacity: 0.4;
  animation-duration: 15s;
}

.halloween__art--bat {
  top: 4%;
  right: 7%;
  width: clamp(115px, 14vw, 210px);
  opacity: 0.92;
  --art-tilt: -4deg;
}

.halloween__art--scythe {
  bottom: 0;
  left: 17%;
  width: clamp(78px, 9vw, 150px);
  opacity: 0.72;
  --art-tilt: 12deg;
  animation-delay: -6s;
}

.halloween__art--ghost {
  top: 48%;
  left: 4.5%;
  width: clamp(76px, 8.6vw, 134px);
  opacity: 0.8;
  --art-tilt: 3deg;
  animation-delay: -2s;
}

.halloween__art--skull {
  top: 38%;
  right: 5%;
  width: clamp(66px, 7.6vw, 118px);
  opacity: 0.85;
  --art-tilt: -3deg;
  animation-delay: -4s;
}

.halloween__art--pumpkin {
  bottom: 0;
  left: 5%;
  width: clamp(92px, 11vw, 174px);
  --art-tilt: -2deg;
  animation-delay: -1s;
}

.halloween__art--cauldron {
  bottom: 0;
  left: 46%;
  width: clamp(86px, 10vw, 160px);
  opacity: 0.9;
  --art-tilt: 2deg;
  animation-delay: -3s;
}

.halloween__art--hat {
  bottom: 0;
  right: 7%;
  width: clamp(84px, 9.6vw, 152px);
  --art-tilt: 5deg;
  animation-delay: -5s;
}

@media (max-width: 900px) {
  .lp-halloween { --clock-size: 56px; }
  .halloween__art--ghost,
  .halloween__art--skull,
  .halloween__art--scythe,
  .halloween__art--cauldron { display: none; }
  .halloween__art--web { width: 190px; opacity: 0.38; }
  .halloween__art--bat { width: 120px; top: 8%; }
  .halloween__art--pumpkin { width: 104px; }
  .halloween__art--hat { width: 92px; }
  .why__grid,
  .pricing__grid,
  .lp-globe__inner { grid-template-columns: minmax(0, 1fr); }
  .lp-globe__stage { max-width: 380px; }
  .pricing__card { height: 300px; }
  .halloween__clock { gap: 10px; }
}

@media (max-width: 560px) {
  .lp-halloween { --clock-size: 44px; }
  .halloween__art--hat { display: none; }
  .halloween__art--web { width: 150px; }
  .pricing__card { height: 280px; }
  .why__card { padding: 22px 20px 24px; }
}

@media (prefers-reduced-motion: reduce) {
  .halloween__art { animation: none; }
  [data-reveal-section].is-in .lp-next__inner > *,
  .lp-next__inner > * { opacity: 1; transform: none; transition: none; }
  .why__card { transition: none; }
}

/* ===========================================================================
   16. scroll motion — the small amount of it

   Two effects, both driven by /scripts/scroll-motion.js through custom
   properties, so the CSS here stays the resting state and the script is the only
   thing that moves:
     · the hero settles as you leave it: the intro lifts a little and fades, so
       the first screen hands over to the sections instead of just scrolling away
     · the globe and the vouches panel drift against the page as they pass, ±13px
   They use the individual `translate` property, not `transform`, because both
   panels are direct children of a `[data-reveal-section]` and the reveal already
   owns their `transform` — the two compose this way instead of one erasing the
   other. Nothing here runs under prefers-reduced-motion: the script does not
   start, the properties keep their defaults, and the page is simply still.
   =========================================================================== */

.lp-intro {
  translate: 0 var(--lp-hero-lift, 0px);
  opacity: var(--lp-hero-fade, 1);
}

.lp-globe__stage { translate: 0 var(--lp-drift-a, 0px); }
.lp-vouch__panel { translate: 0 var(--lp-drift-b, 0px); }

/* ===========================================================================
   18. the status page — the landing's clothes on a second page

   src/pages/status.html uses the landing layout, so it has the same topbar (and
   therefore the same navbar), the same footer and the same fixed shard field. All
   it needs from here is the head band, which paints no background of its own so the
   field shows through it, and the facts grid. Everything else is the section shell
   and the why-cards, unchanged.
   =========================================================================== */

.lp-status__head {
  position: relative;
  z-index: 1;
  padding: clamp(132px, 20vh, 210px) 0 clamp(56px, 9vh, 104px);
  color: var(--lp-ink-soft);
}

.lp-status__head .lp-next__title { max-width: 24ch; }

.lp-status__chip {
  display: inline-block;
  margin: 24px 0 0;
  padding: 5px 11px;
  border: 1px dashed rgba(168, 85, 247, 0.45);
  border-radius: 999px;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(206, 178, 255, 0.82);
}

.lp-status__facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  margin-top: clamp(26px, 4.5vh, 44px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 18px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.07);
}

.lp-status__fact {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  margin: 0;
  padding: 15px 20px;
  background: #000;
}

.lp-status__fact span {
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.42);
}

.lp-status__fact b { font-weight: 400; font-size: 13.5px; color: #fff; }

.lp-status__fine {
  margin: 20px 0 0;
  font-size: 12.5px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.42);
}

.lp-status__fine a { color: rgba(206, 178, 255, 0.85); }
.lp-status__fine a:hover { color: #e4d5ff; }

/* the legal page's prose — the same reading measure and colour as the section copy,
   one block per statement so privacy and terms stay scannable */
.lp-legal__prose { max-width: 66ch; margin-top: clamp(18px, 3vh, 30px); }
.lp-legal__prose p {
  margin: 0 0 14px;
  font-size: 14.5px;
  line-height: 1.7;
  color: #cdc6dc;
}
.lp-legal__prose p:last-child { margin-bottom: 0; }
.lp-legal__prose strong { font-weight: 500; color: #fff; }

@media (max-width: 760px) {
  .lp-status__facts { grid-template-columns: minmax(0, 1fr); }
}

/* ===========================================================================
   17. the footer on this page

   src/partials/footer.html is shared with the 404 shell, and the landing does not
   load components.css, so its layout is repeated here for this page — the facts
   and the legal lines still come from the one partial, and every number in it is
   a build token.
   =========================================================================== */

.footer {
  position: relative;
  z-index: 1;
  /* Flush against the halloween section: a margin here opened a black band between
     the two, and the fixed shard field showed through it. The footer's own gradient
     starts at its first pixel, so nothing shows through anywhere. */
  margin-top: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  background: linear-gradient(180deg, #06040b, #000 68%);
  color: var(--lp-ink-soft);
}

.footer__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
  gap: clamp(24px, 5vw, 64px);
  max-width: 1120px;
  margin-inline: auto;
  padding: clamp(36px, 5vw, 64px) clamp(18px, 4.4vw, 40px) 0;
}

.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 9px;
  flex: none;
}

.brand__mark { display: flex; align-self: center; }
.brand__word { font-size: 1.06rem; font-weight: 500; letter-spacing: -0.02em; color: #fff; }
.brand__tag {
  font-family: var(--mono);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: rgba(255, 255, 255, 0.42);
}

.footer__line { margin: 16px 0 0; max-width: 34ch; font-size: 14px; line-height: 1.6; color: #cdc6dc; }
.footer__cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 28px; }

.footer__heading {
  margin: 0 0 14px;
  font-size: 10.5px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: rgba(255, 255, 255, 0.42);
}

.footer__col ul { display: grid; gap: 9px; margin: 0; padding: 0; list-style: none; }
.footer__col a { font-size: 14px; color: #cdc6dc; }
.footer__col a:hover { color: #dcc7ff; }
.footer__meta { font-family: var(--mono); font-size: 11.5px; color: rgba(255, 255, 255, 0.5); }

.footer__base {
  display: grid;
  gap: 6px;
  max-width: 1120px;
  margin-inline: auto;
  padding: 22px clamp(18px, 4.4vw, 40px) 34px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.footer__legal { margin: 0; max-width: 92ch; font-size: 11.5px; line-height: 1.6; color: rgba(255, 255, 255, 0.42); }
.footer__legal--dim { color: rgba(255, 255, 255, 0.3); }

@media (max-width: 760px) {
  .footer__inner { grid-template-columns: minmax(0, 1fr); }
  .footer__cols { grid-template-columns: 1fr 1fr; }
}
