/* ==========================================================================
   ProxForge — public site
   Design tokens mirror src/tailwind.config.js so the site and the console
   read as the same product. Hand-rolled CSS on purpose: no build step, no
   third-party script, nothing to keep in sync with the app's Vite pipeline.
   ========================================================================== */

:root {
  --bg:      #05070c;
  --bg2:     #141a2b;
  --bg3:     #1e2740;
  --card:    rgba(255,255,255,0.03);
  --line:    rgba(255,255,255,0.07);
  --line2:   rgba(255,255,255,0.12);
  --text:    #e2e8f0;
  --muted:   #94a3b8;
  --accent:  #6c63ff;
  --accent2: #a78bfa;
  --accent3: #38bdf8;
  --good:    #34d399;
  --bad:     #fb7185;
  --warn:    #fbbf24;

  --sans:    'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --display: 'Space Grotesk', var(--sans);
  --mono:    ui-monospace, 'SFMono-Regular', 'Cascadia Code', Consolas, monospace;

  --wrap: 1140px;
  --r:    14px;
  --ease: cubic-bezier(.22,.61,.36,1);
}

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

html {
  scroll-behavior: smooth;
  /* the fixed nav must not cover a section heading when you jump to it */
  scroll-padding-top: 90px;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a   { color: inherit; text-decoration: none; }
em  { color: var(--text); font-style: italic; }

code {
  font-family: var(--mono);
  font-size: .86em;
  background: rgba(108,99,255,.14);
  border: 1px solid rgba(108,99,255,.22);
  color: #c7d2fe;
  padding: .1em .38em;
  border-radius: 5px;
  white-space: nowrap;
}

::selection { background: var(--accent); color: #fff; }

/* ---------- layout ---------- */

.wrap        { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: 28px; }
.wrap--tight { max-width: 860px; }

.section         { padding: 130px 0; position: relative; }
.section--alt    { background: linear-gradient(180deg, transparent, rgba(20,26,43,.55) 18%, rgba(20,26,43,.55) 82%, transparent); }

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

.eyebrow {
  font-family: var(--display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--accent3);
  margin-bottom: 18px;
}

.h2 {
  font-family: var(--display);
  font-size: clamp(30px, 4.6vw, 50px);
  font-weight: 700;
  line-height: 1.14;
  letter-spacing: -.02em;
  margin-bottom: 22px;
}

.dim  { color: var(--muted); }
.grad {
  background: linear-gradient(100deg, var(--accent) 0%, var(--accent2) 45%, var(--accent3) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

.lede {
  color: var(--muted);
  font-size: 19px;
  max-width: 68ch;
  margin-bottom: 54px;
}

/* ---------- scroll progress ---------- */

.scroll-progress {
  position: fixed; inset: 0 0 auto 0;
  height: 2px; z-index: 200;
  background: rgba(255,255,255,.05);
  pointer-events: none;
}
.scroll-progress span {
  display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, var(--accent), var(--accent2), var(--accent3));
  transform-origin: left;
}

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

.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 150;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding: 16px max(28px, calc((100vw - var(--wrap)) / 2 + 28px));
  transition: background .3s var(--ease), border-color .3s var(--ease), padding .3s var(--ease);
  border-bottom: 1px solid transparent;
}
.nav.is-stuck {
  background: rgba(5,7,12,.78);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom-color: var(--line);
  padding-block: 10px;
}

.nav__brand { display: flex; align-items: center; gap: 10px; font-family: var(--display); font-weight: 700; font-size: 19px; letter-spacing: -.01em; }
.nav__mark  { width: 26px; height: 26px; }

.nav__links { display: flex; gap: 20px; font-size: 14px; color: var(--muted); }
/* Without this a two-word link wraps to a second line long before the bar is
   actually full, and takes the whole bar's height with it. */
.nav__links a { position: relative; white-space: nowrap; transition: color .2s; }
.nav__links a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -6px; height: 1.5px;
  background: linear-gradient(90deg, var(--accent), var(--accent3));
  transform: scaleX(0); transform-origin: left; transition: transform .25s var(--ease);
}
.nav__links a:hover           { color: var(--text); }
.nav__links a:hover::after,
.nav__links a.is-current::after { transform: scaleX(1); }
.nav__links a.is-current      { color: var(--text); }

.nav__actions { display: flex; align-items: center; gap: 10px; }

.nav__cta {
  font-size: 13.5px; font-weight: 600;
  white-space: nowrap;
  padding: 8px 15px; border-radius: 999px;
  border: 1px solid rgba(108,99,255,.4);
  background: rgba(108,99,255,.12);
  color: #c7d2fe;
  transition: background .2s, border-color .2s;
}
.nav__cta:hover { background: rgba(108,99,255,.24); border-color: rgba(108,99,255,.7); }

.nav__login {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13.5px; font-weight: 500;
  white-space: nowrap;
  padding: 8px 14px; border-radius: 999px;
  border: 1px solid var(--line2);
  color: var(--muted);
  transition: color .2s, border-color .2s, background .2s;
}
.nav__login svg {
  width: 15px; height: 15px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.nav__login:hover { color: var(--text); border-color: rgba(255,255,255,.28); background: rgba(255,255,255,.05); }

/* Hamburger — mobile only, see the media query at the bottom. */
.nav__toggle {
  display: none;
  flex-direction: column; justify-content: center; gap: 5px;
  width: 40px; height: 40px; padding: 0 9px;
  background: none; border: 1px solid var(--line2); border-radius: 9px;
  cursor: pointer;
}
.nav__toggle span {
  display: block; height: 2px; border-radius: 2px;
  background: var(--text);
  transition: transform .25s var(--ease), opacity .2s;
}
.nav.is-open .nav__toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav.is-open .nav__toggle span:nth-child(2) { opacity: 0; }
.nav.is-open .nav__toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

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

.hero {
  position: relative;
  min-height: 100svh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 150px 0 90px;
  overflow: hidden;
  isolation: isolate;
}

.hero__mesh { position: absolute; inset: 0; width: 100%; height: 100%; z-index: -2; opacity: .55; }

.hero__glow {
  position: absolute; z-index: -1;
  top: -22%; left: 50%; translate: -50% 0;
  width: min(1100px, 130vw); aspect-ratio: 1;
  background:
    radial-gradient(circle at 50% 50%, rgba(108,99,255,.20), transparent 62%),
    radial-gradient(circle at 62% 38%, rgba(56,189,248,.13), transparent 55%);
  filter: blur(20px);
  pointer-events: none;
}

.hero__inner { width: 100%; max-width: var(--wrap); padding-inline: 28px; text-align: center; }

.pill {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 13px; font-weight: 500; letter-spacing: .04em;
  color: var(--muted);
  border: 1px solid var(--line2);
  background: rgba(255,255,255,.03);
  padding: 6px 15px; border-radius: 999px;
  margin-bottom: 30px;
}
.pill__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--good);
  box-shadow: 0 0 0 0 rgba(52,211,153,.55);
  animation: ping 2.4s infinite;
}
@keyframes ping {
  0%   { box-shadow: 0 0 0 0 rgba(52,211,153,.5); }
  70%  { box-shadow: 0 0 0 9px rgba(52,211,153,0); }
  100% { box-shadow: 0 0 0 0 rgba(52,211,153,0); }
}

.hero__title {
  font-family: var(--display);
  font-size: clamp(42px, 8vw, 88px);
  font-weight: 700;
  line-height: 1.03;
  letter-spacing: -.035em;
  margin-bottom: 26px;
}

.hero__sub { color: var(--muted); font-size: 19px; max-width: 66ch; margin: 0 auto 38px; }

.hero__actions { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-bottom: 20px; }

.hero__note {
  font-size: 14.5px; color: var(--muted);
  max-width: 56ch; margin: 0 auto 66px;
}

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--display); font-weight: 600; font-size: 15px;
  padding: 13px 26px; border-radius: 10px;
  border: 1px solid transparent;
  transition: transform .18s var(--ease), box-shadow .25s var(--ease), background .2s, border-color .2s;
  will-change: transform;
}
.btn:hover  { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn--primary {
  background: linear-gradient(100deg, var(--accent), var(--accent2));
  color: #fff;
  box-shadow: 0 10px 30px -10px rgba(108,99,255,.75);
}
.btn--primary:hover { box-shadow: 0 16px 40px -12px rgba(108,99,255,.95); }

.btn--ghost { border-color: var(--line2); color: var(--text); background: rgba(255,255,255,.02); }
.btn--ghost:hover { border-color: rgba(255,255,255,.28); background: rgba(255,255,255,.05); }

.hero__scroll {
  position: absolute; bottom: 26px; left: 50%; translate: -50% 0;
  display: flex; align-items: center; gap: 9px;
  font-size: 11px; letter-spacing: .26em; text-transform: uppercase; color: var(--muted);
  opacity: .65;
}
.hero__scroll span {
  width: 1px; height: 26px;
  background: linear-gradient(180deg, transparent, var(--accent3));
  animation: drop 1.9s var(--ease) infinite;
}
@keyframes drop {
  0%   { transform: scaleY(0); transform-origin: top; }
  45%  { transform: scaleY(1); transform-origin: top; }
  46%  { transform: scaleY(1); transform-origin: bottom; }
  100% { transform: scaleY(0); transform-origin: bottom; }
}

/* ---------- screenshot frames (image slots) ---------- */

.shot { position: relative; }

.shot--hero { max-width: 980px; margin-inline: auto; }

/* 1.9, not 16/9 (1.78) — a maximised browser on a 16:9 display screenshots its
   *viewport*, which is wider than the screen once the OS taskbar and the
   browser's own chrome are taken off the height. At 16/9 the frames cropped the
   sidebar's labels off the left of every capture. */
.shot__frame {
  position: relative;
  aspect-ratio: 1.9;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background:
    linear-gradient(135deg, rgba(108,99,255,.07), rgba(56,189,248,.05)),
    var(--bg2);
  overflow: hidden;
  box-shadow: 0 40px 90px -30px rgba(0,0,0,.9);
}
/* Same ratio as the hero — see above. These carry their own border radius
   because they aren't sitting under a mock browser chrome. */
.shot__frame--tall { aspect-ratio: 1.9; border-radius: var(--r); }
.shot__frame--wide { aspect-ratio: 1.9; border-radius: var(--r); margin-top: 46px; }

.shot__frame img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }

/* the placeholder is what shows until a real screenshot is dropped in */
.shot__placeholder {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; text-align: center; padding: 24px;
  background-image:
    linear-gradient(rgba(255,255,255,.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.028) 1px, transparent 1px);
  background-size: 34px 34px;
}
.shot__placeholder::before {
  content: '';
  width: 34px; height: 34px; margin-bottom: 6px;
  border: 1.5px dashed var(--line2);
  border-radius: 7px;
  background: radial-gradient(circle at 32% 32%, rgba(108,99,255,.5), transparent 62%);
}
.shot__placeholder strong { font-family: var(--mono); font-size: 13px; color: #c7d2fe; font-weight: 500; }
.shot__placeholder span   { font-size: 13.5px; color: var(--muted); max-width: 42ch; }

.shot figcaption, .gallery figcaption {
  margin-top: 14px; text-align: center;
  font-size: 13.5px; color: var(--muted);
}

/* Two up, not three: these are screenshots of a dense console, and at a third
   of the width the text in them is unreadable — which defeats the point of
   showing them at all. */
.gallery {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 28px;
  margin-top: 72px;
}
.gallery--pair { margin-top: 46px; }

/* A single screenshot in a full-width section would blow up to 1140px wide and
   tower over the cards above it. */
.shot--solo { max-width: 820px; margin: 46px auto 0; }

/* ---------- lightbox ----------
   Every screenshot is cropped by object-fit: cover in its frame, so the whole
   point of clicking one is to see the part the crop hid. The lightbox is built
   by main.js and only wired up if JS ran — with no JS the images simply don't
   zoom, which is a fine degradation for a marketing page. */

/* The frames are clickable, so say so. Only the ones holding a real <img>: the
   placeholder frames have nothing to zoom to. */
.shot__frame:has(img) { cursor: zoom-in; }
.shot__frame:has(img)::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(0deg, rgba(108,99,255,.14), transparent 55%);
  opacity: 0; transition: opacity .25s var(--ease);
  pointer-events: none;
}
.shot__frame:has(img):hover::after { opacity: 1; }

.lightbox {
  position: fixed; inset: 0; z-index: 300;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(16px, 5vw, 64px);
  background: rgba(3, 5, 10, .82);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  opacity: 0; visibility: hidden;
  transition: opacity .3s var(--ease), visibility 0s .3s;
  cursor: zoom-out;
}
.lightbox.is-open { opacity: 1; visibility: visible; transition: opacity .3s var(--ease); }

.lightbox__figure {
  margin: 0; max-width: 100%; max-height: 100%;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
}
.lightbox__img {
  max-width: 100%;
  max-height: calc(100vh - clamp(96px, 12vw, 180px));
  object-fit: contain;
  border: 1px solid var(--line2);
  border-radius: var(--r);
  box-shadow: 0 40px 100px -20px rgba(0,0,0,.9);
  background: var(--bg2);
  transform: scale(.96); transition: transform .3s var(--ease);
}
.lightbox.is-open .lightbox__img { transform: scale(1); }
.lightbox__caption {
  text-align: center; font-size: 13.5px; color: var(--muted);
  max-width: 70ch;
}
.lightbox__close {
  position: absolute; top: 18px; right: 20px;
  width: 42px; height: 42px;
  display: flex; align-items: center; justify-content: center;
  font-size: 26px; line-height: 1;
  color: var(--text); background: var(--card);
  border: 1px solid var(--line2); border-radius: 50%;
  cursor: pointer;
  transition: background .2s, border-color .2s, transform .2s var(--ease);
}
.lightbox__close:hover { background: var(--bg3); border-color: var(--accent2); transform: scale(1.06); }

@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox__img { transition: opacity .3s linear; }
  .lightbox__img { transform: none; }
}

/* ---------- marquee ---------- */

.marquee {
  border-block: 1px solid var(--line);
  background: rgba(20,26,43,.4);
  padding: 15px 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee__track {
  display: flex; align-items: center; gap: 22px;
  width: max-content;
  animation: slide 38s linear infinite;
}
.marquee__track span {
  font-family: var(--display); font-size: 14px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  white-space: nowrap;
}
.marquee__track i { color: var(--accent); font-size: 8px; font-style: normal; }
@keyframes slide { to { transform: translateX(-50%); } }

/* ---------- what it is / split ---------- */

.split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 22px;
  margin-top: 52px;
}
.split__col {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  padding: 30px 28px;
}
.split__head {
  font-family: var(--display); font-size: 17px; font-weight: 600;
  margin-bottom: 20px; padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.split__head--bad  { color: var(--bad); }
.split__head--good { color: var(--good); }

.ticks { list-style: none; display: flex; flex-direction: column; gap: 13px; }
.ticks li {
  position: relative; padding-left: 28px;
  font-size: 15.5px; color: var(--muted); line-height: 1.55;
}
.ticks li::before {
  position: absolute; left: 0; top: 0;
  font-weight: 700; font-size: 14px;
}
.ticks--bad  li::before { content: '×'; color: var(--bad);  font-size: 17px; line-height: 1.4; }
.ticks--good li::before { content: '✓'; color: var(--good); }

.quote {
  margin-top: 52px;
  border-left: 2px solid var(--accent);
  padding: 6px 0 6px 26px;
  font-family: var(--display);
  font-size: clamp(18px, 2.2vw, 23px);
  font-weight: 500;
  line-height: 1.5;
  color: var(--text);
  max-width: 74ch;
}

/* ---------- scrollytelling ---------- */

.scrolly { padding: 120px 0 140px; position: relative; }

.scrolly__grid {
  max-width: var(--wrap);
  margin: 60px auto 0;
  padding-inline: 28px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 60px;
  align-items: start;
}

.scrolly__sticky {
  position: sticky;
  /* Centred in the viewport, but never taller than it: a fixed 500px stage
     overflowed under the nav on a laptop-height window, which is exactly where
     the diagram most needs to stay visible. Derived from one custom property so
     the height and the offset can't disagree. */
  --stage-h: min(500px, 68vh);
  top: calc(50vh - (var(--stage-h) / 2));
  height: var(--stage-h);
}

.stage {
  position: relative;
  width: 100%; height: 100%;
  border: 1px solid var(--line);
  border-radius: 20px;
  background:
    radial-gradient(circle at 50% 42%, rgba(108,99,255,.13), transparent 62%),
    var(--bg2);
  display: grid; place-items: center;
  overflow: hidden;
}
.stage__svg { width: min(100%, 460px); height: auto; }

.stage__rails path {
  fill: none;
  stroke: rgba(255,255,255,.09);
  stroke-width: 2;
  stroke-linecap: round;
}

/* The completed run. Length and offset are set from JS (see TRACK_LEN). */
.stage__trail {
  fill: none;
  stroke: url(#edge);
  stroke-width: 2;
  stroke-linecap: round;
  transition: stroke-dashoffset .6s var(--ease);
}

.stage__pulse { opacity: 0; transition: opacity .4s; }
.stage.is-live .stage__pulse { opacity: 1; }

.node rect {
  fill: rgba(255,255,255,.03);
  stroke: var(--line2);
  stroke-width: 1.25;
  transition: fill .45s var(--ease), stroke .45s var(--ease);
}
.node text {
  fill: var(--muted);
  font-family: 'Space Grotesk', sans-serif;
  font-size: 13.5px;
  font-weight: 600;
  text-anchor: middle;
  transition: fill .45s var(--ease);
}
.node { transition: transform .5s var(--ease); transform-box: fill-box; transform-origin: center; }

.node.is-past rect { stroke: rgba(108,99,255,.5); fill: rgba(108,99,255,.09); }
.node.is-past text { fill: #c7d2fe; }

.node.is-active        { transform: scale(1.09); }
.node.is-active rect   { stroke: var(--accent3); fill: rgba(56,189,248,.16); filter: url(#soft); }
.node.is-active text   { fill: #fff; }

.scrolly__steps { display: flex; flex-direction: column; }

/* A step needs enough room that it's clearly the one the diagram is following,
   but its height must not simply track the viewport: at `52vh` a tall window
   stretched the gaps between paragraphs to match, so the text drifted further
   and further apart the bigger the browser got. Clamped, it settles at a
   readable block on a laptop and stops growing on a tall monitor — the lower
   bound is what keeps two steps from sharing the viewport centre and making the
   diagram flick between them. */
.step {
  min-height: clamp(300px, 46vh, 420px);
  display: flex; flex-direction: column; justify-content: center;
  padding: 22px 0;
}
/* Dimming a step is only safe if JS is there to undim it — same reason as .reveal. */
.js .step {
  opacity: .3;
  filter: blur(1px);
  transform: translateY(10px);
  transition: opacity .5s var(--ease), filter .5s var(--ease), transform .5s var(--ease);
}
.js .step.is-active { opacity: 1; filter: blur(0); transform: none; }

.step__idx {
  font-family: var(--display); font-size: 13px; font-weight: 700;
  letter-spacing: .2em; color: var(--accent3); margin-bottom: 10px;
}
.step__title {
  font-family: var(--display);
  font-size: clamp(24px, 3vw, 33px);
  font-weight: 700; letter-spacing: -.02em;
  margin-bottom: 16px;
}
.step p { color: var(--muted); font-size: 16.5px; max-width: 52ch; }
.step p + p { margin-top: 14px; }

.step__meta {
  font-size: 13.5px !important;
  color: #64748b !important;
  border-left: 2px solid var(--line2);
  padding-left: 14px;
  font-style: italic;
}

/* ---------- networking ---------- */

.netgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 22px; }
/* The fifth card is the conclusion the other four build to, so it spans rather
   than sitting alone in a half-empty row. */
.net--wide { grid-column: 1 / -1; }

.net {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  padding: 30px 28px;
  overflow: hidden;
}
/* A hairline down the left, brightening toward the top — reads as the numbered
   run of steps it is, without a heavy border. */
.net::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: linear-gradient(180deg, var(--accent), transparent);
}
.net__num {
  display: block;
  font-family: var(--display); font-size: 12px; font-weight: 700;
  letter-spacing: .2em; color: var(--accent3);
  margin-bottom: 10px;
}
.net h3 { font-family: var(--display); font-size: 19px; font-weight: 600; margin-bottom: 10px; }
.net p  { font-size: 15px; color: var(--muted); }

.fits {
  display: flex; gap: 20px; align-items: flex-start;
  margin-top: 22px; padding: 30px 28px;
  border: 1px solid rgba(52,211,153,.25);
  background: rgba(52,211,153,.04);
  border-radius: var(--r);
}
.fits__mark {
  flex: none;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(52,211,153,.14);
  border: 1px solid rgba(52,211,153,.4);
  color: var(--good); font-weight: 700; font-size: 15px;
}
.fits h3 { font-family: var(--display); font-size: 19px; font-weight: 600; margin-bottom: 10px; }
.fits p  { font-size: 15px; color: var(--muted); }
.fits p + p { margin-top: 12px; }

/* ---------- stats ---------- */

.stats { padding: 40px 0 110px; }
.stats__grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: linear-gradient(180deg, rgba(108,99,255,.06), transparent);
  padding: 46px 30px;
}
.stat { text-align: center; }
.stat__num {
  font-family: var(--display);
  font-size: clamp(38px, 5vw, 56px);
  font-weight: 700; line-height: 1;
  letter-spacing: -.03em;
  margin-bottom: 10px;
  background: linear-gradient(180deg, #fff, #94a3b8);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.stat__lbl { font-size: 14px; color: var(--muted); }

/* ---------- tabs + cards ---------- */

.tabs {
  display: inline-flex; gap: 4px;
  border: 1px solid var(--line);
  background: rgba(255,255,255,.02);
  border-radius: 999px; padding: 4px;
  margin-bottom: 44px;
}
.tab {
  font-family: var(--display); font-size: 14px; font-weight: 600;
  color: var(--muted);
  background: none; border: 0; cursor: pointer;
  padding: 9px 20px; border-radius: 999px;
  transition: color .2s, background .25s var(--ease);
}
.tab:hover      { color: var(--text); }
.tab.is-active  { color: #fff; background: linear-gradient(100deg, var(--accent), var(--accent2)); }

.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.cards.is-hidden { display: none; }

.card {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  padding: 28px 26px;
  overflow: hidden;
  transition: transform .3s var(--ease), border-color .3s var(--ease);
}
.card::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 0%), rgba(108,99,255,.14), transparent 42%);
  opacity: 0; transition: opacity .35s;
  pointer-events: none;
}
.card:hover { transform: translateY(-4px); border-color: var(--line2); }
.card:hover::before { opacity: 1; }

.card__ico {
  width: 42px; height: 42px; margin-bottom: 18px;
  display: grid; place-items: center;
  border-radius: 10px;
  border: 1px solid rgba(108,99,255,.28);
  background: rgba(108,99,255,.1);
  color: var(--accent2);
  font-size: 18px;
}
.card h3 { font-family: var(--display); font-size: 17.5px; font-weight: 600; margin-bottom: 9px; }
.card p  { font-size: 15px; color: var(--muted); }

/* ---------- architecture rack ---------- */

.rack {
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--card);
}
.rack__row {
  display: grid;
  grid-template-columns: 130px 1fr auto;
  align-items: center; gap: 20px;
  padding: 19px 26px;
  border-bottom: 1px solid var(--line);
  transition: background .25s;
}
.rack__row:last-child { border-bottom: 0; }
.rack__row:hover      { background: rgba(255,255,255,.025); }

.rack__name { font-family: var(--mono); font-size: 14.5px; color: #c7d2fe; }
.rack__desc { font-size: 15px; color: var(--muted); }
.rack__tag {
  font-size: 11px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
  padding: 4px 11px; border-radius: 999px;
  border: 1px solid var(--line2); color: var(--muted);
}
.rack__tag--edge { border-color: rgba(56,189,248,.4);  color: var(--accent3); background: rgba(56,189,248,.08); }
.rack__tag--data { border-color: rgba(167,139,250,.4); color: var(--accent2); background: rgba(167,139,250,.08); }

.notes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 46px; }
.note  { border-left: 2px solid var(--accent); padding-left: 20px; }
.note h4 { font-family: var(--display); font-size: 16px; font-weight: 600; margin-bottom: 8px; }
.note p  { font-size: 14.5px; color: var(--muted); }

/* ---------- security ---------- */

.secgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.sec {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  padding: 26px 24px;
  transition: border-color .3s;
}
.sec:hover { border-color: rgba(108,99,255,.35); }
.sec h4 { font-family: var(--display); font-size: 16.5px; font-weight: 600; margin-bottom: 10px; }
.sec p  { font-size: 14.8px; color: var(--muted); }

.warn {
  display: flex; gap: 18px; align-items: flex-start;
  margin-top: 40px; padding: 24px 26px;
  border: 1px solid rgba(251,191,36,.28);
  background: rgba(251,191,36,.05);
  border-radius: var(--r);
}
.warn__ico {
  flex: none;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(251,191,36,.16);
  border: 1px solid rgba(251,191,36,.4);
  color: var(--warn); font-weight: 700; font-size: 14px;
}
.warn p { font-size: 15px; color: var(--muted); }
.warn strong { color: #fde68a; }

/* ---------- pricing ---------- */

/* Stretch rather than start, so the two cards are the same height and the
   `margin-top: auto` on each CTA lands them on the same line. */
.tiers { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: stretch; }

.tier {
  position: relative;
  display: flex; flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  padding: 32px 30px;
}
/* The one people are meant to end up on, so it carries the accent — but it's
   still a card, not a pop-out: the free tier has to read as a real offer
   rather than the thing you scroll past. */
.tier--feature {
  border-color: rgba(108,99,255,.42);
  background: linear-gradient(180deg, rgba(108,99,255,.10), var(--card) 60%);
}
.tier__flag {
  position: absolute; top: -11px; left: 30px;
  font-family: var(--display); font-size: 10.5px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: #c7d2fe;
  padding: 4px 12px; border-radius: 999px;
  border: 1px solid rgba(108,99,255,.5);
  background: #131728;
}

.tier__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 18px; margin-bottom: 18px;
  border-bottom: 1px solid var(--line);
}
.tier__name  { font-family: var(--display); font-size: 21px; font-weight: 700; }
.tier__price {
  font-family: var(--display); font-size: 26px; font-weight: 700;
  letter-spacing: -.02em; line-height: 1;
}
.tier__price span { font-size: 13px; font-weight: 500; color: var(--muted); margin-left: 6px; letter-spacing: 0; }

.tier__pitch { font-size: 15.5px; color: var(--muted); margin-bottom: 22px; }

.tier__limits {
  list-style: none;
  display: flex; flex-direction: column; gap: 10px;
  padding: 18px 20px; margin-bottom: 22px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(255,255,255,.02);
  font-size: 15px; color: var(--muted);
}
.tier__limits li { display: flex; align-items: baseline; gap: 12px; }
.tier__num {
  flex: none; min-width: 30px;
  font-family: var(--display); font-size: 19px; font-weight: 700;
  color: var(--text); text-align: right;
}

/* Pushes the button to the bottom of whichever card is shorter, so the two
   CTAs line up however the copy lengths land. */
.tier__list { margin-bottom: 26px; }
.tier__cta  { margin-top: auto; width: 100%; }

.fits--plain {
  margin-top: 34px;
  border-color: rgba(108,99,255,.25);
  background: rgba(108,99,255,.05);
}
.fits__mark--info {
  background: rgba(108,99,255,.16);
  border-color: rgba(108,99,255,.45);
  color: #c7d2fe;
  font-family: var(--display); font-style: italic;
}

/* ---------- closing call to action ---------- */

.cta {
  display: flex; align-items: center; justify-content: space-between; gap: 28px;
  flex-wrap: wrap;
  margin-top: 34px; padding: 30px 32px;
  border: 1px solid rgba(108,99,255,.3);
  border-radius: var(--r);
  background: linear-gradient(120deg, rgba(108,99,255,.12), rgba(56,189,248,.06));
}
.cta h3 { font-family: var(--display); font-size: 21px; font-weight: 700; margin-bottom: 6px; }
.cta p  { font-size: 15px; color: var(--muted); }
.cta__actions { display: flex; gap: 12px; flex-wrap: wrap; }

/* ---------- status ---------- */

.statusbox {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: linear-gradient(180deg, rgba(108,99,255,.08), transparent);
  padding: 34px 32px;
  display: flex; flex-direction: column; gap: 16px;
}
.statusbox p       { color: var(--muted); font-size: 16.5px; }
.statusbox strong  { color: var(--text); }
.statusbox__foot   { border-top: 1px solid var(--line); padding-top: 16px; font-style: italic; font-size: 15px !important; }

.roadmap { display: flex; flex-direction: column; gap: 14px; margin-top: 34px; }
.rm {
  display: grid; grid-template-columns: 96px 1fr; gap: 22px; align-items: center;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--card);
  padding: 18px 22px;
}
.rm p { font-size: 15px; color: var(--muted); }
.rm__state {
  font-family: var(--display); font-size: 11px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  text-align: center;
  padding: 5px 0; border-radius: 999px;
}
.rm__state--done { color: var(--good);   background: rgba(52,211,153,.12); border: 1px solid rgba(52,211,153,.3); }
.rm__state--wip  { color: var(--accent3); background: rgba(56,189,248,.12); border: 1px solid rgba(56,189,248,.3); }
.rm__state--idea { color: var(--muted);  background: rgba(255,255,255,.04); border: 1px solid var(--line2); }

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

.foot { border-top: 1px solid var(--line); padding: 60px 0 40px; background: rgba(20,26,43,.4); }
.foot__top {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 40px;
  flex-wrap: wrap;
  padding-bottom: 34px; border-bottom: 1px solid var(--line);
}
.foot__brand { font-family: var(--display); font-size: 21px; font-weight: 700; margin-bottom: 6px; }
.foot__tag   { font-size: 14.5px; color: var(--muted); max-width: 46ch; }
.foot__links { display: flex; flex-wrap: wrap; gap: 20px; font-size: 14.5px; color: var(--muted); }
.foot__links a:hover { color: var(--text); }
.foot__note  { margin-top: 24px; font-size: 13px; color: #64748b; }

/* ---------- reveal-on-scroll ---------- */

/* Only hide what JS is able to bring back. Without the `js` class on <html>
   (see the inline script in index.html) these rules never apply and the page
   renders as a plain, fully visible document. */
.js .reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
  transition-delay: var(--d, 0ms);
}
.js .reveal.is-in { opacity: 1; transform: none; }

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

/* Eight links plus two buttons is more than the bar holds at its own max
   width. Tighten before the hamburger takes over rather than letting the
   links wrap under the brand for the 240px in between. */
@media (max-width: 1240px) {
  .nav__links { gap: 14px; font-size: 13.2px; }
  .nav__login { padding-inline: 10px; }
  .nav__cta   { padding-inline: 12px; }
}

@media (max-width: 1000px) {
  /* The bar stays fixed; the links become a dropdown panel under it, opened by
     the hamburger. The bar goes opaque while open even before it's "stuck",
     or the panel would read as floating over the hero. */
  .nav__toggle { display: flex; }
  .nav.is-open {
    background: rgba(5,7,12,.96);
    border-bottom-color: var(--line);
  }
  .nav__links {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; gap: 0;
    padding: 6px 28px 18px;
    background: rgba(5,7,12,.96);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--line);
    visibility: hidden; opacity: 0; transform: translateY(-8px);
    transition: opacity .25s var(--ease), transform .25s var(--ease), visibility 0s .25s;
  }
  .nav.is-open .nav__links {
    visibility: visible; opacity: 1; transform: none;
    transition: opacity .25s var(--ease), transform .25s var(--ease);
  }
  .nav__links a { padding: 13px 0; font-size: 15.5px; border-bottom: 1px solid var(--line); }
  .nav__links a:last-child { border-bottom: 0; }
  .nav__links a::after { display: none; }
  .split, .cards, .secgrid, .notes, .gallery, .netgrid, .tiers { grid-template-columns: 1fr; }
  .fits { flex-direction: column; gap: 14px; }
  /* Source order stands when stacked — Free first. Leading a phone with a card
     whose price is "announced at launch" buries the offer that's actually
     available today. */
  .tiers { gap: 30px; }
  .stats__grid { grid-template-columns: repeat(2, 1fr); gap: 34px; }

  /* the sticky diagram can't share a row with the steps on a narrow screen:
     it pins to the top and the steps scroll under it instead */
  .scrolly__grid { grid-template-columns: 1fr; gap: 0; }
  /* The sticky band carries its own opaque background rather than only the
     stage doing so: the gap between the nav and the stage would otherwise let
     the step text scroll visibly through it. */
  .scrolly__sticky {
    top: 54px; z-index: 5;
    height: auto;
    padding: 12px 0 14px;
    margin-bottom: 22px;
    background: var(--bg);
  }
  /* Short on purpose: all seven nodes at once ate half a phone screen. The
     stage shows a ~3-node window and JS pans the SVG to keep the active node
     centred (see panTo in main.js). Without JS the middle of the diagram shows,
     static — it's aria-hidden decoration, so that's fine. */
  .scrolly__sticky .stage { height: 170px; }
  .stage__svg { transition: transform .5s var(--ease); }
  .stage { background-color: rgba(5,7,12,.94); backdrop-filter: blur(10px); }
  .step  { min-height: auto; padding: 34px 0; opacity: 1; filter: none; transform: none; }
}

@media (max-width: 640px) {
  body { font-size: 16px; }
  .section { padding: 84px 0; }
  .wrap { padding-inline: 20px; }
  .stats__grid { grid-template-columns: 1fr; }
  .rack__row { grid-template-columns: 1fr auto; gap: 6px 14px; }
  .rack__desc { grid-column: 1 / -1; }
  .rm { grid-template-columns: 1fr; gap: 12px; }
  .rm__state { justify-self: start; padding-inline: 14px; }
  .foot__top { flex-direction: column; }

  /* Brand + login + hamburger is all that fits. The login is the one that
     stays: someone who already has an account needs a way back in from any
     device, and "register interest" is still on the hero, the pricing cards
     and the closing CTA. */
  .nav__cta { display: none; }
  .cta { flex-direction: column; align-items: flex-start; }
  .cta__actions .btn { flex: 1 1 auto; }
  .tier { padding: 28px 22px; }
  .tier__head { flex-direction: column; align-items: flex-start; gap: 6px; }
}

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  /* These must out-specify `.js .reveal` / `.js .step` above, or motion-sensitive
     visitors get a page permanently stuck at the dimmed starting state. */
  .js .reveal { opacity: 1; transform: none; }
  .js .step   { opacity: 1; filter: none; transform: none; }
  .marquee__track { animation: none; }
}
