/* Wedge v17 · static conversion of the studio's licensed Framer template.
   Pure black, Space Grotesk 500 display, Geist body, Source Serif 4 italic
   accents, pill buttons, hairline cells, ribbon video in the hero and CTA,
   ribbon art cards in the feature sections. All copy is Wedge's own.
   The page is complete and readable with no JS and no motion. */

/* ---------- tokens ---------- */

:root {
  --bg: #000;
  --paper: #FAFAFA;
  --tx: rgba(255, 255, 255, .95);
  --tx-mid: rgba(255, 255, 255, .78);
  --tx-mut: rgba(255, 255, 255, .58);
  --tx-faint: rgba(255, 255, 255, .38);
  --line: rgba(255, 255, 255, .09);
  --line-2: rgba(255, 255, 255, .17);
  --fill: rgba(255, 255, 255, .06);
  --fill-2: rgba(255, 255, 255, .12);
  --red: #C8102E;
  --acc-blue: #7EC4E3;
  --acc-green: #7EEA9B;
  --acc-amber: #F2B76C;
  --disp: 'Space Grotesk', system-ui, sans-serif;
  --sans: 'Geist', 'Inter', system-ui, -apple-system, sans-serif;
  --serif: 'Source Serif 4', Georgia, serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
  --ease: cubic-bezier(.33, 1, .68, 1);
  --head-h: 68px;
}

/* ---------- base ---------- */

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; scroll-padding-top: calc(var(--head-h) + 16px); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--bg);
  color: var(--tx-mid);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: rgba(160, 130, 255, .35); color: #fff; }

a { color: inherit; }
img, video { display: block; max-width: 100%; }

a:focus-visible, button:focus-visible, summary:focus-visible {
  outline: 2px solid var(--paper);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip {
  position: absolute; left: 16px; top: -48px; z-index: 100;
  background: var(--paper); color: #000;
  padding: 10px 18px; border-radius: 99px;
  font-size: 14px; font-weight: 500; text-decoration: none;
  transition: top .2s var(--ease);
}
.skip:focus { top: 14px; }

.wrap { max-width: 1120px; margin-inline: auto; padding-inline: 24px; }

/* ---------- header ---------- */

.site-head {
  position: fixed; inset: 0 0 auto 0; z-index: 50;
  height: var(--head-h);
  display: flex; align-items: center;
}
/* progressive glass. the blur layer runs past the bar and dissolves through
   a mask, so there is no hard edge where the frosting stops. */
.site-head::before {
  content: ""; position: absolute;
  inset: 0 0 -44px 0; z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(0,0,0,.82), rgba(0,0,0,.52) 48%, rgba(0,0,0,0) 100%);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-mask-image: linear-gradient(180deg, #000 42%, rgba(0,0,0,.45) 68%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 42%, rgba(0,0,0,.45) 68%, transparent 100%);
  opacity: 0;
  transition: opacity .5s var(--ease);
}
.site-head.scrolled::before { opacity: 1; }
.site-head .wrap {
  width: 100%;
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: 20px;
}
.head-cta { justify-self: end; }
.brand {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--disp); font-size: 16.5px; font-weight: 600;
  letter-spacing: -.02em; color: var(--tx); text-decoration: none;
  white-space: nowrap;
}
/* the brand mark is the hexagon ring, same geometry as the Orbitc mark in
   the hero marquee, per David 2026-08-22 */
.brand .mark {
  width: 18px; height: 18px; display: inline-block; flex: none;
  fill: currentColor;
}
.site-nav { display: flex; align-items: center; gap: 4px; justify-self: center; }
.site-nav a {
  font-size: 14.5px; color: var(--tx-mut); text-decoration: none;
  padding: 8px 14px; border-radius: 99px;
  transition: color .2s var(--ease), background .25s var(--ease);
}
.site-nav a:hover { color: var(--tx); background: var(--fill); }

/* ---------- buttons, chips ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  height: 46px; padding-inline: 24px; border-radius: 99px;
  font-family: var(--sans); font-size: 15px; font-weight: 500;
  text-decoration: none; white-space: nowrap; border: 0; cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease);
}
.btn-primary { background: var(--paper); color: #000; }
.btn-primary:hover { background: #fff; }
.btn-ghost {
  background: rgba(255, 255, 255, .09);
  color: var(--tx);
  border: 1px solid rgba(255, 255, 255, .18);
  -webkit-backdrop-filter: blur(14px) saturate(1.5);
  backdrop-filter: blur(14px) saturate(1.5);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22), 0 8px 24px -12px rgba(0, 0, 0, .5);
}
.btn-ghost:hover {
  background: rgba(255, 255, 255, .15);
  border-color: rgba(255, 255, 255, .3);
}
.site-nav .btn { height: 38px; padding-inline: 18px; font-size: 14px; }
.btn-row {
  display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;
  margin-top: 34px;
}

.chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 16px; border-radius: 99px;
  background: var(--fill); border: 1px solid var(--line-2);
  font-size: 13px; font-weight: 500; color: var(--tx-mid);
  margin-bottom: 28px;
}
.chip .spark { color: var(--tx-mut); font-size: 11px; }

/* the hero's announcement chip: a soft glow breathing on the EDGE (no
   sheen, per David), a hover that brightens it, and a small "New" pill
   inside instead of the spark */
.hero .chip {
  position: relative;
  transition: background .25s var(--ease), border-color .25s var(--ease), color .25s var(--ease);
}
.hero .chip::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit;
  pointer-events: none; opacity: .55;
  animation: chip-breathe 4.2s var(--ease) infinite;
  transition: opacity .35s var(--ease);
}
@keyframes chip-breathe {
  0%, 100% { box-shadow: 0 0 12px rgba(255, 255, 255, .07), inset 0 0 10px rgba(255, 255, 255, .04); }
  50% { box-shadow: 0 0 26px rgba(255, 255, 255, .17), inset 0 0 14px rgba(255, 255, 255, .08); }
}
.hero .chip:hover {
  background: var(--fill-2);
  border-color: rgba(255, 255, 255, .42);
  color: var(--tx);
}
.hero .chip:hover::before { opacity: 1; }
.chip-new {
  background: var(--paper); color: #000;
  border-radius: 99px; padding: 2px 9px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .04em;
}
@media (prefers-reduced-motion: reduce) {
  .hero .chip::before { animation: none; }
}

/* ---------- type ---------- */

h1, h2, h3 { font-family: var(--disp); color: var(--tx); text-wrap: balance; }

h1 {
  font-size: clamp(40px, 5.3vw, 74px);
  font-weight: 500; letter-spacing: -.04em; line-height: 1.1;
  max-width: 17ch; margin-inline: auto;
}
h2 {
  font-size: clamp(30px, 3.7vw, 50px);
  font-weight: 500; letter-spacing: -.038em; line-height: 1.15;
  max-width: 21ch;
}
.center h2 { margin-inline: auto; }
h3 { font-size: 19px; font-weight: 500; letter-spacing: -.02em; line-height: 1.3; }

/* the serif italic emphasis is retired per David (2026-08-22). em reads in
   the same face as the words around it, site-wide; the tags stay for
   meaning and AEO. */
em, .accent {
  font-family: inherit; font-style: normal; font-weight: inherit;
  letter-spacing: inherit; color: inherit;
}

.lede {
  font-size: clamp(16.5px, 1.35vw, 19px);
  line-height: 1.55; color: var(--tx-mut);
  max-width: 52ch; margin-top: 22px;
  text-wrap: pretty;
}
.center .lede, .hero .lede, .close-scene .lede { margin-inline: auto; }

.body-lg {
  font-size: 16.5px; line-height: 1.65; color: var(--tx-mut);
  max-width: 60ch; text-wrap: pretty;
}
.kicker {
  font-size: clamp(17px, 1.5vw, 20px); line-height: 1.55;
  color: var(--tx); max-width: 46ch; margin-inline: auto;
  text-wrap: balance;
}
.caption { font-size: 13px; color: var(--tx-faint); }
.plate-cap { margin-top: 18px; font-size: 12.5px; color: var(--tx-faint); }

/* ---------- scenes ---------- */

.scene { position: relative; padding-block: clamp(88px, 12vh, 150px); }
.scene.center { text-align: center; }
.scene > .wrap { position: relative; z-index: 1; }

/* ---------- hero with ribbon video ---------- */

.hero {
  min-height: max(100svh, 680px);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding-block: calc(var(--head-h) + 34px) 30px;
  text-align: center;
  overflow: hidden;
}
/* the manifesto scrolls OVER the hero. the hero is FIXED (not JS
   counter-translated, which lags scroll by a frame and wobbles), so it
   cannot move at all; .hero-spacer holds its slot in the flow (height set
   by story.js heroStep) and heroStep only drives the blur dissolve */
@media (min-width: 761px) {
  html.anim .hero {
    position: fixed; top: 0; left: 0; width: 100%; z-index: 0;
    will-change: filter;
  }
}

.bg-media {
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden; pointer-events: none;
}
/* no edge mask on the hero ribbon, per David it runs edge to edge; the
   fixed hero never exposes its bounds, so no cutoff can show */
.bg-media video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
/* the video stays bright. legibility comes from a scrim behind the copy,
   not from dimming the whole frame. */
.bg-media::after {
  content: ""; position: absolute; inset: 0; z-index: 2;
  background:
    radial-gradient(52% 50% at 50% 50%, rgba(0,0,0,.78), rgba(0,0,0,.44) 56%, rgba(0,0,0,.1) 80%),
    linear-gradient(180deg, rgba(0,0,0,.55), rgba(0,0,0,.18) 26%, rgba(0,0,0,.18) 70%, #000 97%);
}
.close-scene .bg-media::after {
  background:
    radial-gradient(52% 50% at 50% 46%, rgba(0,0,0,.6), rgba(0,0,0,.26) 58%, rgba(0,0,0,0) 80%),
    linear-gradient(180deg, #000, rgba(0,0,0,0) 22%, rgba(0,0,0,0) 74%, #000 98%);
}

/* the CSS aurora sits UNDER the video as the no-video and reduced-motion
   fallback, so the hero is never a flat black void. */
.aurora {
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden; pointer-events: none; isolation: isolate;
}
.aurora span {
  position: absolute; display: block; border-radius: 50%;
  mix-blend-mode: screen; filter: blur(80px); opacity: var(--o, .6);
}
.aurora span:nth-child(1) {
  --o: .8; left: -28%; top: 24%; width: 150%; height: 48%; rotate: -13deg;
  background: radial-gradient(closest-side, rgba(56, 78, 210, .85), rgba(30, 40, 130, .4) 55%, transparent 80%);
}
.aurora span:nth-child(2) {
  --o: .65; left: -8%; top: 32%; width: 110%; height: 38%; rotate: -17deg;
  background: radial-gradient(closest-side, rgba(140, 74, 224, .8), rgba(80, 36, 150, .35) 58%, transparent 80%);
}
.aurora span:nth-child(3) {
  --o: .55; left: 4%; top: 42%; width: 60%; height: 24%; rotate: -11deg;
  background: radial-gradient(closest-side, rgba(126, 196, 227, .7), transparent 78%);
}

/* ---------- fact strip, the honest stand-in for a logo wall ---------- */

.factstrip {
  padding-block: 34px 60px;
  text-align: center;
}
.factstrip p {
  font-size: 13px; letter-spacing: .02em; color: var(--tx-faint);
  margin-bottom: 18px;
}
.factstrip ul {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 12px 54px; list-style: none;
}
.factstrip li {
  font-family: var(--disp); font-weight: 500; font-size: 17px;
  letter-spacing: -.01em; color: var(--tx-mut);
  font-variant-numeric: tabular-nums;
}
.factstrip li b { font-weight: 600; color: var(--tx-mid); }

/* ---------- manifesto · pinned scroll-lock statement ---------- */

.manifesto { text-align: left; overflow-x: clip; }
.mani-stage { position: relative; width: 100%; }

/* the finale line. in the static page it simply follows the paragraphs. */
.mani-final { position: relative; margin-top: 72px; }
.mani-final-inner > p {
  font-family: var(--disp); font-weight: 500;
  font-size: clamp(28px, 3.6vw, 52px);
  letter-spacing: -.034em; line-height: 1.25;
  color: #fff; text-align: center;
  max-width: 19em; margin-inline: auto;
  text-wrap: balance;
}
.mani-final .aurora {
  z-index: -1;
  overflow: visible;
  inset: -20% -26%;
}
/* no vignette here. the page is already black, and the vignette's ellipse
   is what reads as an edge when the finale fades in. */
.mani-final .aurora::after { display: none; }
@media (max-width: 760px) {
  .mani-final .aurora { display: none; }
}
.mani-final-inner { position: relative; }
.mani-stats {
  list-style: none; margin-top: 44px;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 12px 46px;
}
.mani-stats li {
  font-family: var(--disp); font-weight: 500; font-size: 16.5px;
  letter-spacing: -.01em; color: var(--tx-mid);
  font-variant-numeric: tabular-nums;
}
.mani-stats li b { font-weight: 600; color: var(--tx); margin-right: 2px; }
.mani-final-inner > p em {
  font-family: inherit; font-style: normal; font-weight: inherit;
  letter-spacing: inherit;
}

/* the pin. desktop, motion allowed. scroll drives the words, then the
   paragraphs stand back and the finale takes the stage. */
@media (min-width: 761px) {
  /* the negative margin pulls the manifesto up over the fixed hero's slot
     so its text enters the viewport soon after scrolling starts, instead
     of half a screen of empty centered stage arriving first (per David
     2026-08-23, "text should move up sooner") */
  html.anim .manifesto { height: 300vh; padding-block: 0; margin-top: -20svh; }
  html.anim .mani-stage {
    position: sticky; top: 0; height: 100svh;
    display: flex; align-items: center; justify-content: center;
  }
  /* the whole block also fades, slightly behind the tokens. this is the
     guarantee: even if a token's exit transition is interrupted or never
     runs, the body cannot linger under the finale. */
  /* the exit and the return are ASYMMETRIC on purpose (2026-08-23, David
     flagged half-exploded words hanging on reverse scroll). Exiting, the
     tokens fly slow with shockwave delays and the body fade trails as the
     backup. Returning, the tokens snap home FAST and uniform while the
     body is still invisible, and only then does the body resolve in, so a
     mid-flight state can never be seen half-scattered. The transition that
     applies is always the destination state's, which is what makes the two
     directions differ. */
  html.anim .mani-body {
    transition: opacity .5s var(--ease) .05s;
  }
  html.anim .manifesto.done .mani-body {
    opacity: 0; pointer-events: none;
    transition: opacity .7s var(--ease) .1s; /* exit: trail the tokens */
  }
  /* story.js holds .returning for ~.38s after the finale releases: the
     body is forced dark and the tokens go transparent IN ONE FRAME, travel
     home invisible, then the class drops and the assembled body resolves
     in. a half-exploded token can never be on screen */
  html.anim .manifesto.returning .mani-body {
    opacity: 0;
    visibility: hidden; /* atomic. opacity alone cannot cover the reversal,
      the exit's slow fade keeps RUNNING because the reversal never changes
      the opacity target (0 mid-exit, 0 here), so the browser retains the
      old transition. visibility is not transitioned anywhere, it hides the
      block in the same frame */
    transition: opacity .1s var(--ease);
  }
  html.anim .manifesto.returning .mani-body .mani-w,
  html.anim .manifesto.returning .mani-body .mchip {
    opacity: 0;
    transition:
      color .3s var(--ease),
      background .3s var(--ease),
      border-color .3s var(--ease),
      box-shadow .3s var(--ease),
      transform .32s var(--ease),
      opacity 0s;
  }
  /* chips keep their inline-flex (still transformable), so the icon gap
     survives; only plain words need the inline-block upgrade */
  html.anim .mani-body .mani-w { display: inline-block; }
  html.anim .mani-body .mani-w,
  html.anim .mani-body .mchip {
    transition:
      color .3s var(--ease),
      background .3s var(--ease),
      border-color .3s var(--ease),
      box-shadow .3s var(--ease),
      transform .32s var(--ease),
      opacity .28s var(--ease);
  }
  html.anim .manifesto.done .mani-body .mani-w,
  html.anim .manifesto.done .mani-body .mchip {
    transform: translate(var(--ex, 0px), var(--ey, -60vh)) rotate(var(--er, 0deg)) scale(.3);
    opacity: 0;
    transition:
      color .3s var(--ease),
      background .3s var(--ease),
      border-color .3s var(--ease),
      box-shadow .3s var(--ease),
      transform 1s var(--ease) var(--ed, 0s),
      opacity .85s var(--ease) var(--ed, 0s);
  }
  /* the finale. the headline PUNCHES IN at the center as one unit, fast and
     slightly overshooting, so it reads as the thing that knocked the old
     words apart. the container itself only carries a quick backdrop bloom. */
  html.anim .mani-final {
    position: absolute; inset: 0; margin: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    opacity: 0; pointer-events: none;
    transition: opacity .4s var(--ease);
  }
  html.anim .manifesto.done .mani-final {
    opacity: 1; pointer-events: auto; transition-delay: .05s;
  }
  html.anim .mani-final-inner {
    transform: scale(.62); opacity: 0; filter: blur(14px);
    transition:
      transform .55s cubic-bezier(.22, .9, .28, 1.12),
      opacity .4s var(--ease),
      filter .5s var(--ease);
  }
  html.anim .manifesto.done .mani-final-inner {
    transform: none; opacity: 1; filter: none;
  }
  /* the stats rise once the headline has landed */
  html.anim .mani-stats li {
    opacity: 0; transform: translateY(16px);
    transition: opacity .6s var(--ease), transform .6s var(--ease);
  }
  html.anim .manifesto.done .mani-stats li { opacity: 1; transform: none; }
  html.anim .manifesto.done .mani-stats li:nth-child(1) { transition-delay: .9s; }
  html.anim .manifesto.done .mani-stats li:nth-child(2) { transition-delay: .98s; }
  html.anim .manifesto.done .mani-stats li:nth-child(3) { transition-delay: 1.06s; }
  html.anim .manifesto.done .mani-stats li:nth-child(4) { transition-delay: 1.14s; }
  /* the aura behind the finale is a static gradient wash. it does NOT animate
     and carries no blur filter. measured: animating a blurred element forces
     the browser to re-run an 80px blur every frame, which cost more than the
     entire 54 card field. the radial falloffs are already soft, so the filter
     bought nothing. the life in this scene comes from the floating cards. */
  html.anim .mani-final .aurora span {
    filter: none;
    animation: none;
    opacity: calc(var(--o, .6) * 1.25);
  }
}
/* opacity and translate only. animating scale() on an 80px-blurred element
   forces the browser to re-run the blur every frame, which was costing more
   than the entire 54-card field. translate moves a cached texture instead. */
@keyframes aura-breathe {
  from { transform: translate3d(-2.5%, 1.5%, 0); opacity: .5; }
  to   { transform: translate3d(3%, -2.5%, 0); opacity: .95; }
}
.mani-body { max-width: 24em; margin-inline: auto;
  font-family: var(--disp); font-weight: 500;
  font-size: clamp(22px, 2.9vw, 38px);
  letter-spacing: -.028em; line-height: 1.38;
  color: var(--tx);
}
.mani-body p { text-wrap: pretty; }
.mani-body p + p { margin-top: 1.05em; }
.mchip {
  display: inline-flex; align-items: center; gap: .32em;
  vertical-align: middle;
  position: relative; top: -.06em;
  margin-inline: .06em;
  padding: .2em .6em; border-radius: 99px;
  background: var(--fill); border: 1px solid var(--line-2);
  font-size: .58em; font-weight: 500; letter-spacing: 0;
  line-height: 1.4; color: var(--tx-mid); white-space: nowrap;
}
.mchip svg {
  flex: none; width: 1.22em; height: 1.22em;
  stroke: var(--tx-mut); fill: none; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.mchip .lfill { fill: var(--tx-mut); stroke: none; }

/* interactive chips. each icon has its own move on hover. */
.mchip {
  cursor: default;
  transition: background .3s var(--ease), border-color .3s var(--ease),
    color .3s var(--ease), transform .3s var(--ease), box-shadow .3s var(--ease);
}
.mchip:hover {
  background: rgba(255, 255, 255, .13);
  border-color: rgba(255, 255, 255, .34);
  color: var(--tx);
  transform: translateY(-2px);
  box-shadow: 0 6px 20px -8px rgba(0, 0, 0, .6);
}
.mchip:hover svg { stroke: var(--tx); }
.mchip:hover .lfill { fill: var(--tx); }

/* lit chips. the scroll sweep reaches them and they switch ON, each in its
   own color, so the five promises pop instead of reading as furniture. */
.mchip.lit {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .36);
  color: #fff;
}
.mchip.lit svg { stroke: currentColor; }
.mchip.lit .lfill { fill: currentColor; }
.mchip--imagine.lit { border-color: rgba(255, 214, 130, .5); box-shadow: 0 0 22px -4px rgba(255, 214, 130, .55); }
.mchip--imagine.lit svg { stroke: #FFD98A; }
.mchip--date.lit { border-color: rgba(155, 209, 255, .5); box-shadow: 0 0 22px -4px rgba(155, 209, 255, .5); }
.mchip--date.lit svg { stroke: #9BD1FF; }
.mchip--date.lit .lfill { fill: #9BD1FF; }
.mchip--price.lit { border-color: rgba(159, 240, 181, .5); box-shadow: 0 0 22px -4px rgba(159, 240, 181, .5); }
.mchip--price.lit svg { stroke: #9FF0B5; }
.mchip--fast.lit { border-color: rgba(255, 233, 150, .55); box-shadow: 0 0 22px -4px rgba(255, 233, 150, .6); }
.mchip--fast.lit svg { stroke: #FFE996; fill: #FFE996; }
.mchip--wedge.lit { border-color: rgba(255, 90, 110, .55); box-shadow: 0 0 22px -4px rgba(255, 90, 110, .6); }
.mchip--wedge.lit .lfill { fill: #FF5A6E; }

/* fast. the bolt strikes, fills bright, and flashes. */
.mchip--fast:hover svg {
  animation: bolt-jolt .5s var(--ease);
  fill: currentColor;
  filter: drop-shadow(0 0 6px rgba(255, 233, 150, .9));
  stroke: #FFE996;
}
@keyframes bolt-jolt {
  0%   { transform: none; }
  20%  { transform: translate(-1.5px, 1px) rotate(-9deg) scale(1.2); }
  45%  { transform: translate(1.5px, -1px) rotate(7deg) scale(1.25); }
  70%  { transform: translate(-1px, .5px) rotate(-4deg) scale(1.1); }
  100% { transform: none; }
}

/* Wedge. the mark drives in like the logo. */
.mchip--wedge svg { transform-box: view-box; transform-origin: 50% 60%; }
.mchip--wedge:hover svg { animation: wedge-drive .55s var(--ease); }
@keyframes wedge-drive {
  0%   { transform: translateX(-10px) rotate(-24deg); opacity: 0; }
  60%  { transform: translateX(1.5px) rotate(4deg); opacity: 1; }
  100% { transform: none; }
}

/* any time. the clock hand sweeps a full lap. */
.mchip--time svg .hand { transform-box: view-box; transform-origin: 50% 50%; }
.mchip--time:hover svg .hand { animation: hand-lap .8s cubic-bezier(.5, 0, .2, 1); }
@keyframes hand-lap { to { transform: rotate(360deg); } }

/* imagined. the bulb switches on. */
.mchip--imagine svg { transform-box: view-box; transform-origin: 50% 45%; }
.mchip--imagine:hover svg {
  animation: bulb-on .55s var(--ease);
  filter: drop-shadow(0 0 7px rgba(255, 226, 130, .95));
}
.mchip--imagine:hover .bulb { fill: #FFE28A; stroke: #FFE28A; }
@keyframes bulb-on {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.25); }
  60%  { transform: scale(.96); }
  100% { transform: scale(1); }
}

/* on the date. the calendar pops and draws its check. */
.mchip--date svg { transform-box: view-box; transform-origin: 50% 55%; }
.mchip--date .cal-check { stroke-dasharray: 1; stroke-dashoffset: 1; }
.mchip--date:hover svg { animation: cal-pop .45s var(--ease); }
.mchip--date:hover .cal-check {
  stroke: #9FE8B8;
  animation: check-draw .45s .12s var(--ease) forwards;
}
@keyframes cal-pop {
  0% { transform: scale(1); } 40% { transform: scale(1.18); } 100% { transform: scale(1); }
}
@keyframes check-draw { to { stroke-dashoffset: 0; } }

/* price. the note wobbles and the coin pings. */
.mchip--price svg { transform-box: view-box; transform-origin: 50% 50%; }
.mchip--price .coin { transform-box: view-box; transform-origin: 50% 50%; }
.mchip--price:hover svg { animation: note-wobble .6s var(--ease); }
.mchip--price:hover .coin { animation: coin-ping .6s .1s var(--ease); stroke: #9FE8B8; }
@keyframes note-wobble {
  0%   { transform: rotate(0); }
  30%  { transform: rotate(-7deg) scale(1.12); }
  65%  { transform: rotate(5deg); }
  100% { transform: rotate(0); }
}
@keyframes coin-ping {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.6); }
  100% { transform: scale(1); }
}

/* ---------- hairline cell rows ---------- */

.cellrow {
  display: grid;
  border: 1px solid var(--line); border-radius: 18px;
  overflow: hidden; text-align: left;
  margin-top: 52px;
}
.cellrow.cols-3 { grid-template-columns: repeat(3, 1fr); }
.cellrow.cols-3 .cell:nth-child(3n + 1) { border-left: 0; }
.cellrow.cols-3 .cell:nth-child(n + 4) { border-top: 1px solid var(--line); }
.cell {
  padding: 30px 26px 32px;
  border-left: 1px solid var(--line);
  transition: background .3s var(--ease);
}
.cell:hover { background: rgba(255, 255, 255, .03); }
.cell svg {
  display: block; margin-bottom: 18px;
  stroke: var(--tx-mut); fill: none; stroke-width: 1.3;
  stroke-linecap: round; stroke-linejoin: round;
}
.cell h3 { margin-bottom: 8px; }
.cell p { font-size: 14.5px; line-height: 1.6; color: var(--tx-mut); }
.cell .cadence { display: block; margin-top: 12px; font-size: 12px; letter-spacing: .04em; color: var(--tx-faint); }

/* ---------- feature sections, copy beside a ribbon art card ---------- */

.feature { padding-block: clamp(56px, 8vh, 96px); }
.feature .wrap {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(36px, 5vw, 80px); align-items: center;
}
.feature.flip .wrap { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
.feature.flip .f-copy { order: 2; }
.feature.flip .f-art { order: 1; }

.f-label {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 500; margin-bottom: 18px;
}
.f-label svg { stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.f-label.blue { color: var(--acc-blue); }
.f-label.green { color: var(--acc-green); }
.f-label.amber { color: var(--acc-amber); }

.f-copy h2 { margin-bottom: 18px; }
.f-copy .caption { margin-top: 14px; }
.f-copy .btn { margin-top: 26px; }

.f-art {
  position: relative;
  border-radius: 22px; overflow: hidden;
  aspect-ratio: 1 / 0.96;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(20px, 3.5vw, 44px);
}
.f-art > img {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
}
/* no vignette on the art. the washes stay flat and bright to the edge. */

/* coded ribbon art. each view's backdrop is a layered gradient wash in the
   palette sampled from the old ribbon renders, and the blobs drift slowly
   so the art is alive even while the view holds. */
.fa-grad { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.fa-grad span {
  position: absolute; border-radius: 50%;
  animation: fa-float 13s ease-in-out infinite alternate;
  will-change: translate, scale;
}
.fa-grad span:nth-child(2) { animation-duration: 17s; animation-delay: -6s; }
.fa-grad span:nth-child(3) { animation-duration: 21s; animation-delay: -11s; }
@keyframes fa-float {
  from { translate: -5% 4%; scale: 1; }
  to   { translate: 6% -5%; scale: 1.14; }
}
/* timeline · vivid blue rising into lilac, luminous end to end. no dark
   accents anywhere, per David: the washes stay bright */
.fa-a { background: linear-gradient(168deg, #E8DEFF 0%, #BCC2FF 44%, #2E86FE 100%); }
.fa-a span:nth-child(1) { left: -20%; bottom: -18%; width: 105%; height: 95%; background: radial-gradient(closest-side, #1180FE 6%, rgba(17, 128, 254, 0) 74%); }
.fa-a span:nth-child(2) { right: -16%; top: -18%; width: 95%; height: 85%; background: radial-gradient(closest-side, #F2EBFF 10%, rgba(242, 235, 255, 0) 75%); }
.fa-a span:nth-child(3) { left: 18%; top: 4%; width: 85%; height: 78%; background: radial-gradient(closest-side, #92A6FF 0%, rgba(146, 166, 255, 0) 72%); }
/* updates · luminous violet with an amber corner */
.fa-b { background: linear-gradient(150deg, #C0B8FF 0%, #8880FF 48%, #6659F0 100%); }
.fa-b span:nth-child(1) { right: -20%; top: -22%; width: 95%; height: 85%; background: radial-gradient(closest-side, #F2CE9A 8%, rgba(242, 206, 154, 0) 72%); }
.fa-b span:nth-child(2) { left: -16%; bottom: -20%; width: 100%; height: 90%; background: radial-gradient(closest-side, #7488FF 0%, rgba(116, 136, 255, 0) 74%); }
.fa-b span:nth-child(3) { left: -14%; top: -20%; width: 85%; height: 75%; background: radial-gradient(closest-side, #D4CCFF 6%, rgba(212, 204, 255, 0) 72%); }
/* approvals · pink falling into ember orange */
.fa-c { background: linear-gradient(160deg, #FFC7FF 0%, #FF94FF 40%, #FF6434 100%); }
.fa-c span:nth-child(1) { left: -18%; bottom: -22%; width: 100%; height: 90%; background: radial-gradient(closest-side, #FF6434 8%, rgba(255, 100, 52, 0) 74%); }
.fa-c span:nth-child(2) { right: -20%; top: 8%; width: 90%; height: 82%; background: radial-gradient(closest-side, #573457 0%, rgba(87, 52, 87, 0) 70%); opacity: .55; }
.fa-c span:nth-child(3) { left: 6%; top: -22%; width: 90%; height: 78%; background: radial-gradient(closest-side, #FFD4FF 10%, rgba(255, 212, 255, 0) 74%); }

/* the floating product widget on the art */
.fw {
  position: relative; z-index: 2;
  width: min(400px, 100%);
  background: #14161C;
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: 16px;
  box-shadow: 0 10px 28px -20px rgba(0, 0, 0, .3);
  color: #E8EAF0;
  font-size: 13px; line-height: 1.5;
  padding: 16px;
  text-align: left;
}
.fw-head {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 11.5px; letter-spacing: .04em; color: rgba(232, 234, 240, .55);
  margin-bottom: 12px;
}
.fw-head b { font-weight: 500; color: rgba(232, 234, 240, .8); letter-spacing: 0; }
.fw-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 12px; border-radius: 10px;
  background: rgba(255, 255, 255, .045);
  border: 1px solid rgba(255, 255, 255, .06);
}
.fw-row + .fw-row, .fw-row + .fw-msg, .fw-msg + .fw-row { margin-top: 8px; }
.fw-row .t { color: #E8EAF0; font-weight: 500; }
.fw-row .d { color: rgba(232, 234, 240, .5); font-size: 12px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.fw-pill {
  font-size: 11px; font-weight: 500; padding: 3px 9px; border-radius: 99px; white-space: nowrap;
}
.fw-pill.done { background: rgba(126, 234, 155, .14); color: var(--acc-green); }
.fw-pill.now { background: rgba(126, 196, 227, .16); color: var(--acc-blue); }
.fw-pill.wait { background: rgba(255, 255, 255, .08); color: rgba(232, 234, 240, .6); }

.fw-msg {
  padding: 11px 12px; border-radius: 10px;
  background: rgba(255, 255, 255, .045);
  border: 1px solid rgba(255, 255, 255, .06);
}
.fw-msg .who {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 11.5px; color: rgba(232, 234, 240, .55); margin-bottom: 4px;
}
.fw-msg .who b { color: rgba(232, 234, 240, .85); font-weight: 500; }
.fw-compose {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 8px; padding: 10px 12px; border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, .12);
  color: rgba(232, 234, 240, .45); font-size: 12.5px;
}
.fw-compose .send {
  background: #FAFAFA; color: #000; font-weight: 500; font-size: 12px;
  padding: 6px 12px; border-radius: 99px; white-space: nowrap;
}
.fw-btns { display: flex; gap: 8px; margin-top: 10px; }
.fw-btns .ok {
  flex: 1; text-align: center; background: #FAFAFA; color: #000;
  font-weight: 500; font-size: 12.5px; padding: 8px 0; border-radius: 99px;
}
.fw-btns .no {
  flex: 1; text-align: center; background: rgba(255, 255, 255, .08);
  color: rgba(232, 234, 240, .8); font-size: 12.5px; padding: 8px 0; border-radius: 99px;
}
.fw-redact {
  display: inline-block; width: 4.2em; height: .75em; border-radius: 2px;
  background: var(--red); vertical-align: -1px;
}

/* ---------- feature grid · one rounded panel, two columns, hairline
   dividers, roomy cells (reference: big features panel) ---------- */

.fgrid {
  display: grid; grid-template-columns: 1fr 1fr;
  border: 1px solid var(--line); border-radius: 18px;
  overflow: hidden; text-align: left; margin-top: 72px;
}
.fgrid .cell {
  padding: clamp(34px, 4.5vw, 56px) clamp(28px, 4vw, 60px);
  border-left: 1px solid var(--line); border-top: 1px solid var(--line);
}
.fgrid .cell:nth-child(odd) { border-left: 0; }
.fgrid .cell:nth-child(-n + 2) { border-top: 0; }
.fgrid .cell h3 { font-size: 17px; margin: 20px 0 10px; }
.fgrid .cell p { font-size: 15px; line-height: 1.65; max-width: 46ch; }

/* ---------- case studies · alternating statement + gradient plate ---------- */

.case-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(36px, 6vw, 90px); align-items: center;
  text-align: left; margin-top: clamp(64px, 9vh, 120px);
}
.case-row-flip .case-copy { order: 2; }
.case-row-flip .case-art { order: 1; }
.case-line {
  font-family: var(--disp); font-weight: 500;
  font-size: clamp(21px, 2.1vw, 27px);
  letter-spacing: -.022em; line-height: 1.42;
  color: var(--tx); text-wrap: pretty;
}
.case-stats { display: flex; gap: 46px; margin-top: 30px; }
.case-stats b {
  display: block; font-family: var(--disp); font-weight: 600;
  font-size: 24px; letter-spacing: -.02em; color: var(--tx);
  font-variant-numeric: tabular-nums;
}
.case-stats span { display: block; margin-top: 4px; font-size: 13.5px; color: var(--tx-faint); }
.case-copy .btn { margin-top: 34px; }
.case-art {
  aspect-ratio: 1 / 0.92; border-radius: 22px;
  display: flex; align-items: center; justify-content: center;
}
.case-art-a { background: radial-gradient(85% 70% at 78% 18%, rgba(190, 225, 255, .5), transparent 60%), radial-gradient(100% 85% at 15% 88%, #0B1B8E, transparent 72%), linear-gradient(155deg, #2436E0, #1520A8); }
.case-art-b { background: radial-gradient(85% 70% at 20% 15%, rgba(255, 214, 170, .55), transparent 62%), radial-gradient(100% 85% at 85% 88%, #8A5CFF, transparent 70%), linear-gradient(150deg, #F0B45C, #C86AB8); }
.case-mark {
  display: inline-flex; flex-direction: column; align-items: center; gap: 6px;
  font-family: var(--disp); font-weight: 600; font-size: 21px;
  letter-spacing: -.01em; color: #fff; text-align: center;
}
.case-mark svg { margin-bottom: 2px; }
.case-mark i {
  font-style: normal; font-weight: 500; font-size: 12px;
  letter-spacing: .04em; color: rgba(255, 255, 255, .75);
}

/* ---------- integrations · copy beside a field of marks ---------- */

.intg {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(36px, 6vw, 90px); align-items: center;
}
.intg-copy h2 { margin-bottom: 18px; }
.intg-copy .lede { margin-bottom: 8px; }
.intg-copy .btn { margin-top: 26px; }
.intg-grid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(18px, 2.6vw, 40px); justify-items: center;
}
.ig {
  width: 46px; height: 46px; display: inline-flex;
  align-items: center; justify-content: center;
  color: rgba(255, 255, 255, .55);
  transition: color .3s var(--ease), transform .3s var(--ease);
}
.ig svg { width: 30px; height: 30px; }
.ig-dim { color: rgba(255, 255, 255, .28); }
.ig:hover { color: #fff; transform: translateY(-2px); }

/* ---------- reviews · one voice at a time, defocus handoff ---------- */

.rev-stage { display: grid; max-width: 780px; margin-inline: auto; }
.rev {
  grid-area: 1 / 1; margin: 0;
  opacity: 0; filter: blur(10px); transform: translateY(14px);
  transition: opacity .7s var(--ease), filter .7s var(--ease), transform .7s var(--ease);
  pointer-events: none;
}
.rev.on { opacity: 1; filter: none; transform: none; pointer-events: auto; transition-delay: .25s; }
.rev blockquote {
  margin: 0; font-family: var(--disp); font-weight: 500;
  font-size: clamp(22px, 2.4vw, 32px);
  letter-spacing: -.024em; line-height: 1.4;
  color: var(--tx); text-wrap: balance;
}
.rev figcaption {
  margin-top: 30px; display: flex; align-items: center; gap: 12px;
  justify-content: center;
}
.rev-ava {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .1); border: 1px solid var(--line-2);
  font-size: 13px; font-weight: 600; letter-spacing: .02em; color: var(--tx-mid);
}
.rev-who { text-align: left; }
.rev-who b { display: block; font-size: 14px; font-weight: 600; color: var(--tx); }
.rev-who i { font-style: normal; font-size: 13px; color: var(--tx-faint); }
.rev-dots { display: flex; gap: 8px; justify-content: center; margin-top: 40px; }
.rev-dot {
  width: 26px; height: 4px; border-radius: 99px; border: 0; padding: 0;
  background: rgba(255, 255, 255, .16); cursor: pointer;
  transition: background .3s var(--ease), width .3s var(--ease);
}
.rev-dot.on { background: rgba(255, 255, 255, .85); width: 34px; }
.rev-note { margin-top: 26px; font-size: 12.5px; color: var(--tx-faint); }

@media (max-width: 760px) {
  .case-row, .intg { grid-template-columns: 1fr; }
  .case-row-flip .case-copy { order: 1; }
  .case-row-flip .case-art { order: 2; }
}

/* ---------- stat grid ---------- */

.statgrid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  max-width: 880px; margin: 52px auto 0;
  border: 1px solid var(--line); border-radius: 18px;
  overflow: hidden;
}
.stat { padding: 30px 18px 26px; border-left: 1px solid var(--line); }
.statgrid .stat:nth-child(3n + 1) { border-left: 0; }
.statgrid .stat:nth-child(n + 4) { border-top: 1px solid var(--line); }
.stat b {
  display: block; font-family: var(--disp); font-weight: 500;
  font-size: clamp(30px, 3.4vw, 44px); letter-spacing: -.035em;
  color: var(--tx); line-height: 1.1; font-variant-numeric: tabular-nums;
}
.stat span { display: block; margin-top: 6px; font-size: 13.5px; color: var(--tx-faint); }
.statgrid + .kicker { margin-top: 44px; }

/* ---------- FAQ ---------- */

.faq-list { max-width: 720px; margin: 44px auto 0; text-align: left; }
.faq {
  border-bottom: 1px solid var(--line);
}
.faq:first-child { border-top: 1px solid var(--line); }
.faq summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  padding: 20px 4px;
  font-family: var(--disp); font-weight: 500; font-size: 18px;
  letter-spacing: -.015em; color: var(--tx);
  transition: color .2s var(--ease);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+"; flex: none;
  font-family: var(--sans); font-weight: 400; font-size: 20px;
  color: var(--tx-faint);
  transition: transform .25s var(--ease);
}
.faq[open] summary::after { transform: rotate(45deg); }
.faq p {
  padding: 0 4px 20px;
  font-size: 15.5px; line-height: 1.65; color: var(--tx-mut);
  max-width: 60ch;
}
/* answers materialize in place, a pure defocus, no motion (rise removed
   per David 2026-08-23) */
.faq[open] p { animation: faq-reveal .6s var(--ease) both; }
@keyframes faq-reveal {
  from { opacity: 0; filter: blur(10px); }
  to { opacity: 1; filter: blur(0); }
}
/* smooth height on browsers that can interpolate to auto; others keep the snap + blur */
@supports (interpolate-size: allow-keywords) {
  .faq { interpolate-size: allow-keywords; }
  .faq::details-content {
    height: 0; overflow: clip;
    transition: height .45s var(--ease), content-visibility .45s allow-discrete;
  }
  .faq[open]::details-content { height: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .faq[open] p { animation: none; }
  .faq::details-content { transition: none; }
}

/* ---------- close ---------- */

.close-scene {
  padding-block: clamp(140px, 20vh, 230px);
  text-align: center; overflow: hidden;
}
.founder-line { margin-top: 26px; font-size: 14px; color: var(--tx-mut); }
.founder-line a { color: var(--tx-mid); text-underline-offset: 3px; }
.founder-line a:hover { color: var(--tx); }

/* ---------- footer ---------- */

.site-foot { border-top: 1px solid var(--line); padding: 56px 0 40px; }
.foot-grid {
  display: grid; grid-template-columns: 1.6fr 1fr 1fr;
  gap: 40px;
}
.foot-brand p { margin-top: 14px; font-size: 14px; color: var(--tx-faint); max-width: 30ch; }
.foot-col h4 {
  font-family: var(--sans); font-size: 13px; font-weight: 500;
  color: var(--tx-mid); margin-bottom: 14px;
}
.foot-col a {
  display: block; padding: 5px 0;
  font-size: 14px; color: var(--tx-faint); text-decoration: none;
  transition: color .2s var(--ease);
}
.foot-col a:hover { color: var(--tx-mid); }
.foot-base {
  margin-top: 48px; padding-top: 22px;
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px 24px;
  font-size: 13px; color: var(--tx-faint);
}

/* ==========================================================================
   MOTION. Three layers, all gated so the static page stays complete.
   1. Load. The hero headline rises word by word out of a mask.
   2. Entrance. .w elements fade-rise once as they enter, staggered by --i.
   3. Scroll. Scrubbed timelines tied to scroll position, inside @supports,
      so Chrome and Safari get the full treatment and everything else gets
      the entrance layer only.
   ========================================================================== */

/* ---------- layer 2 · entrance reveals ---------- */

html.anim .w {
  opacity: 0;
  transform: translateY(18px);
  filter: blur(6px);
  transition:
    opacity .75s var(--ease) calc(var(--i, 0) * 90ms),
    transform .75s var(--ease) calc(var(--i, 0) * 90ms),
    filter .75s var(--ease) calc(var(--i, 0) * 90ms);
}
html.anim .w.in { opacity: 1; transform: none; filter: blur(0); }

/* art cards land with a touch of scale so they feel placed, not printed */
html.anim .w-scale {
  opacity: 0;
  transform: translateY(26px) scale(.965);
  filter: blur(9px);
  transition:
    opacity .85s var(--ease) calc(var(--i, 0) * 90ms),
    transform .85s var(--ease) calc(var(--i, 0) * 90ms),
    filter .85s var(--ease) calc(var(--i, 0) * 90ms);
}
html.anim .w-scale.in { opacity: 1; transform: none; filter: blur(0); }

/* ---------- intro loader · spin, stop, wipe, lift ---------- */

.intro-loader {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
}
/* the black is its own layer, so it can fade away under the lockup while
   the lockup is still flying to the header */
.intro-loader::before {
  content: ""; position: absolute; inset: 0; background: #050505;
}
.intro-loader.ready::before {
  animation: ld-bg-out .7s var(--ease) 1.15s forwards;
}
.intro-loader.gone { display: none; }
html.no-js .intro-loader { display: none; }
/* the lock is flex-centered, and the wordmark starts at zero width. as the
   word grows, flex centering slides the nut leftward on its own, so the
   mark spins dead-center first and then glides across while the name is
   revealed. --ldw is the measured wordmark width, set by story.js. */
/* the lockup is a 2x copy of the header brand (mark 18, gap 9, 16.5px
   text). the flight morphs these metrics to the brand's by layout, so the
   proportions are free to change, but keeping them in step reads best */
.ld-lock {
  position: relative;
  display: flex; align-items: center; gap: 18px;
}
/* after the flight lands, the handoff is ATOMIC, the real brand appears
   and the flying copy vanishes in the same frame, both without
   transitions. the copies are pixel-identical (the flight morphs layout,
   so the text is natively rasterized at landing size), and any overlap or
   crossfade would still show, stacked identical antialiased glyphs
   composite bolder at the edges, and a crossfade dips solid pixels to
   ~75%. one frame to one identical frame is the only invisible swap */
.intro-loader.landed .ld-lock { opacity: 0; }
/* the nut spins while the page actually loads (story.js decelerates it to
   upright once everything is in, so a slow network just means more turns),
   then .ready starts the wordmark and the fade */
.ld-mark {
  width: 36px; height: 36px; flex: none; fill: var(--tx);
  animation: ld-spin-loop .9s linear infinite;
}
.ld-word {
  font-family: var(--disp); font-size: 33px; font-weight: 600;
  letter-spacing: -.02em; color: var(--tx); white-space: nowrap;
  max-width: 0; overflow: hidden;
  margin-left: -18px; padding-left: 18px; /* swallow the flex gap until the word exists */
  filter: blur(6px);
}
.intro-loader.ready .ld-word {
  animation: ld-wipe .8s cubic-bezier(.3, .7, .25, 1) .25s forwards;
}
@keyframes ld-spin-loop { from { rotate: 0deg; } to { rotate: 360deg; } }
@keyframes ld-wipe {
  to { max-width: var(--ldw, 260px); filter: blur(0); }
}
@keyframes ld-bg-out { to { opacity: 0; } }
/* behind the fade the page waits blurred and paused; story.js lifts .intro
   as the fade starts, so the whole site resolves from a blur while the
   hero words stagger in. the real header brand stays hidden until the
   flying lockup lands on it (.intro-fly covers the flight window). */
html.intro .h1w > span { animation-play-state: paused; }
/* blur the header's nav and CTA rather than the header itself, so the
   brand's corner is already crisp when the flying lockup lands on it */
html.intro .site-nav, html.intro .head-cta, html.intro #main, html.intro .site-foot { filter: blur(22px); }
html.js .site-nav, html.js .head-cta, html.js #main, html.js .site-foot { transition: filter .9s var(--ease); }
/* no opacity transition on the brand, it must appear in the exact frame
   the flying copy is dropped (see the atomic handoff note above) */
html.intro .site-head .brand, html.intro-fly .site-head .brand { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .intro-loader { display: none; } }

/* leaving for the app pages (story.js adds the class, then navigates).
   opacity ONLY, a filter would give the fixed hero a new containing block
   and jump it mid-fade */
html.page-leave .site-head, html.page-leave #main, html.page-leave .site-foot {
  opacity: 0;
  transition: opacity .3s var(--ease);
}

/* arriving WITHOUT the intro loader (repeat visits): the page resolves in
   as one piece instead of stuttering element by element while assets land.
   opacity only, same fixed-hero rule as above */
html.page-enter .site-head, html.page-enter #main, html.page-enter .site-foot { opacity: 0; }
html.page-enter-go .site-head, html.page-enter-go #main, html.page-enter-go .site-foot {
  opacity: 1;
  transition: opacity .55s var(--ease);
}

/* ---------- layer 1 · hero word entrance, per word, blur clearing ---------- */

.h1w { display: inline-block; }
.h1w > span { display: inline-block; }
html.anim .h1w > span {
  opacity: 0;
  filter: blur(10px);
  translate: 0 16px;
  animation: word-in .9s var(--ease) both;
  animation-delay: calc(140ms + var(--wd, 0) * 70ms);
}
@keyframes word-in {
  to { opacity: 1; filter: blur(0); translate: 0 0; }
}

/* ---------- hero logo marquee · PLACEHOLDER marks, swap before deploy ---------- */

.hero-logos { width: 100%; margin-top: clamp(64px, 10vh, 118px); }
.hero-logos > p {
  font-size: 15px; letter-spacing: .01em; color: var(--tx-mid);
  text-align: center; margin-bottom: 36px;
}
.logos-marquee {
  overflow: hidden;
  max-width: min(720px, 86vw);
  margin-inline: auto;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}
.marq-inner {
  display: flex; width: max-content;
  animation: marq 38s linear infinite;
}
.logos-marquee ul {
  display: flex; align-items: center; list-style: none;
  gap: 54px; padding-right: 54px;
}
.logos-marquee li {
  display: inline-flex; align-items: center; gap: 10px;
  line-height: 1;
  font-family: var(--disp); font-weight: 600; font-size: 19.5px;
  letter-spacing: -.015em; color: rgba(255, 255, 255, .62);
  white-space: nowrap;
  transition: color .3s var(--ease);
}
.logos-marquee li:hover { color: rgba(255, 255, 255, .95); }
.logos-marquee svg { flex: none; display: block; fill: currentColor; }
@keyframes marq { to { transform: translateX(-50%); } }

/* ---------- manifesto scroll highlight, driven word by word from JS ---------- */

.mani-w { color: rgba(255, 255, 255, .18); transition: color .3s var(--ease); }
.mani-w.lit { color: rgba(255, 255, 255, .95); }
html.no-js .mani-w { color: inherit; }

/* ---------- responsive ---------- */

@media (max-width: 960px) {
  .feature .wrap, .feature.flip .wrap { grid-template-columns: 1fr; gap: 30px; }
  .feature.flip .f-copy { order: 1; }
  .feature.flip .f-art { order: 2; }
  .f-art { aspect-ratio: 1 / 0.82; }
  .fgrid { grid-template-columns: 1fr; }
  .fgrid .cell { border-left: 0; border-top: 1px solid var(--line); }
  .fgrid .cell:first-child { border-top: 0; }
  .fgrid .cell:nth-child(n + 3) { border-top: 1px solid var(--line); }
  .foot-grid { grid-template-columns: 1fr 1fr; }
  .foot-brand { grid-column: 1 / -1; }
}

@media (max-width: 760px) {
  :root { --head-h: 60px; }
  .logos-marquee ul { gap: 40px; padding-right: 40px; }
  .logos-marquee li { font-size: 17px; }
  .site-nav { display: none; }
  .site-head .wrap { grid-template-columns: 1fr auto; }
  .scene { padding-block: 72px; }
  .cellrow.cols-3 { grid-template-columns: 1fr; }
  .cellrow.cols-3 .cell { border-left: 0; }
  .cellrow.cols-3 .cell:nth-child(n + 2) { border-top: 1px solid var(--line); }
  .statgrid { grid-template-columns: 1fr 1fr; }
  .statgrid .stat:nth-child(3n + 1) { border-left: 1px solid var(--line); }
  .statgrid .stat:nth-child(odd) { border-left: 0; }
  .statgrid .stat:nth-child(n + 3) { border-top: 1px solid var(--line); }
  .factstrip ul { gap: 10px 30px; }
  .manifesto p { line-height: 1.5; }
  .aurora span { filter: blur(56px); }
}

@media (max-width: 560px) {
  .fgrid { grid-template-columns: 1fr; }
  .fgrid .cell { border-left: 0 !important; }
  .fgrid .cell:nth-child(n + 2) { border-top: 1px solid var(--line); }
  .btn-row .btn { width: 100%; max-width: 320px; }
  .foot-grid { grid-template-columns: 1fr; }
}

/* ---------- reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .bg-media video { display: none; }
  html.anim .w, html.anim .w-scale { opacity: 1; transform: none; filter: none; transition: none; }
  html.anim .h1w > span { animation: none; opacity: 1; filter: none; translate: 0 0; }
  .marq-inner { animation: none; }
  .marq-inner ul[aria-hidden] { display: none; }
  .mani-w { color: rgba(255, 255, 255, .95); transition: none; }
  .mani-final .aurora span { animation: none; }
  .mchip, .mchip:hover { transform: none; }
  .mchip:hover svg, .mchip:hover svg * { animation: none; }
}


/* ---------- feature tour · pinned full-screen ---------- */

.feature-tour { position: relative; padding-block: clamp(56px, 8vh, 96px); }
.ft-grid { display: flex; flex-direction: column; gap: 30px; }
.ft-card { margin-bottom: 48px; }
.ft-card:last-child { margin-bottom: 0; }

@media (min-width: 761px) {
  /* the copy column scrolls like normal page content, no motion effects.
     each beat holds roughly a viewport of scroll; the block driving the art
     is bright, the others sit dimmed, exactly like the reference. */
  html.anim .ft-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: 0 clamp(36px, 5vw, 80px);
    align-items: start;
    --artw: calc(min(1072px, 100vw - 48px) * 0.535);
    /* how far below the section top the art rests so that its static start
       equals its pinned (centered) position the moment the section settles */
    --ft-drop: calc((100svh - var(--artw) * 0.96) / 2 - clamp(56px, 8vh, 96px));
  }
  html.anim .ft-beat {
    grid-column: 1;
    min-height: 92svh;
    display: flex; flex-direction: column; justify-content: center;
    opacity: .3; filter: blur(5px);
    transition: opacity .55s var(--ease), filter .55s var(--ease);
  }
  html.anim .ft-beat.on { opacity: 1; filter: none; }
  /* flex column stretches children; buttons keep their natural width */
  html.anim .ft-beat .btn { align-self: flex-start; }
  /* the first beat starts level with the art, so the section arrives as a
     normally aligned copy-beside-art row scrolling together; the art only
     pins once it reaches the middle of the viewport */
  html.anim .ft-grid > .ft-beat:first-child {
    justify-content: flex-start;
    padding-top: var(--ft-drop);
    min-height: calc(var(--ft-drop) + var(--artw) * 0.96);
  }
  /* the last beat matches the art height too, so approvals ends as an
     aligned row and the art unpins and leaves WITH it into the next
     section (beat 3 is the second-to-last grid child, before card 3) */
  html.anim .ft-grid > .ft-beat:nth-last-child(2) { min-height: calc(var(--artw) * 0.96); }

  /* the art. the frame never moves and never fades; views hand over by
     layering. the outgoing ribbon stays opaque underneath while the incoming
     one focuses in on top, so the colors truly morph instead of dipping to
     black. scrolling back up, the leaving card floats above and defocuses
     away to reveal the previous view (the .leave class, set by JS with the
     scroll direction). */
  html.anim .ft-card {
    grid-column: 2; grid-row: 1 / 4;
    margin: var(--ft-drop) 0 0;
    position: sticky;
    top: calc((100svh - var(--artw) * 0.96) / 2);
    align-self: start;
    opacity: 0; z-index: 0;
    transition: opacity .85s var(--ease);
    pointer-events: none;
  }
  html.anim .ft-card.on { opacity: 1; z-index: 2; pointer-events: auto; }
  html.anim .ft-card.past { opacity: 1; z-index: 1; }
  html.anim .ft-card.leave { z-index: 3; }
  html.anim .ft-card .fa-grad {
    transform: scale(1.09); filter: blur(12px) saturate(1.12);
    transition: transform 1s var(--ease), filter 1s var(--ease);
  }
  html.anim .ft-card.on .fa-grad,
  html.anim .ft-card.past .fa-grad { transform: none; filter: none; }
  /* the widget rides its own beat: up from below, defocused, slightly after
     the backdrop starts to morph */
  html.anim .ft-card .fw {
    opacity: 0; transform: translateY(44px); filter: blur(10px);
    transition:
      opacity .6s var(--ease) .15s,
      transform .6s var(--ease) .15s,
      filter .6s var(--ease) .15s;
  }
  html.anim .ft-card.on .fw { opacity: 1; transform: none; filter: none; }
  html.anim .ft-card.past .fw {
    opacity: 0; transform: translateY(-32px); filter: blur(8px);
    transition-delay: 0s;
  }
}


/* ---------- past projects · horizontal card rail ---------- */

.projrail { overflow: hidden; }
.pr-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; margin-bottom: 34px;
}
.pr-head h2 { margin: 0; }
.pr-nav { display: flex; gap: 10px; flex: none; }
.pr-btn {
  width: 38px; height: 38px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--fill); border: 1px solid var(--line-2); color: var(--tx-mid);
  cursor: pointer; padding: 0;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
.pr-btn:hover { background: rgba(255, 255, 255, .14); color: var(--tx); border-color: rgba(255, 255, 255, .3); }
.pr-btn svg { stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.pr-track {
  display: flex; gap: 18px;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* first card aligns with the wrap's left edge; the rest bleed off-screen.
     the same inset feeds scroll-padding so snap respects it. */
  --pr-inset: max(24px, calc((100vw - 1120px) / 2 + 24px));
  padding-inline: var(--pr-inset);
  scroll-padding-inline: var(--pr-inset);
  padding-block: 6px 22px;
  scrollbar-width: none;
}
.pr-track::-webkit-scrollbar { display: none; }
.pr-card {
  position: relative; flex: 0 0 auto;
  width: clamp(270px, 26vw, 340px);
  aspect-ratio: 3 / 4;
  border-radius: 24px; overflow: hidden;
  scroll-snap-align: start;
  padding: 22px;
  display: flex; flex-direction: column; justify-content: flex-end;
  background: #0B0A12;
}
/* the art layer. oversized so its slow sway never shows an edge, blurred a
   touch so the conic sweeps blend like the renders they replace. */
.pr-art {
  position: absolute; inset: -22%; z-index: 0;
  filter: blur(14px) saturate(1.06);
  animation: pr-sway 34s ease-in-out infinite alternate;
}
.pr-card:nth-child(2n) .pr-art { animation-duration: 42s; animation-delay: -17s; }
.pr-card:nth-child(3n) .pr-art { animation-duration: 38s; animation-delay: -9s; }
@keyframes pr-sway {
  from { rotate: -7deg; scale: 1.02; }
  to   { rotate: 8deg; scale: 1.12; }
}
.pr-card::after {
  content: ""; position: absolute; inset: 46% 0 0 0; z-index: 0;
  background: linear-gradient(180deg, transparent, rgba(5, 4, 10, .55));
  transition: inset .5s var(--ease);
}
.pr-card.open::after { inset: 18% 0 0 0; }
.pr-plus {
  position: absolute; top: 16px; right: 16px; z-index: 2;
  width: 34px; height: 34px; border-radius: 50%; border: 0; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .24); color: #fff;
  backdrop-filter: blur(8px); cursor: pointer;
  transition: background .25s var(--ease), rotate .35s var(--ease);
}
.pr-plus svg { stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; }
.pr-plus:hover { background: rgba(255, 255, 255, .36); }
.pr-card.open .pr-plus { rotate: 45deg; background: rgba(255, 255, 255, .34); }
/* the description flips from the short line to the full story. both texts
   sit ON the card's base (absolute, bottom-anchored) so neither ever gets
   squished by a collapsing row; the container's height glides between the
   two measured heights (story.js sizeFlips) and the copy trades through a
   pure sequenced defocus, no translate (rebuilt per David 2026-08-23, the
   old grid-row trade clipped the text mid-flight and read rough) */
.pr-flip { position: relative; }
html.js .pr-flip { overflow: hidden; transition: height .5s var(--ease); }
html.js .pr-flip > div { position: absolute; left: 0; right: 0; bottom: 0; }
html.no-js .pr-flip > div:last-child { display: none; }
.pr-flip p { transition: opacity .22s var(--ease), filter .26s var(--ease); }
.pr-flip .pr-short { transition-delay: .26s; }
.pr-flip .pr-long { opacity: 0; filter: blur(8px); transition-delay: 0s; }
.pr-card.open .pr-short { opacity: 0; filter: blur(8px); transition-delay: 0s; }
.pr-card.open .pr-long { opacity: 1; filter: blur(0); transition-delay: .26s; }
.pr-tag {
  position: absolute; top: 14px; left: 14px; z-index: 1;
  font-size: 11px; font-weight: 500; letter-spacing: .02em;
  padding: 4px 10px; border-radius: 99px;
  background: rgba(8, 6, 16, .45); border: 1px solid rgba(255, 255, 255, .22);
  color: rgba(255, 255, 255, .92);
  backdrop-filter: blur(6px);
}
.pr-txt { position: relative; z-index: 1; text-align: left; }
.pr-txt h3 { font-size: 15.5px; font-weight: 600; letter-spacing: -.01em; color: #fff; margin: 0 0 6px; }
.pr-txt p { font-size: 12.8px; line-height: 1.5; color: rgba(255, 255, 255, .78); margin: 0; max-width: none; }

/* each card's art is a conic sweep in its own palette, like the reference
   renders, with a radial accent for depth */
.pr-g1 { background: radial-gradient(90% 70% at 82% 92%, rgba(102, 12, 24, .9), transparent 62%), conic-gradient(from 205deg at 66% 60%, #1B2ACF, #8A2BE2 16%, #E44BD0 36%, #B0179E 52%, #2440E8 72%, #1B2ACF); }
.pr-g2 { background: radial-gradient(80% 65% at 15% 10%, rgba(240, 240, 205, .35), transparent 58%), conic-gradient(from 230deg at 62% 58%, #8A8A30, #ABA83F 26%, #3E35C8 60%, #6C55E8 78%, #8A8A30); }
.pr-g3 { background: radial-gradient(85% 70% at 85% 15%, rgba(190, 225, 255, .4), transparent 60%), conic-gradient(from 180deg at 42% 52%, #3F7A18, #86B72F 20%, #7FB4E8 46%, #3E82D8 70%, #123B20 90%, #3F7A18); }
.pr-g4 { background: radial-gradient(80% 65% at 20% 88%, rgba(30, 30, 120, .5), transparent 60%), conic-gradient(from 215deg at 56% 44%, #8E7DE8, #C9C548 30%, #3E4BD8 64%, #9B8CF0 84%, #8E7DE8); }
.pr-g5 { background: radial-gradient(85% 70% at 85% 90%, rgba(120, 30, 60, .55), transparent 62%), conic-gradient(from 200deg at 60% 54%, #E85CC8, #FF8AD8 24%, #FF7A3C 58%, #D8447A 80%, #E85CC8); }
.pr-g6 { background: radial-gradient(80% 65% at 18% 12%, rgba(180, 245, 235, .3), transparent 58%), conic-gradient(from 220deg at 60% 50%, #1E8A8A, #4FD8C8 24%, #2E4BD8 60%, #123B6A 84%, #1E8A8A); }
.pr-g7 { background: radial-gradient(85% 70% at 80% 15%, rgba(230, 250, 160, .35), transparent 60%), conic-gradient(from 240deg at 55% 55%, #6AA82F, #CFE84F 24%, #2E8A5A 60%, #0F3A22 84%, #6AA82F); }

/* ---------- the galaxy · screens emerging from the depth ----------
   PLACEHOLDER client work, decorative and aria-hidden.

   The flow runs OUTWARD. Every screen is born just beyond the headline's
   ellipse, deep in the field: tiny, heavily blurred, out of focus. It travels
   away from the centre along its ray, growing and pulling into focus, and
   exits past the page edge at full size. So:
   - nothing ever crosses the text. spawn points sit outside its ellipse and
     motion is strictly away from it.
   - nothing pops in at the edges. the edges are where cards EXIT.
   - nothing alpha-fades in view. depth is carried by blur and scale, and the
     only opacity ramp happens in the first instants while a card is still a
     blurred speck.
   - only the floating screens are blurred. the background is never filtered.

   Perf rule that still stands: no animated transform contains var()
   indirection beyond custom-property endpoints, and blur is animated only on
   card-sized elements, which measures cheap because filter cost scales with
   blurred area and the cards are small precisely when their blur is large. */

.galaxy { display: none; }

@media (min-width: 761px) {
  html.anim .galaxy {
    display: block; position: absolute; inset: 0; z-index: 1;
    overflow: hidden; pointer-events: none;
  }

  /* the background glow at the mouth of the hole. a gradient, not a blur. */
  .gx-haze {
    position: absolute; inset: 0; z-index: 0; pointer-events: none;
    background:
      radial-gradient(38% 42% at 50% 46%, rgba(66, 52, 160, .5), rgba(38, 28, 100, .18) 56%, transparent 78%);
  }

  /* bokeh orbs. static blur, translate-only drift. measured free. */
  .gx-orb {
    position: absolute; z-index: 0; border-radius: 50%;
    pointer-events: none;
    animation: orb-drift 26s ease-in-out infinite alternate;
    will-change: translate;
  }
  .gx-orb-1 { left: 6%;  top: 12%; width: 150px; height: 150px; filter: blur(34px);
    background: radial-gradient(circle, rgba(140, 110, 255, .34), transparent 70%); }
  .gx-orb-2 { right: 9%; top: 18%; width: 100px; height: 100px; filter: blur(26px);
    background: radial-gradient(circle, rgba(126, 196, 227, .3), transparent 70%); animation-duration: 31s; animation-delay: -9s; }
  .gx-orb-3 { left: 14%; bottom: 14%; width: 190px; height: 190px; filter: blur(44px);
    background: radial-gradient(circle, rgba(96, 74, 220, .3), transparent 70%); animation-duration: 35s; animation-delay: -17s; }
  .gx-orb-4 { right: 13%; bottom: 10%; width: 130px; height: 130px; filter: blur(30px);
    background: radial-gradient(circle, rgba(255, 156, 190, .2), transparent 70%); animation-duration: 29s; animation-delay: -5s; }
  .gx-orb-5 { left: 38%; top: 6%;  width: 84px; height: 84px; filter: blur(22px);
    background: radial-gradient(circle, rgba(167, 139, 250, .3), transparent 70%); animation-duration: 24s; animation-delay: -13s; }
  .gx-orb-6 { right: 30%; bottom: 5%; width: 110px; height: 110px; filter: blur(28px);
    background: radial-gradient(circle, rgba(120, 90, 240, .26), transparent 70%); animation-duration: 33s; animation-delay: -21s; }
  .gx-orb-7 { left: 3%;  top: 52%; width: 90px; height: 90px; filter: blur(24px);
    background: radial-gradient(circle, rgba(126, 196, 227, .22), transparent 70%); animation-duration: 27s; animation-delay: -3s; }

  /* the starfield behind everything. two implied layers per tile size, so
     the repeat never reads as a grid. oversized so its slow spin never
     shows an edge. */
  .gx-stars {
    position: absolute; inset: -30%; z-index: 0; pointer-events: none;
    background-image:
      radial-gradient(1.8px 1.8px at 13% 21%, rgba(255, 255, 255, .95), transparent 100%),
      radial-gradient(1px 1px at 67% 9%, rgba(255, 255, 255, .6), transparent 100%),
      radial-gradient(1.5px 1.5px at 41% 57%, rgba(200, 210, 255, .85), transparent 100%),
      radial-gradient(.9px .9px at 86% 44%, rgba(255, 255, 255, .5), transparent 100%),
      radial-gradient(1.7px 1.7px at 24% 82%, rgba(255, 255, 255, .8), transparent 100%),
      radial-gradient(1px 1px at 74% 76%, rgba(210, 190, 255, .6), transparent 100%),
      radial-gradient(.8px .8px at 54% 33%, rgba(255, 255, 255, .45), transparent 100%),
      radial-gradient(1px 1px at 32% 8%, rgba(255, 255, 255, .5), transparent 100%);
    background-size: 460px 460px, 380px 380px, 420px 420px, 340px 340px, 500px 500px, 300px 300px, 360px 360px, 320px 320px;
    animation: star-spin 260s linear infinite, star-twinkle 7s ease-in-out infinite alternate;
  }

  /* the belt. the whole field orbits the headline slowly; each card
     counter-rotates at the same rate so its UI stays upright while it
     rides the orbit. */
  .gx-belt {
    position: absolute; inset: 0; z-index: 1;
    transform-origin: 50% 46%;
    animation: belt-spin 240s linear infinite;
  }

  /* the floating app cards are retired per David (2026-08-22). the scene is
     the aurora, orbs, and starfield only, flat behind the words. markup is
     kept in index.html for a possible revival. */
  .gx-item { display: none; }

  /* mouse parallax + viewport fit live on the INNER element, so they never
     touch the keyframe animation on the item. nearer screens move more. */
  .gx-item > .gxc {
    transform:
      translate3d(calc(var(--mx, 0px) * var(--dp, .5)), calc(var(--my, 0px) * var(--dp, .5)), 0)
      scale(var(--fit, 1));
    transform-origin: 50% 50%;
    transition: transform .6s cubic-bezier(.2, .6, .3, 1);
  }
  @media (max-width: 1360px) { html.anim .galaxy { --fit: .84; } }
  @media (max-width: 1120px) { html.anim .galaxy { --fit: .7; } }

  /* bare tags inside cards must never inherit page typography. this is the
     guard against the finale styles that once leaked 52px type into the field. */
  .gxc-in :where(h1, h2, h3, h4, h5, p, em, i, blockquote, figcaption) {
    font: inherit; color: inherit; margin: 0;
    letter-spacing: inherit; line-height: inherit;
    text-align: inherit; text-shadow: none; text-transform: none;
    max-width: none; text-wrap: initial;
  }

  html.anim .galaxy {
    opacity: 0; transform: scale(1.08);
    transition: opacity 1.2s var(--ease) .25s, transform 1.6s var(--ease) .25s;
    /* dissolve at the stage's top and bottom edges, so unpinning never
       shows a hard cut line against the black of the next section */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 10%, #000 78%, transparent 98%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 10%, #000 78%, transparent 98%);
  }
  html.anim .manifesto.done .galaxy { opacity: 1; transform: none; }
  .mani-final .aurora {
    -webkit-mask-image: linear-gradient(to bottom, transparent 2%, #000 22%, #000 68%, transparent 92%);
    mask-image: linear-gradient(to bottom, transparent 2%, #000 22%, #000 68%, transparent 92%);
  }

  .mani-final-inner { position: relative; z-index: 4; }
}

/* each card holds a fixed station along its ray and breathes back and forth
   on it. alternate direction means no loop seam, so nothing is ever born or
   dies in view. stations mix the old spawn and exit points, biased inward
   so the field reads at full size. */
@keyframes gx-drift {
  from { translate: calc(var(--sx, 20vw) * .7 + var(--ox, 64vw) * .3)
                    calc(var(--sy, 0vh) * .7 + var(--oy, 0vh) * .3);
         scale: calc(var(--s1, .62) * 1.1); }
  to   { translate: calc(var(--sx, 20vw) * .48 + var(--ox, 64vw) * .52)
                    calc(var(--sy, 0vh) * .48 + var(--oy, 0vh) * .52);
         scale: calc(var(--s1, .62) * 1.24); }
}

@keyframes orb-drift {
  from { translate: -26px 14px; }
  to   { translate: 30px -18px; }
}

@keyframes belt-spin {
  from { rotate: 0deg; }
  to   { rotate: 360deg; }
}
/* cancels the belt's spin so every screen stays upright, keeping each
   card's own static tilt */
@keyframes gx-counter {
  from { rotate: var(--r, 0deg); }
  to   { rotate: calc(var(--r, 0deg) - 360deg); }
}
@keyframes star-spin {
  from { rotate: 0deg; }
  to   { rotate: 360deg; }
}
@keyframes star-twinkle {
  from { opacity: .38; }
  to   { opacity: .68; }
}

@media (prefers-reduced-motion: reduce) {
  .gx-item, .gx-orb, .gx-belt, .gx-stars, .fa-grad span, .pr-art { animation: none; }
  .gx-item { opacity: 0; }
  .gx-item > .gxc { transition: none; }
  html.anim .galaxy { transition: none; }
}

/* ---------- the card shell. the page owns the frame, designers own the interior ---------- */

.gxc {
  position: relative;
  background: #0B0C11;
  border: 1px solid rgba(255, 255, 255, .13);
  border-radius: 11px;
  box-shadow:
    0 24px 54px -22px rgba(0, 0, 0, .85),
    0 2px 8px -2px rgba(0, 0, 0, .5),
    inset 0 1px 0 rgba(255, 255, 255, .08);
  overflow: hidden;
  margin: 0;
  flex: none;
}
/* five screen formats */
.gxc--desk   { width: 340px; height: 214px; }
.gxc--app    { width: 320px; height: 240px; }
.gxc--tall   { width: 250px; height: 320px; }
.gxc--square { width: 240px; height: 240px; }
.gxc--phone  { width: 128px; height: 268px; border-radius: 17px; }

.gxc-in {
  width: 100%; height: 100%;
  font-size: 7px; line-height: 1.4;
  color: rgba(255, 255, 255, .62);
  overflow: hidden;
}
.gxc-in * { box-sizing: border-box; }


@media (prefers-reduced-motion: reduce) {
  html.anim .galaxy { transition: none; }
}
