/* ===========================================================================
   TRNCH landing — "Orbit"

   THE DESIGN, stated as what it is rather than where the ruler was put:

     ground       near-black rgb(6,5,16), so a photographic plate can sit
                  behind the copy without a seam
     cards        rgb(18,17,26), 25px radius, a single 1px hairline at 10%
     headings     lowercase, weight 500, tight tracking (-0.05em), line-height
                  ~1.0 — the wordmark and the section heads read as one voice
     primary btn  the accent at ~50% alpha, 12px radius, weight 700 at 18px;
                  exactly one of them is ever in view
     type scale   one big heading size (60px at 1440) doing most of the work,
                  with the hero tagline a deliberate step down at 40px

   The accent is the app's own --color-signal (#6636ea, "Ultraviolet"), so the
   landing and the product are the same purple. Type is Satoshi; the app mock
   inside the demo window uses the product's real pairing, Plus Jakarta Sans +
   DM Mono, so the window reads as the actual thing rather than a lookalike.
   =========================================================================== */

/* Fonts are loaded from <link> tags in the document head, NOT with @import.
   An @import here cannot start until this stylesheet has been fetched and
   parsed, which buries the font request one level deeper in the waterfall:
       HTML -> styles.css -> font css -> font files
   The <link> version discovers the font CSS while the HTML is still parsing,
   in parallel with this file, and preconnect warms the TLS handshake first. */

:root {
  --bg:        #060510;
  --card:      #12111A;
  --card-2:    #16151F;
  --line:      rgba(203, 208, 235, 0.10);
  --line-2:    rgba(203, 208, 235, 0.18);

  --fg:        #F7F7F7;
  --head:      #EAEDFF;
  --dim:       #9A9EB8;
  /* lifted from #6E7189 on 2026-09-20: that failed WCAG AA on all six places it
     is used (3.67-4.23 against the three backgrounds it lands on, needs 4.5).
     #8085A0 clears 4.83 on the lightest of them. */
  --dimmer:    #8085A0;

  /* our signal violet, lifted where it has to survive a dark ground */
  --violet:      #6636EA;
  --violet-lift: #9D7BFF;
  --violet-btn:  rgba(102, 54, 234, 0.50);
  --violet-glow: rgba(102, 54, 234, 0.35);

  --r-btn:   12px;
  --r-card:  25px;
  --r-win:   14px;

  --maxw: 1200px;
  --ease: cubic-bezier(0.23, 1, 0.32, 1);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: 'Satoshi', ui-sans-serif, system-ui, -apple-system, sans-serif;
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; }

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 24px;
}

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

h1, h2, h3 {
  font-weight: 500;
  letter-spacing: -0.05em;
  line-height: 1.0;
  color: var(--head);
  margin: 0;
  text-transform: lowercase;
}

/* One big size does most of the work at 1440, which is what keeps the page
   both section heads and the closing — 60px / 60px lh / -3px tracking — and
   36px / 40px lh / -0.9px for card headings. Mine was 66 / 56 / 32 with
   tighter tracking throughout, which is what made it read denser than theirs. */
.h-xl  { font-size: clamp(34px, 4.17vw, 60px); letter-spacing: -0.05em; }
/* the hero tagline is a deliberate step down from the section heads */
/* 40px / 48px line-height at 1440.
   Mine was computing 47.5px at lh 1.0, which read 19% oversized and tighter. */
.hero-tag { font-size: clamp(26px, 2.78vw, 40px); letter-spacing: -0.05em; line-height: 1.2; }
.h-lg  { font-size: clamp(30px, 4.17vw, 60px); }
.h-md  { font-size: clamp(22px, 2.5vw, 36px); line-height: 1.111; letter-spacing: -0.025em; }

.sub {
  color: var(--dim);
  font-size: clamp(15px, 1.35vw, 19px);
  line-height: 1.5;
  letter-spacing: -0.01em;
  margin: 18px 0 0;
  font-weight: 400;
}

.eyebrow {
  color: var(--violet-lift);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin: 0 0 12px;
}

/* ---- buttons ------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 54px;
  padding: 0 30px;
  border-radius: var(--r-btn);
  border: 1px solid var(--line);
  font-family: inherit;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--fg);
  text-decoration: none;
  cursor: pointer;
  transition: transform .22s var(--ease), background .22s var(--ease), box-shadow .22s var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn-primary {
  background: var(--violet-btn);
  box-shadow: 0 8px 34px -10px var(--violet-glow);
}
.btn-primary:hover {
  background: rgba(102, 54, 234, 0.82);
  box-shadow: 0 14px 44px -10px var(--violet-glow);
}

.btn-ghost { background: rgba(255, 255, 255, 0.12); }
.btn-ghost:hover { background: rgba(255, 255, 255, 0.19); }

.btn-sm {
  height: 40px;
  padding: 0 20px;
  font-size: 16px;
  border-radius: 8px;
  border: 0;
  background: var(--card);
}
.btn-sm:hover { background: #1c1b28; }

/* ---- nav ----------------------------------------------------------------- */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  padding: 18px 0;
  transition: background .3s var(--ease), backdrop-filter .3s var(--ease), border-color .3s var(--ease);
  border-bottom: 1px solid transparent;
}
.nav.stuck {
  background: rgba(6, 5, 16, 0.72);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom-color: var(--line);
}
.nav-in { display: flex; align-items: center; justify-content: space-between; }

.mark {
  font-size: 26px;
  font-weight: 900;
  letter-spacing: -0.06em;
  color: var(--fg);
  text-decoration: none;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  gap: 9px;
}
.mark .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--violet-lift);
  box-shadow: 0 0 14px var(--violet-lift);
}
.nav-right { display: flex; align-items: center; gap: 10px; }
.nav-link {
  color: rgba(255, 255, 255, 0.84);
  text-decoration: none;
  font-size: 15px;
  font-weight: 500;
  padding: 0 14px;
  transition: color .2s;
  /* the hero art is bright on the right, where these sit */
  text-shadow: 0 1px 10px rgba(6, 5, 16, 0.75);
}
.nav.stuck .nav-link { color: var(--dim); text-shadow: none; }
.nav-link:hover { color: var(--fg); }
.nav .mark { text-shadow: 0 1px 12px rgba(6, 5, 16, 0.6); }
.nav .btn-sm { box-shadow: 0 2px 18px rgba(6, 5, 16, 0.55); }

/* ---- hero ---------------------------------------------------------------- */

.hero {
  position: relative;
  /* 335 = the 96 that was always here + the 239px the centred car occupied in
     normal flow. The car is gone; the SPACE IS KEPT on purpose, so the plate
     crops where it always did, the Earth's limb lands where it always did,
     and replacement hero art drops into the same slot with no re-tuning.
     Chosen by looking at the limb against the copy at 1440, not by arithmetic
     alone — see README, "Hero art". */
  padding: 112px 0 335px;
  text-align: center;
  overflow: hidden;
}
.hero-bg {
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: 100%;
  min-width: 1500px;
  /* COVER, not width-scaled. The plate this replaced was 1.707:1; ours is
     1.792:1, so at the same 1500px floor it renders 837px tall against a
     934px hero and stops 97px short of the bottom. `cover` makes coverage
     independent of both the plate's aspect and the hero's height — which
     matters because the hero's height is now a deliberately reserved slot
     that will change when new art goes in. */
  height: 100%;
  object-fit: cover;
  object-position: 62% 22%;
  z-index: -10;
  pointer-events: none;
  -webkit-user-select: none;   /* safari < 17 still wants the prefix */
  user-select: none;
}
/* the plate drifts and breathes very slowly, so the sky is never quite still */
.hero-bg {
  --py: 0px;                      /* scroll parallax, set by stars.js */
  animation: drift 64s ease-in-out infinite alternate;
  transform-origin: 72% 18%;      /* the earth, so the limb stays put */
  will-change: transform;
}
@keyframes drift {
  0%   { transform: translateX(-50%) translateY(var(--py, 0px)) scale(1); }
  100% { transform: translateX(-50%) translateY(var(--py, 0px)) scale(1.055); }
}

/* an extra layer of stars over the void, drifting and twinkling. Masked away
   from the earth in the upper right so it never speckles the bright limb. */
.hero-stars {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  z-index: -8;
  pointer-events: none;
  -webkit-mask-image: radial-gradient(ellipse 95% 78% at 78% 6%,
            transparent 0%, transparent 34%, #000 74%);
          mask-image: radial-gradient(ellipse 95% 78% at 78% 6%,
            transparent 0%, transparent 34%, #000 74%);
}

/* A soft, very diffuse pool of dark behind the headline. A plate whose void is
   already black through the middle so this is invisible on it; on a plate
   where the limb runs closer to centre it is what keeps the copy readable.
   Sits under the text, over the sky. */
.hero-scrim {
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: min(1180px, 96%);
  height: 560px;
  z-index: -7;
  pointer-events: none;
  background: radial-gradient(ellipse 58% 62% at 50% 40%,
              rgba(6, 5, 16, 0.80) 0%,
              rgba(6, 5, 16, 0.55) 42%,
              rgba(6, 5, 16, 0.22) 68%,
              rgba(6, 5, 16, 0) 100%);
  filter: blur(6px);
}

/* the image stops; keep the void black underneath it rather than a seam */
.hero-fade {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 340px;
  z-index: -6;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(6,5,16,0) 0%, rgba(6,5,16,.75) 55%, var(--bg) 100%);
}

.wordmark {
  font-size: clamp(74px, 12.8vw, 184px);
  font-weight: 700;
  letter-spacing: -0.07em;
  line-height: 0.9;
  color: #D9DBFF;
  margin: 0;
  text-transform: lowercase;
  text-shadow: 0 0 90px rgba(157, 123, 255, 0.35);
}

.hero h1 { margin-top: 14px; }
.hero .sub {
  max-width: 720px;
  margin: 14px auto 0;
  /* grey works over pure black; over a limb that
     runs nearer centre it needs lifting */
  color: rgba(255, 255, 255, 0.80);
  text-shadow: 0 1px 14px rgba(6, 5, 16, 0.85);
}
.wordmark, .hero-tag { text-shadow: 0 2px 40px rgba(6, 5, 16, 0.55); }
.wordmark { text-shadow: 0 0 90px rgba(157, 123, 255, 0.35), 0 2px 40px rgba(6, 5, 16, 0.5); }

.cta-row {
  display: flex;
  gap: 14px;
  justify-content: center;
  margin-top: 28px;
  flex-wrap: wrap;
}

/* The copy sits above the plate and the scrim. Nothing else floats over the
   hero any more: the astronaut, the Shopify plaque and both cars were removed
   on 2026-09-21 (see README, "Hero art: what was here and why it went"). The
   space they occupied is deliberately KEPT — see `.hero`'s padding-bottom —
   so replacement art drops into the same slot without re-tuning the section. */
.hero .wrap { position: relative; z-index: 2; }

}

/* ---- generic section ----------------------------------------------------- */

.section { padding: clamp(76px, 10vw, 130px) 0; position: relative; }
.section-head { text-align: center; max-width: 760px; margin: 0 auto clamp(40px, 5vw, 62px); }
.section-head .sub { margin-left: auto; margin-right: auto; }

/* ---- the demo window ----------------------------------------------------- */

/* The window breaks OUT of .wrap. At the wrap's own 1120px it was narrower
   than the hero car above it, and the feed's ten columns had to be squeezed
   to fit — the whole point of the shot is that you can read them. */
/* The window breaks OUT of .wrap. At the wrap's own 1120px it was narrower
   than the hero car above it, and the feed's ten columns had to be squeezed
   to fit — the whole point of the shot is that you can read them.

   The break-out is done with MARGIN, not `translateX(-50%)`: .demo-stage also
   carries .reveal, whose scroll animation owns `transform`, so a transform
   here is overwritten the moment the section reveals and the window lands
   half a screen to the right. */
.demo-stage {
  position: relative;
  width: min(1440px, 94vw);   /* 1440 is the canvas's own width — never upscaled */
  margin-left: calc(50% - min(720px, 47vw));
}
/* Below 1000 the window hands over to the square canvas (demo.js, COMPACT_BELOW).
   The handover is set HERE as well as there and the two must agree: a stage of
   940px is the narrowest the 1440 canvas stays readable at (scale 0.65, so its
   14px type lands at 9px), and 94vw reaches 940 at a 1000px viewport. */
@media (max-width: 1000px) {
  .demo-stage { width: min(680px, 94vw); margin-left: calc(50% - min(340px, 47vw)); }
}
/* violet bloom behind the glass, so the window reads as lit rather than pasted on */
.demo-stage::before {
  content: '';
  position: absolute;
  inset: 6% 2% 10%;
  background: radial-gradient(ellipse at 50% 45%, rgba(102,54,234,.42), rgba(102,54,234,0) 68%);
  filter: blur(70px);
  z-index: 0;
  pointer-events: none;
}

.win {
  position: relative;
  z-index: 1;
  border-radius: var(--r-win);
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--line-2);
  box-shadow: 0 60px 120px -40px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(255,255,255,.02);
}

.win-bar {
  height: 38px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px;
  background: #0E0D16;
  border-bottom: 1px solid var(--line);
}
.win-dots { display: flex; gap: 7px; }
.win-dots i { width: 11px; height: 11px; border-radius: 50%; background: #2A2937; display: block; }
.win-url {
  margin: 0 auto;
  font-size: 12px;
  color: var(--dimmer);
  background: #191823;
  border-radius: 6px;
  padding: 4px 16px;
  letter-spacing: -0.01em;
  font-weight: 500;
}
.win-live {
  font-size: 9.5px;      /* the label got longer when it stopped overclaiming */
  white-space: nowrap;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: #59D39B;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.win-live i {
  width: 6px; height: 6px; border-radius: 50%; background: #59D39B;
  animation: pulse 1.8s ease-in-out infinite;
}
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.25} }

.win-body {
  position: relative;
  overflow: hidden;
  background: #0d0727;   /* the app runs dark */
}

/* The app is authored at a fixed canvas and scaled to fit the window, so the
   cursor coordinates in demo.js are one stable coordinate space. There are
   TWO canvases — 1440x900 and a 640x640 square for phones — and demo.js swaps
   between them, so the box lives in app-mock.css rather than here. */
.app {
  font-family: 'Plus Jakarta Sans', ui-sans-serif, system-ui, sans-serif;
}

/* ---- bento --------------------------------------------------------------- */

.bento {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
.tile {
  position: relative;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 28px 28px 0;
  overflow: hidden;
  min-height: 330px;
  display: flex;
  flex-direction: column;
  transition: border-color .3s var(--ease), transform .3s var(--ease);
}
.tile:hover { border-color: var(--line-2); transform: translateY(-3px); }
.tile h3 { max-width: 15ch; }
.tile-art {
  margin-top: auto;
  padding-top: 22px;
  transition: transform .35s var(--ease);
}
.tile:hover .tile-art { transform: scale(1.04); }

/* ---- stages (research live / two veiled) --------------------------------- */

.stages { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }

.stage {
  position: relative;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
  min-height: 400px;
  display: flex;
  flex-direction: column;
}
.stage-head { padding: 26px 26px 0; position: relative; z-index: 3; }
.stage-art {
  margin-top: auto;
  padding: 24px 0 0 26px;
  position: relative;
  z-index: 1;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 11px;
  border-radius: 999px;
  margin-bottom: 14px;
}
.pill-live { color: #6FE3AC; background: rgba(89, 211, 155, 0.12); border: 1px solid rgba(89,211,155,.22); }
.pill-live i { width: 6px; height: 6px; border-radius: 50%; background: #6FE3AC; animation: pulse 1.8s ease-in-out infinite; }
.pill-soon { color: #83879E; background: rgba(203, 208, 235, 0.07); border: 1px solid var(--line); }

/* the veil: the fake UI is finished-looking, then covered */
.veil {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: flex-end;          /* sit the badge over the art, not the copy */
  justify-content: center;
  padding-bottom: 78px;
  /* opaque enough to read as "not revealed yet", sheer enough that a finished
     UI is plainly sitting underneath it */
  background: linear-gradient(to bottom, rgba(10, 9, 20, 0.34) 0%, rgba(10, 9, 20, 0.62) 52%, rgba(10, 9, 20, 0.80) 100%);
  backdrop-filter: blur(2.5px) saturate(0.55);
  -webkit-backdrop-filter: blur(2.5px) saturate(0.55);
}
.veil span {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #7E8299;
  padding: 11px 22px;
  border: 1px solid rgba(203, 208, 235, 0.13);
  border-radius: 999px;
  background: rgba(18, 17, 26, 0.6);
}
.stage.veiled .stage-head { z-index: 5; }
.stage.veiled .stage-head h3,
.stage.veiled .stage-head .eyebrow { opacity: .7; }

/* ---- closing ------------------------------------------------------------- */

.closing {
  position: relative;
  text-align: center;
  padding: clamp(110px, 14vw, 190px) 0;
  overflow: hidden;
}
.orbit {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: min(720px, 90vw);
  aspect-ratio: 1;
  z-index: 0;
  pointer-events: none;
}
.orbit .ring {
  position: absolute;
  inset: 0;
  border: 1px solid rgba(203, 208, 235, 0.1);
  border-radius: 50%;
  animation: spin 46s linear infinite;
}
.orbit .ring.r2 { inset: 17%; animation-duration: 34s; animation-direction: reverse; }
.orbit .ring.r3 { inset: 34%; animation-duration: 26s; }
.orbit .ring i {
  position: absolute;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--violet-lift);
  box-shadow: 0 0 16px var(--violet-lift);
}
@keyframes spin { to { transform: rotate(360deg); } }

.closing-glow {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 900px; height: 520px;
  background: radial-gradient(ellipse, rgba(102,54,234,.30), rgba(102,54,234,0) 68%);
  filter: blur(60px);
  z-index: 0;
  pointer-events: none;
}
.closing .wrap { position: relative; z-index: 2; }

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

.footer { border-top: 1px solid var(--line); padding: 56px 0 44px; }
.footer-in { display: flex; justify-content: space-between; gap: 48px; flex-wrap: wrap; }
.footer-mark .mark { font-size: 34px; }
.footer-mark p { color: var(--dim); font-size: 15px; margin: 12px 0 0; letter-spacing: -0.01em; }
.footer-mark small { color: var(--dimmer); font-size: 13px; display: block; margin-top: 18px; }
.cols { display: flex; gap: 68px; flex-wrap: wrap; }
.col h4 {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--dimmer);
  text-transform: uppercase;
  margin: 0 0 16px;
}
.col a {
  display: block;
  color: var(--dim);
  text-decoration: none;
  font-size: 14px;
  margin-bottom: 11px;
  transition: color .2s;
}
.col a:hover { color: var(--fg); }

/* ---- reveal -------------------------------------------------------------- */

/* only hide these when scripting is available to bring them back */
.js .reveal { opacity: 0; transform: translateY(26px); transition: opacity .8s var(--ease), transform .8s var(--ease); }
.js .reveal.in { opacity: 1; transform: none; }

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

@media (max-width: 960px) {
  .bento, .stages { grid-template-columns: 1fr; }
  .tile { min-height: 280px; }
  .stage { min-height: 320px; }
  .hero { padding-top: 120px; }
}

@media (max-width: 720px) {
  /* the hero fades into the page background down here, so the reserved space
     under the copy reads as more sky rather than as a gap */
  .hero { padding-bottom: 120px; }
}
@media (max-width: 640px) {
  .wrap { padding: 0 16px; }
  .btn { height: 48px; font-size: 16px; padding: 0 22px; }
  .cta-row .btn { flex: 1 1 100%; }
  .cols { gap: 36px; }
  /* The whole nav goes on a phone: the page is a
     waitlist, the email field is the first thing under the wordmark, and
     Product / Roadmap are both reachable from the footer. The Early access
     button stays because it is the one link with intent. */
  .nav-link { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .orbit .ring, .win-live i, .pill-live i, .hero-bg { animation: none !important; }
  .hero-stars { display: none; }
  .js .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ---- tiles whose art is a real screenshot --------------------------------
   Card imagery runs to the card edge and scales on hover;
   these two tiles hold actual crops of the app (dark theme, so they sit on
   the dark card correctly) and follow the same treatment. The top edge is
   feathered so the screenshot does not hard-cut against the card. */

.tile-art.shot {
  margin: auto -28px 0;                 /* cancel the card's side padding */
  padding-top: 24px;
  position: relative;
  overflow: hidden;
}
.tile-art.shot img {
  width: 100%;
  display: block;
  border-top: 1px solid rgba(203, 208, 235, 0.08);
  transition: transform .45s var(--ease);
  transform-origin: center top;
}
.tile:hover .tile-art.shot img { transform: scale(1.045); }

/* a short fade from the card colour into the screenshot */
.tile-art.shot::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: 24px;
  height: 46px;
  pointer-events: none;
  background: linear-gradient(to bottom, var(--card) 0%, rgba(18, 17, 26, 0) 100%);
}

/* ---- focus -------------------------------------------------------------
   Keyboard focus worked, but it was the browser's default gold ring, which
   is both off-palette and nearly invisible against the plate. This is the
   same ring in our own violet, only for :focus-visible so it never shows on
   a mouse click. */

:focus-visible {
  outline: 2px solid var(--violet-lift);
  outline-offset: 3px;
  border-radius: 6px;
}
.btn:focus-visible { outline-offset: 4px; }
.mark:focus-visible { border-radius: 8px; }

/* ---- anchor offset ------------------------------------------------------
   The nav is fixed and ~77px tall, so an in-page anchor was parking its own
   heading exactly underneath it — every nav link landed on a hidden title.
   scroll-margin-top pushes the landing point clear of the bar. */

#product, #stages, #start { scroll-margin-top: 104px; }
#top { scroll-margin-top: 0; }

@media (max-width: 640px) {
  #product, #stages, #start { scroll-margin-top: 88px; }
}

/* ---- screen-reader only -------------------------------------------------
   Carries the description of the demo window, whose markup is aria-hidden
   because it is a scripted decoration rather than content. Uses the clip-path
   technique rather than display:none so it is still announced. */

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- the three-stage arc, in the first screen ---------------------------
   The product is pitched as all-in-one, but two of the three stages are not
   built. Naming all three with the unbuilt ones marked keeps the promise and
   the honesty in the same glance — and matches the "three stages, one of them
   is live" section further down rather than contradicting it. */

.hero-arc {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 10px;
  margin: 0 0 26px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.hero-arc > a,
.hero-arc > span {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(18, 17, 26, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--dim);
}
.hero-arc > a.on,
.hero-arc > span.on {
  color: #D7CCFF;
  border-color: rgba(157, 123, 255, 0.4);
  background: rgba(102, 54, 234, 0.22);
}
.hero-arc > a.on::before,
.hero-arc > span.on::before {
  content: '';
  width: 6px; height: 6px; border-radius: 50%;
  background: #9D7BFF;
  box-shadow: 0 0 10px #9D7BFF;
}
.hero-arc i {
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: 0.08em;
  color: var(--dimmer);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 6px;
}

@media (max-width: 640px) {
  /* measured at 390: the three chips came to ~362px against 358px available,
     so it wrapped 2+1 and looked lopsided. Tightened just enough for one row. */
  .hero-arc { font-size: 9.5px; gap: 5px; margin-bottom: 18px; letter-spacing: 0.06em; }
}

/* 320 (iPhone SE): the three chips wrap 2+1 here and that is left alone on
   purpose. Dropping the font to 8px was tried and REVERTED — it still wrapped
   (the labels are simply longer than the 288px available) and only cost
   legibility. Nothing overflows and nothing clips, so the wrap is the
   least-bad outcome at this width. */

@media (max-width: 640px) {
  .hero-arc > a, .hero-arc > span { padding: 5px 8px; gap: 5px; }
  .hero-arc > a.on::before,
.hero-arc > span.on::before { width: 5px; height: 5px; }
  .hero-arc i { display: none; }
  .hero-arc > a:not(.on), .hero-arc > span:not(.on) { opacity: .75; }
}

/* ---- the stage rail -----------------------------------------------------
   The three stages were three equal cards, sitting between the bento's card
   grid and the stage grid — the same shape twice over. A rail turns
   them into one journey with a position on it.

   It uses the SAME grid as .stages (3 x 1fr, 16px gap) so the nodes land on
   the card centres structurally. A hand-computed width was off by up to 13px
   because it ignored the gap. */

.stage-rail {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin: 0 0 22px;
}
.stage-rail .seg {
  position: relative;
  height: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.stage-rail .seg i {
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid rgba(203, 208, 235, 0.22);
  background: var(--bg);
  position: relative;
  z-index: 1;
}
.stage-rail .seg.on i {
  border-color: var(--violet-lift);
  background: var(--violet-lift);
  box-shadow: 0 0 0 5px rgba(157, 123, 255, 0.14), 0 0 16px rgba(157, 123, 255, 0.6);
}
/* centre of this cell to centre of the next = one cell width + one gap */
.stage-rail .seg:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 50%;
  width: calc(100% + 16px);
  height: 2px;
  border-radius: 2px;
  background: rgba(203, 208, 235, 0.14);
}
.stage-rail .seg.on::after {
  background: linear-gradient(to right, var(--violet-lift), rgba(203, 208, 235, 0.14));
}

@media (max-width: 960px) {
  .stage-rail { display: none; }   /* the cards stack; a rail across them is meaningless */
}


/* ---- the waitlist form ---------------------------------------------------
   The same component runs at the top of the hero and at the foot of the page.
   There is no pricing section any more — the product is pre-launch, so the
   only thing to ask for is an email, and the launch discount is the reason to
   give it. Both instances submit to the same handler; the hero one is the
   wider of the two because it carries the hero's weight on its own now that
   the tagline is a single line. */

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.wait { margin: 30px auto 0; max-width: 480px; }
.wait-hero { max-width: 510px; }

.wait-row {
  display: flex;
  gap: 10px;
  /* the input and the button read as ONE control, so the gap is small and the
     radii match; at 520px they stack and the button goes full width */
  align-items: stretch;
}

.wait-in {
  flex: 1 1 auto;
  min-width: 0;
  height: 52px;
  padding: 0 18px;
  font: 400 16px/1 'Satoshi', system-ui, sans-serif;
  color: #EAEDFF;
  background: rgba(14, 13, 24, 0.72);
  border: 1px solid rgba(157, 123, 255, 0.28);
  border-radius: 12px;
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
.wait-in::placeholder { color: rgba(234, 237, 255, 0.36); }
.wait-in:hover { border-color: rgba(157, 123, 255, 0.45); }
.wait-in:focus {
  outline: none;
  background: rgba(14, 13, 24, 0.9);
  border-color: #6636EA;
  box-shadow: 0 0 0 3px rgba(102, 54, 234, 0.28);
}
/* only after a submit attempt, so it does not shout at someone mid-typing */
.wait.tried .wait-in:invalid { border-color: #E2566B; box-shadow: 0 0 0 3px rgba(226, 86, 107, 0.2); }

.wait-btn { height: 52px; flex: 0 0 auto; white-space: nowrap; }
.wait-btn[disabled] { opacity: .6; pointer-events: none; }

.wait-offer {
  margin: 14px 0 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: rgba(234, 237, 255, 0.6);
  text-shadow: 0 1px 14px rgba(6, 5, 16, 0.85);
}
.wait-offer b { color: #C9B6FF; font-weight: 700; }

.wait-msg { margin: 10px 0 0; font-size: 14px; min-height: 20px; }
.wait-msg.ok  { color: #5FCB9B; }
.wait-msg.err { color: #E2566B; }

.hero-more { margin: 16px 0 0; font-size: 14px; }
.hero-more a {
  color: rgba(234, 237, 255, 0.62);
  text-decoration: none;
  border-bottom: 1px solid rgba(234, 237, 255, 0.22);
  padding-bottom: 2px;
  transition: color .18s ease, border-color .18s ease;
}
.hero-more a:hover { color: #EAEDFF; border-color: rgba(234, 237, 255, 0.5); }

@media (max-width: 520px) {
  .wait-row { flex-wrap: wrap; }
  .wait-in, .wait-btn { flex: 1 1 100%; }
}

/* ---- the copy chooser ---------------------------------------------------
   A REVIEW TOOL, not part of the design. It replaced the old ?v=1..5 wording
   switch, which changed the whole page at once; these are per-section and mix
   and match. It shows on localhost, or anywhere with `?copy` on the URL so a
   combination can be shared, and it writes only to localStorage — the shipped
   page is always DEFAULTS in copy.js. Collapsed to a tab until asked for, so
   it never sits over the closing form. */
.chooser {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 60;
  width: min(430px, calc(100vw - 32px));
  font: 500 12px/1.4 'Satoshi', system-ui, sans-serif;
  color: #EAEDFF;
  text-align: right;
}
.ch-tab {
  height: 34px;
  padding: 0 16px;
  border: 1px solid rgba(157, 123, 255, .3);
  border-radius: 999px;
  background: rgba(14, 13, 24, .9);
  color: rgba(234, 237, 255, .72);
  font: 700 11px/1 'Satoshi', system-ui, sans-serif;
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  box-shadow: 0 10px 40px rgba(0, 0, 0, .5);
}
.ch-tab:hover { color: #fff; border-color: rgba(157, 123, 255, .6); }
.ch-body {
  display: none;
  margin-top: 10px;
  padding: 16px;
  text-align: left;
  border: 1px solid rgba(157, 123, 255, .26);
  border-radius: 16px;
  background: rgba(14, 13, 24, .96);
  -webkit-backdrop-filter: blur(22px);
  backdrop-filter: blur(22px);
  box-shadow: 0 24px 70px rgba(0, 0, 0, .7);
  max-height: min(70vh, 560px);
  overflow-y: auto;
}
.chooser.open .ch-body { display: block; }
.ch-row { display: flex; align-items: flex-start; gap: 10px; padding: 7px 0; }
.ch-label { flex: 0 0 108px; padding-top: 6px; font-size: 11px; color: rgba(234, 237, 255, .52); }
.ch-opts { display: flex; flex-wrap: wrap; gap: 5px; }
.ch-opt {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid rgba(234, 237, 255, .14);
  border-radius: 9px;
  background: transparent;
  color: rgba(234, 237, 255, .66);
  font: 700 11px/1 'Satoshi', system-ui, sans-serif;
  cursor: pointer;
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.ch-opt em { font-style: normal; font-weight: 500; font-size: 11px; opacity: .72; }
.ch-opt:hover { border-color: rgba(157, 123, 255, .5); color: #fff; }
.ch-opt.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.ch-opt.on em { opacity: .85; }
.ch-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid rgba(234, 237, 255, .12);
  font-size: 11px;
  color: rgba(234, 237, 255, .5);
}
.ch-foot code {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 12px;
  color: #C9B6FF;
  background: rgba(157, 123, 255, .12);
  padding: 3px 8px;
  border-radius: 6px;
}
.ch-reset {
  margin-left: auto;
  border: 0;
  background: none;
  color: rgba(234, 237, 255, .5);
  font: 500 11px 'Satoshi', system-ui, sans-serif;
  text-decoration: underline;
  cursor: pointer;
}
.ch-reset:hover { color: #fff; }
.ch-note { margin: 10px 0 0; font-size: 11px; line-height: 1.5; color: rgba(234, 237, 255, .38); }

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

/* ---- the three-tool rail under the demo window ---------------------------
   The recording walks through research -> store -> ads, but a viewer who
   joins half way through has no idea they are watching three tools rather
   than three screens of one. This rail is on the whole time and lights the
   stage that is playing, so the point lands without anyone reading the copy. */
.win-stages {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  background: #0E0D16;
  border-top: 1px solid var(--line);
}
.ws {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 8px 12px;
  border-radius: 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--dimmer);
  transition: background .35s var(--ease), color .35s var(--ease);
}
.ws i {
  width: 20px; height: 20px; flex: 0 0 20px;
  border-radius: 50%;
  background: #23222F;
  /* 4.97:1 on #23222F. --dimmer landed at 4.31 — an 11px numeral is body
     text for WCAG, not a large or decorative label. */
  color: #8C90A8;
  display: flex; align-items: center; justify-content: center;
  font-style: normal; font-size: 11px; font-weight: 800;
  transition: background .35s var(--ease), color .35s var(--ease);
}
.ws em {
  font-style: normal;
  font-size: 12px;
  font-weight: 400;
  /* 4.5:1 on #0E0D16 (measured 5.45). #4C4A5C was 2.24 and failed WCAG AA at
     this size — it is 12px body text, not a large or decorative label. */
  color: #83879E;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ws.on { background: rgba(102, 54, 234, .16); color: #EDEAFF; }
.ws.on i { background: var(--accent); color: #fff; }
.ws.on em { color: #9D93C6; }
.ws.done i { background: #3E9E7A; color: #fff; }
.ws-arw {
  flex: 0 0 14px; height: 8px;
  border-right: 1.6px solid #2E2C3D;
  border-top: 1.6px solid #2E2C3D;
  transform: rotate(45deg);
}
@media (max-width: 720px) {
  .win-stages { gap: 4px; padding: 9px 8px; }
  .ws { padding: 6px 7px; font-size: 11px; gap: 7px; }
  .ws i { width: 17px; height: 17px; flex-basis: 17px; font-size: 10px; }
  .ws em { display: none; }
  .ws-arw { flex-basis: 8px; }
}

/* The arc chips are ANCHORS now — they jump to the window and restart the
   recording at that stage — so every rule above that keyed off `span` had to
   take `a` as well, or they lose the pill entirely and render as blue
   underlined links. */
.hero-arc a.arc-go { text-decoration: none; cursor: pointer; transition: border-color .2s var(--ease), color .2s var(--ease), background .2s var(--ease); }
.hero-arc a.arc-go:hover { border-color: rgba(157,123,255,.55); color: #EDEAFF; background: rgba(102,54,234,.18); }
.hero-arc a.arc-go:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Tiles 5 and 6 carry a second piece of art so a copy set can turn them into
   the store-builder and ad-manager tiles. Renaming a tile without changing
   its art would put a heading about storefronts over a chart of return rates. */
.tile-art.alt { display: none; }
.tile.use-alt .tile-art:not(.alt) { display: none; }
.tile.use-alt .tile-art.alt { display: block; }

/* ---- the demo caption and the consent line ------------------------------
   Both exist because the page was telling sighted visitors something its own
   source said was untrue. The demo window carried "REAL DATA" over invented
   figures, and a live email field sat above three dead legal links. */
.demo-note {
  margin: 18px auto 0;
  max-width: 640px;
  text-align: center;
  font-size: 13px;
  line-height: 1.55;
  color: var(--dimmer);
}

.wait-consent {
  margin: 12px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: rgba(234, 237, 255, 0.5);
  text-shadow: 0 1px 14px rgba(6, 5, 16, 0.85);
}
.wait-consent a { color: rgba(234, 237, 255, 0.72); }

/* Not yet a link, and it must not look like one. Used where a destination is
   drafted but has no public URL — see the comment in the footer. */
.soon { color: rgba(234, 237, 255, 0.34); }
.footer .soon { display: block; font-size: 14px; line-height: 1; padding: 2px 0; }

@media (max-width: 720px) {
  /* The window bar cannot hold the URL and the label at 390 — the label was
     clipping to "RECORDED DEMO · SAMPLE DA". The caption directly under the
     window carries the same disclosure in full, so the badge goes rather than
     truncating the one thing on the page that must not be half-read. */
  .win-live { display: none; }
}

/* A waitlist that cannot name its controller is closed, and looks closed.
   See site-config.js — this clears the moment ENTITY and CONTACT_EMAIL exist. */
.wait-blocked .wait-in,
.wait-blocked .wait-btn { opacity: .45; cursor: not-allowed; }
.wait-blocked .wait-offer { opacity: .5; }

/* ---- the waitlist: Google, email, and the referral panel -----------------
   Two ways in, ranked by friction. The Google button is first because it is
   one click and the address comes back verified; the field is under it for
   everyone without a Google account. The button only renders when
   GOOGLE_CLIENT_ID is set — a button that fails when pressed is worse than
   no button. */

.wait-google { margin-bottom: 14px; }
.g-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  width: 100%;
  height: 52px;
  border-radius: 12px;
  border: 1px solid rgba(203, 208, 235, 0.22);
  background: #fff;
  color: #1F1F1F;
  font: 700 15px 'Satoshi', system-ui, sans-serif;
  cursor: pointer;
  transition: background .18s var(--ease), transform .12s var(--ease);
}
.g-btn:hover { background: #F2F2F4; }
.g-btn:active { transform: scale(.99); }
.g-btn svg { width: 18px; height: 18px; flex: 0 0 18px; }

.wait-or {
  position: relative;
  text-align: center;
  margin: 14px 0 0;
}
.wait-or::before {
  content: '';
  position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: var(--line);
}
.wait-or span {
  position: relative;
  padding: 0 12px;
  background: var(--bg);
  font-size: 12.5px;
  color: var(--dimmer);
}

/* --- the panel that replaces the form --- */
.wait-done {
  max-width: 510px;
  margin: 30px auto 0;
  padding: 26px;
  border-radius: 18px;
  border: 1px solid rgba(157, 123, 255, 0.28);
  background: rgba(18, 17, 26, 0.72);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  text-align: left;
}
.wait-done.rewarded { border-color: rgba(95, 203, 155, 0.45); }
.done-tick {
  width: 34px; height: 34px; margin: 0 0 14px;
  border-radius: 50%;
  background: #5FCB9B;
  position: relative;
}
.done-tick::after {
  content: '';
  position: absolute; left: 12px; top: 9px;
  width: 8px; height: 15px;
  border-right: 2.5px solid #071C13;
  border-bottom: 2.5px solid #071C13;
  transform: rotate(42deg);
}
/* the site lowercases headings; a confirmation is a sentence, not a brand mark */
.done-h { font-size: 24px; margin: 0 0 4px; text-transform: none; letter-spacing: -0.03em; }
.done-pos { margin: 0 0 22px; font-size: 15px; color: var(--dim); }
.done-pos b { color: var(--text); font-weight: 700; }

.done-ref { padding-top: 20px; border-top: 1px solid var(--line); }
.done-ref-h { margin: 0 0 4px; font-size: 15.5px; font-weight: 700; color: var(--text); }
.done-ref-h b { color: #7FE3B5; }
.done-ref-p { margin: 0 0 14px; font-size: 14px; line-height: 1.6; color: var(--dim); }
.done-ref-p b { color: var(--text); }

.done-link {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 5px 5px 14px;
  border-radius: 11px;
  border: 1px solid var(--line-2);
  background: rgba(6, 5, 16, 0.6);
}
.done-link code {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 13.5px;
  color: #C9B6FF;
}
.done-copy {
  flex: 0 0 auto;
  height: 36px;
  padding: 0 16px;
  border: 0;
  border-radius: 8px;
  background: var(--accent);
  color: #fff;
  font: 700 13px 'Satoshi', system-ui, sans-serif;
  cursor: pointer;
}
.done-copy:hover { filter: brightness(1.08); }

.done-share { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.share {
  flex: 1 1 auto;
  text-align: center;
  padding: 9px 12px;
  border-radius: 9px;
  border: 1px solid var(--line-2);
  font-size: 13px;
  font-weight: 600;
  color: var(--dim);
  text-decoration: none;
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.share:hover { border-color: rgba(157, 123, 255, .5); color: var(--text); }

.done-prog {
  display: flex;
  align-items: center;
  gap: 11px;
  margin-top: 18px;
  font-size: 13px;
  color: var(--dim);
}
.done-dots { display: flex; gap: 6px; }
.done-dots i {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: rgba(203, 208, 235, 0.16);
  display: block;
}
.done-dots i.on { background: #5FCB9B; }
.wait-done.rewarded .done-prog { color: #7FE3B5; font-weight: 600; }

@media (max-width: 520px) {
  .wait-done { padding: 20px; }
  .done-h { font-size: 21px; }
  .share { flex-basis: 100%; }
}
/* inert until waitlist.js gives it a destination */
.share:not([href]) { opacity: .5; pointer-events: none; }
