/* ============================================================
   VoraSphere page — ported from "VoraSphere Landing"

   Loaded AFTER home.css and scoped entirely under `body.vs`. It carries
   this page's layout, motion and vocabulary — not its colours. Every
   colour comes from the active site theme, so the theme switcher in the
   header moves this page with the rest of the site.

   The reference painted every rule inline. Here the same rules are
   classes so the page can be themed, printed and reduced-motion'd in
   one place.

   Type: Sora (display + body) · JetBrains Mono (labels and readouts)
   ============================================================ */

body.vs {
  /* No palette here any more. Every colour on this page comes from the
     active site theme, so the header's theme switcher moves this page with
     all the others. What survives is the page's own vocabulary — the names
     a rule reads by — now pointing at theme tokens rather than at hex.

     --vs-cyan and --vs-teal keep their names because thirty rules below
     say "cyan" where they mean "the bright charge"; renaming them would
     churn the whole file to say the same thing. */
  --vs-teal:   var(--accent-2);
  --vs-teal-d: var(--green-m);
  /* the bright charge, not the mid one: --vs-cyan is what this page prints
     on the dark slab, where the mid charge is too close to the ground */
  --vs-cyan:   var(--lime-b);
  --vs-cyan-p: color-mix(in srgb, var(--lime-b) 55%, var(--on-slab));
  /* these cards sit on the dark slab, so their body is a lifted slab —
     the page's card surface would be a light panel on a dark band */
  --vs-card:   color-mix(in srgb, var(--on-slab) 8%, var(--slab));
  --vs-mute:   var(--ink-3);
  --vs-live:   var(--ok);       /* the platform, shipping today */
  --vs-road:   var(--pending);  /* the business modules, still ahead */

  --radius: 19px;
  --radius-sm: 999px;

  --ff-display: "Sora", system-ui, sans-serif;
  --ff-body: "Sora", system-ui, sans-serif;
  --ff-mono: "JetBrains Mono", ui-monospace, monospace;
  --ff-serif: "Sora", system-ui, sans-serif;

  font-family: var(--ff-body);
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}


/* the ambient canvas replaces the home page's dotted bit field */
body.vs::before { display: none; }

body.vs ::selection { background: var(--vs-cyan-p); color: var(--ink); }

body.vs h1, body.vs h2, body.vs h3 {
  font-family: var(--ff-display);
  font-weight: 800;
  letter-spacing: -0.03em;
}

/* The reference centres on a 1140px measure. Scoped to <main> on purpose:
   the header and footer are shared with every other page and run
   full-bleed there, so capping them here would make this one page's
   chrome narrower than the rest of the site. */
/* the site measure, not this page's own — see the note in allyvora.css */
body.vs main .wrap { max-width: var(--maxw); padding-inline: 32px; }

/* the header is sticky, so an in-page jump has to stop short of it */
body.vs main > section[id] { scroll-margin-top: 84px; }

/* ---------- the motion vocabulary ---------- */
@keyframes vs-ripple   { from { transform: translate(-50%, -50%) scale(0); opacity: .7; }
                         to   { transform: translate(-50%, -50%) scale(1); opacity: 0; } }
@keyframes vs-bob      { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(7px); } }
@keyframes vs-spin     { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes vs-sweep    { from { background-position: 0% 50%; } to { background-position: 200% 50%; } }
@keyframes vs-sweep3   { from { background-position: 0% 50%; } to { background-position: 300% 50%; } }
@keyframes vs-orbit    { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes vs-pulsedot { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.7); } }
@keyframes vs-pop      { 0% { transform: scale(1); }
                         50% { transform: scale(1.09) rotate(-1.5deg); }
                         100% { transform: translateY(-4px) scale(1.04); } }
@keyframes vs-scan     { from { left: -140px; } to { left: 100%; } }
@keyframes vs-dash     { from { background-position: 0 0; } to { background-position: 22px 0; } }

/* ============================================================
   CANVASES
   ============================================================ */

/* The ambient data streams, behind the whole page. A <canvas>, so the
   browser offers no "Save image as…" on it — the site emits no <img>
   anywhere and this page is no exception. */
.vs-ambient {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: -1;
  pointer-events: none;
}

/* ============================================================
   HERO — the sphere
   ============================================================ */
.vs-hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: crosshair;
}
.vs-sphere-wrap { position: absolute; inset: 0; will-change: transform; }
.vs-sphere-wrap canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

.vs-hero-copy {
  position: relative; z-index: 2;
  text-align: center;
  padding: 110px 24px 60px;
  max-width: 880px;
  pointer-events: none;
  will-change: transform;
}
.vs-kicker {
  font-family: var(--ff-mono);
  font-size: 12px; letter-spacing: .28em;
  color: var(--vs-teal); font-weight: 600;
  margin: 0 0 26px;
}
.vs-hero h1 {
  margin: 0;
  font-size: clamp(52px, 7.5vw, 96px);
  line-height: .96;
  letter-spacing: -0.045em;
}
/* the wordmark sweep: a gradient clipped to the glyphs, panned forever */
.vs-shine {
  background: linear-gradient(90deg, var(--ink) 35%, var(--vs-teal) 50%, var(--ink) 65%);
  background-size: 250% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  animation: vs-sweep 7s linear infinite;
}
/* the hero's second line runs teal -> cyan -> teal instead of through the ink */
.vs-shine-c {
  background-image: linear-gradient(90deg, var(--vs-teal), var(--vs-cyan), var(--vs-teal));
  background-size: 200% 100%;
  animation-duration: 5s;
}
.vs-lead {
  font-size: 19px; line-height: 1.65;
  color: var(--ink-2);
  max-width: 620px; margin: 30px auto 0;
  text-wrap: pretty;
}
.vs-actions {
  display: flex; gap: 14px; justify-content: center;
  margin-top: 40px;
  pointer-events: auto;
  flex-wrap: wrap;
}
.vs-hint {
  font-family: var(--ff-mono);
  font-size: 11px; letter-spacing: .18em;
  color: var(--vs-mute);
  margin-top: 54px;
}
.vs-scroll {
  position: absolute; bottom: 26px; left: 50%;
  transform: translateX(-50%);
  animation: vs-bob 2.2s ease-in-out infinite;
  color: var(--vs-mute); font-size: 20px;
}

/* the two pill buttons, matching the reference exactly */
body.vs .btn {
  border-radius: 999px;
  font-weight: 600; font-size: 15px;
  padding: 15px 30px;
}
/* The fill and its label come from home.css, so every primary button on the
   site is the same colour pair and there is exactly one contrast guarantee
   to keep. This page only sets its own shape. */
body.vs .btn-primary {
  box-shadow: 0 8px 24px color-mix(in srgb, var(--lime-c) 28%, transparent);
}
body.vs .btn-primary:hover {
  box-shadow: 0 12px 30px color-mix(in srgb, var(--lime-c) 36%, transparent);
}
body.vs .btn-ghost {
  border: 1.5px solid color-mix(in srgb, var(--ink) 16%, transparent);
  color: var(--ink); background: none;
}
body.vs .btn-ghost:hover { border-color: var(--vs-teal); color: var(--vs-teal); background: none; }

/* ============================================================
   STATS STRIP
   ============================================================ */
.vs-stats {
  position: relative; overflow: hidden;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
/* the light bar that sweeps the strip left to right */
.vs-stats::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 130px;
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, var(--vs-cyan) 14%, transparent), transparent);
  animation: vs-scan 5s linear infinite;
  pointer-events: none;
}
.vs-stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); }
.vs-stat {
  padding: 38px 20px; text-align: center;
  border-left: 1px solid var(--line);
}
.vs-stat:first-child { border-left: 0; }
.vs-stat b {
  display: block;
  font-size: 44px; font-weight: 800; letter-spacing: -0.03em;
  background: linear-gradient(90deg, var(--ink) 30%, var(--vs-teal) 50%, var(--ink) 70%);
  background-size: 200% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  animation: vs-sweep 5s linear infinite;
}
.vs-stat span {
  display: block;
  font-family: var(--ff-mono);
  font-size: 11.5px; letter-spacing: .08em;
  color: var(--ink-3); margin-top: 8px;
}

/* ============================================================
   SECTION FURNITURE
   ============================================================ */
.vs-sec { padding: 130px 0 40px; }
.vs-sec-tight { padding: 120px 0 40px; }
.vs-eyebrow {
  font-family: var(--ff-mono);
  font-size: 12px; letter-spacing: .28em;
  color: var(--vs-teal); font-weight: 600;
  margin: 0;
}
.vs-h2 {
  margin: 18px 0 0;
  font-size: clamp(34px, 4vw, 52px);
  line-height: 1.08;
  max-width: 700px;
}
.vs-note {
  margin: 22px 0 0;
  font-size: 16px; line-height: 1.7;
  color: var(--ink-3); max-width: 620px;
}
.vs-head-row {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; flex-wrap: wrap;
}
/* the "LIVE" chip beside the platform heading */
.vs-badge {
  font-family: var(--ff-mono); font-size: 12px;
  color: var(--ok); background: color-mix(in srgb, var(--ok) 10%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ok) 35%, transparent);
  padding: 8px 16px; border-radius: 999px;
  display: flex; align-items: center; gap: 8px;
}
.vs-badge i {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--vs-live); display: inline-block;
  animation: vs-pulsedot 1.8s ease-in-out infinite;
}

/* ============================================================
   CARDS — a 1.5px animated gradient edge around a plain surface body
   ============================================================ */
.vs-card {
  border-radius: 19px; padding: 1.5px;
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--vs-teal) 25%, transparent),
              color-mix(in srgb, var(--vs-cyan) 90%, transparent),
              color-mix(in srgb, var(--vs-teal) 25%, transparent));
  background-size: 300% 100%;
  animation: vs-sweep3 4s linear infinite;
}
.vs-card > div {
  background-color: var(--surface);
  border-radius: 17.5px;
  height: 100%; box-sizing: border-box;
  transition: transform .3s ease;
  cursor: default;
}
.vs-card:hover > div { transform: translateY(-6px); }

.vs-grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; margin-top: 56px; }
.vs-grid-2 { display: grid; grid-template-columns: 1fr 1fr;     gap: 18px; margin-top: 56px; }
.vs-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 24px; }

/* --- the V O R A cards --- */
.vs-vora .vs-card > div { padding: 30px 26px; }
.vs-letter { font-size: 52px; font-weight: 800; letter-spacing: -0.04em; color: var(--vs-teal); line-height: 1; }
.vs-word   { font-weight: 700; font-size: 17px; margin-top: 16px; }
.vs-card p { margin: 10px 0 0; font-size: 14px; line-height: 1.6; color: var(--ink-3); text-wrap: pretty; }
.vs-sphere-line {
  margin: 34px auto 0; text-align: center;
  font-size: 16px; color: var(--ink-2);
  max-width: 620px; line-height: 1.7;
}
.vs-sphere-line strong { color: var(--vs-teal); }

/* --- the platform pillars --- */
.vs-pillars .vs-card > div { padding: 34px 32px; }
.vs-pillars .vs-card:hover > div { transform: translateY(-4px); }
.vs-k { font-family: var(--ff-mono); font-size: 12px; color: var(--vs-mute); }
.vs-t { font-weight: 700; font-size: 20px; margin-top: 12px; letter-spacing: -0.01em; }
.vs-pillars .vs-card p { font-size: 14.5px; line-height: 1.7; margin-top: 12px; }

/* ============================================================
   THE DARK SLAB — how a tenant comes alive
   ============================================================ */
.vs-tenant {
  background: var(--slab); color: var(--on-slab);
  margin-top: 120px;
  position: relative; overflow: hidden;
}
.vs-tenant .wrap { padding-block: 110px; position: relative; }
.vs-tenant .vs-eyebrow { color: var(--vs-cyan); }
.vs-tenant .vs-h2 {
  max-width: 640px;
  background: linear-gradient(90deg, var(--on-slab) 35%, var(--vs-cyan) 50%, var(--on-slab) 65%);
  background-size: 250% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  animation: vs-sweep 7s linear infinite;
}
/* two dashed rings drifting behind the slab, opposite directions */
.vs-orbit { position: absolute; border-radius: 50%; pointer-events: none; }
.vs-orbit i {
  position: absolute; top: -4px; left: 50%;
  width: 8px; height: 8px; margin-left: -4px;
  border-radius: 50%;
}
.vs-orbit-a {
  top: -180px; right: -140px; width: 480px; height: 480px;
  border: 1px dashed color-mix(in srgb, var(--vs-cyan) 25%, transparent);
  animation: vs-orbit 60s linear infinite;
}
.vs-orbit-a i { background: var(--vs-cyan); box-shadow: 0 0 12px var(--vs-cyan); }
.vs-orbit-b {
  bottom: -220px; left: -160px; width: 540px; height: 540px;
  border: 1px dashed color-mix(in srgb, var(--on-slab) 12%, transparent);
  animation: vs-orbit 90s linear infinite reverse;
}
.vs-orbit-b i { width: 6px; height: 6px; margin-left: -3px; top: -3px;
                background: color-mix(in srgb, var(--on-slab) 60%, transparent); }

/* the marching dashed rule above the three steps */
.vs-dashrule {
  height: 2px; margin: 54px 0 0;
  background: repeating-linear-gradient(90deg,
              color-mix(in srgb, var(--vs-cyan) 70%, transparent) 0 10px,
              transparent 10px 22px);
  animation: vs-dash 1s linear infinite;
}
.vs-tenant .vs-card {
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--vs-cyan) 10%, transparent),
              color-mix(in srgb, var(--vs-cyan) 60%, transparent),
              color-mix(in srgb, var(--vs-cyan) 10%, transparent));
  background-size: 300% 100%;
}
.vs-tenant .vs-card > div { background-color: var(--vs-card); padding: 32px 28px; }
.vs-tenant .vs-card:hover > div { transform: translateY(-4px); }
.vs-tenant .vs-card p { color: var(--on-slab-2); font-size: 14px; line-height: 1.7; }
/* the step number, inside its own orbiting dashed ring */
.vs-step-n { position: relative; width: 46px; height: 46px; }
.vs-step-n span {
  position: absolute; inset: 0;
  border: 1.5px dashed color-mix(in srgb, var(--vs-cyan) 50%, transparent);
  border-radius: 50%;
  animation: vs-orbit 8s linear infinite;
}
.vs-step-n b {
  position: absolute; inset: 5px;
  border-radius: 50%; border: 1.5px solid var(--vs-cyan);
  color: var(--vs-cyan);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--ff-mono); font-size: 14px; font-weight: 600;
}
.vs-step-t { font-weight: 700; font-size: 18px; margin-top: 20px; }
.vs-tenant-foot {
  margin: 44px 0 0; font-size: 15px;
  color: var(--on-slab-2); max-width: 640px; line-height: 1.7;
}

/* ============================================================
   MODULES — two belts running opposite ways
   ============================================================ */
.vs-modules { padding: 130px 0 20px; }
.vs-legend {
  display: flex; gap: 22px; margin-top: 34px; flex-wrap: wrap;
  font-family: var(--ff-mono); font-size: 11.5px; color: var(--ink-3);
}
.vs-legend b { font-weight: 400; }
.vs-legend .dot-live { color: var(--vs-live); }
.vs-legend .dot-road { color: var(--warm); }

/* A grid that wraps, in place of the two belts that used to scroll past.
   Live modules are listed first, then the roadmap, so the two halves of the
   legend read in order. */
.vs-modules-grid {
  margin-top: 34px;
  display: flex; flex-wrap: wrap; gap: 12px;
}
.vs-chip {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 13px 22px;
  font-size: 14.5px; font-weight: 600;
  white-space: nowrap;
  transition: border-color .25s ease, box-shadow .25s ease, color .25s ease;
  cursor: default;
}
.vs-chip:hover {
  border-color: var(--vs-teal); color: var(--vs-teal);
  box-shadow: 0 12px 26px color-mix(in srgb, var(--vs-teal) 14%, transparent);
  animation: vs-pop .45s ease;
}
.vs-chip i {
  width: 7px; height: 7px; border-radius: 50%;
  animation: vs-pulsedot 2.4s ease-in-out infinite;
}
.vs-chip .on-live { background: var(--vs-live); }
.vs-chip .on-road { background: var(--vs-road); }

/* ============================================================
   CTA
   ============================================================ */
.vs-demo { padding: 0 0 120px; margin-top: 110px; }
.vs-demo-box {
  position: relative; overflow: hidden;
  background: var(--slab);
  border-radius: 28px;
  padding: 90px 40px; text-align: center;
}
.vs-demo-box h2 { position: relative; margin: 0; font-size: clamp(30px, 3.6vw, 46px); color: var(--on-slab); }
.vs-demo-box p {
  position: relative; margin: 18px auto 0; max-width: 520px;
  font-size: 16px; line-height: 1.7;
  /* 85% (the reference's value) is 4.3:1 on the gradient's light end —
     90% clears AA for 16px body copy. */
  color: var(--on-slab-2);
}
.vs-demo-box .btn {
  position: relative; margin-top: 36px;
  background: var(--lime-b); color: var(--on-accent); border-color: transparent;
  padding: 16px 34px; font-weight: 700; box-shadow: none;
}
.vs-demo-box .btn:hover { color: var(--on-accent); }
.vs-orbit-c {
  top: -120px; right: -80px; width: 340px; height: 340px;
  border: 1.5px dashed color-mix(in srgb, var(--on-slab) 25%, transparent);
  animation: vs-orbit 45s linear infinite;
}
.vs-orbit-c i { width: 10px; height: 10px; margin-left: -5px; top: -5px;
                background: var(--surface); box-shadow: 0 0 14px color-mix(in srgb, var(--on-slab) 90%, transparent); }
.vs-orbit-d {
  bottom: -140px; left: -60px; width: 300px; height: 300px;
  border: 1.5px dashed color-mix(in srgb, var(--on-slab) 16%, transparent);
  animation: vs-orbit 70s linear infinite reverse;
}
.vs-orbit-d i { background: color-mix(in srgb, var(--on-slab) 80%, transparent); }

/* ============================================================
   REVEALS
   The wipe is armed by the script (body.vs-js), so with JavaScript
   blocked every section is simply visible instead of clipped to
   nothing — the reference hid them in the markup and left a blank
   page behind a failed script.

   The resting state is opacity alone, and the clip lives in the
   animation rather than in the hidden state. That is not a style choice:
   clip-path: inset(0 100% 0 0) makes an element zero-wide, and
   IntersectionObserver measures the clipped box — so a clipped element
   reports intersectionRatio 0 for ever and the observer that would
   un-clip it never fires its threshold. The page held that deadlock and
   nothing below the fold ever appeared.
   ============================================================ */
body.vs-js [data-reveal] {
  opacity: 0;
  transition: opacity .5s ease;
}
body.vs-js [data-reveal].vs-in {
  opacity: 1;
  animation: vs-reveal .9s cubic-bezier(.2, .7, .2, 1) both;
}
@keyframes vs-reveal {
  from { clip-path: inset(0 100% 0 0); opacity: 0; }
  to   { clip-path: inset(0 0 0 0);    opacity: 1; }
}

/* the click ripple, appended by the script */
.vs-ring {
  position: fixed; z-index: 60;
  border: 1.5px solid var(--vs-cyan);
  border-radius: 50%; pointer-events: none;
  animation: vs-ripple .8s ease-out forwards;
}
.vs-ring-local { position: absolute; z-index: 3; animation-duration: .9s; }

/* ============================================================
   THE SHARED CHROME, RE-TINTED
   ============================================================ */
body.vs .nav {
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
body.vs .footer { background: var(--bg); border-top: 1px solid var(--line); }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1040px) {
  .vs-grid-4 { grid-template-columns: repeat(2, 1fr); }
  .vs-stat-grid { grid-template-columns: repeat(2, 1fr); }
  .vs-stat:nth-child(3) { border-left: 0; }
}
/* 3-up card rows were still 3-up at 768px, which left the body copy in
   164px columns. They go single-column well before that now. */
@media (max-width: 880px) {
  .vs-grid-2, .vs-grid-3 { grid-template-columns: 1fr; }
  .vs-head-row { align-items: flex-start; }
}
@media (max-width: 760px) {
  body.vs main .wrap { padding-inline: 22px; }
  .vs-sec, .vs-sec-tight, .vs-modules { padding-top: 88px; }
  .vs-tenant { margin-top: 88px; }
  .vs-tenant .wrap { padding-block: 78px; }
  .vs-demo { margin-top: 78px; padding-bottom: 88px; }
  .vs-demo-box { padding: 60px 24px; border-radius: 22px; }
  .vs-hero-copy { padding-top: 96px; }
  .vs-lead { font-size: 17px; }
}
@media (max-width: 560px) {
  .vs-grid-4 { grid-template-columns: 1fr; }
  .vs-stat-grid { grid-template-columns: 1fr; }
  .vs-stat { border-left: 0; border-top: 1px solid var(--line); }
  .vs-stat:first-child { border-top: 0; }
  .vs-actions .btn { width: 100%; justify-content: center; }
}

/* ============================================================
   REDUCED MOTION
   Everything decorative stops. The reveals are neutralised too, so a
   section can never be left clipped to nothing.
   ============================================================ */
/* The hero invites a pointer it will not get on a phone.

   The touch wording is hidden unconditionally and switched on inside the
   query, rather than each variant being hidden by its own query. A device
   reporting hover:none with pointer:fine — some laptops, TV browsers,
   headless Chrome — matched neither, and printed both strings run together. */
.vs-hint .on-touch { display: none; }
@media (hover: none) and (pointer: coarse) {
  body.vs .vs-hero { cursor: default; }
  .vs-hint .on-pointer { display: none; }
  .vs-hint .on-touch { display: inline; }
}

@media (prefers-reduced-motion: reduce) {
  body.vs *,
  body.vs *::before,
  body.vs *::after {
    animation: none !important;
    transition: none !important;
  }
  body.vs .vs-shine,
  body.vs .vs-stat b,
  body.vs .vs-tenant .vs-h2 {
    background: none;
    -webkit-text-fill-color: currentColor;
    color: var(--ink);
  }
  body.vs .vs-tenant .vs-h2 { color: var(--on-slab); }
  body.vs-js [data-reveal] { opacity: 1; clip-path: none; animation: none; }
  /* the scan bar is only ever a moving highlight; parked, it reads as a
     stray gradient panel across the stats strip. */
  body.vs .vs-stats::after { display: none; }
}
