/* ==========================================================================
   Actra — reset, element defaults, backdrop, scroll-reveal primitives
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  min-height: 100vh;
  background: var(--ink-950);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

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

a { color: inherit; text-decoration: none; }

button, input, select, textarea {
  font: inherit;
  font-family: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
}

button { cursor: pointer; }

:focus-visible {
  outline: 1px solid var(--violet-400);
  outline-offset: 4px;
}

::selection {
  background: var(--violet-600);
  color: var(--paper);
}

h1, h2, h3, h4 {
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

p { text-wrap: pretty; }

/* --- Typographic roles ------------------------------------------------
   Archivo throughout. It carries a width axis, so display type runs expanded
   and heavy while body copy sits at normal width — one family, real contrast.
   -------------------------------------------------------------------- */

h1, h2, h3, h4, .display {
  font-family: var(--font-display);
  font-stretch: var(--wide-lg);
  font-weight: 600;
  line-height: 1.0;
  letter-spacing: -0.028em;
  text-wrap: balance;
}

.display em,
h1 em, h2 em, h3 em {
  font-style: italic;
  color: transparent;
  background: linear-gradient(96deg, var(--lilac-200) 0%, var(--violet-400) 58%, var(--violet-500) 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

p { text-wrap: pretty; }

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.lede {
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--text-dim);
  max-width: 46ch;
}

.mono {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.02em;
}

/* --- Layout primitives ------------------------------------------------ */

.shell {
  width: 100%;
  max-width: var(--shell-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.grid12 {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(16px, 2vw, 40px);
}

.rule {
  height: 1px;
  background: var(--line);
  border: 0;
}

.section {
  position: relative;
  padding-block: var(--s-11);
}

.page {
  position: relative;
  z-index: var(--z-content);
}

/* --- Backdrop: night city -------------------------------------------- */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-backdrop);
  pointer-events: none;
  overflow: hidden;
  background:
    radial-gradient(120% 78% at 50% 116%, #180f3f 0%, rgba(9, 6, 22, 0) 62%),
    linear-gradient(180deg, #05030d 0%, #080614 44%, #0a0620 100%);
}

.backdrop__bloom {
  position: absolute;
  inset: -25% -15% -35%;
}

.backdrop__bloom::before,
.backdrop__bloom::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  filter: blur(50px);
  will-change: transform;
}

.backdrop__bloom::before {
  left: 4%;
  bottom: 14%;
  width: 62vw;
  height: 46vw;
  background: radial-gradient(circle, rgba(90, 76, 251, 0.28) 0%, rgba(90, 76, 251, 0) 66%);
  animation: drift-a 46s var(--ease-io) infinite alternate;
}

.backdrop__bloom::after {
  right: -6%;
  bottom: 6%;
  width: 48vw;
  height: 38vw;
  background: radial-gradient(circle, rgba(59, 43, 212, 0.34) 0%, rgba(59, 43, 212, 0) 68%);
  animation: drift-b 62s var(--ease-io) infinite alternate;
}

@keyframes drift-a {
  from { transform: translate3d(-3%, 2%, 0) scale(1); }
  to   { transform: translate3d(7%, -4%, 0) scale(1.14); }
}

@keyframes drift-b {
  from { transform: translate3d(4%, 0, 0) scale(1.1); }
  to   { transform: translate3d(-6%, -6%, 0) scale(0.94); }
}

.backdrop__horizon {
  position: absolute;
  left: -10%;
  right: -10%;
  bottom: 0;
  height: 30vh;
  background: linear-gradient(180deg,
    rgba(124, 109, 255, 0) 0%,
    rgba(124, 109, 255, 0.12) 56%,
    rgba(182, 173, 255, 0.20) 100%);
  filter: blur(26px);
}

.backdrop__layer {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translate3d(-50%, 0, 0);
  will-change: transform;
}

.backdrop__layer--far  { opacity: 0.48; }
.backdrop__layer--mid  { opacity: 0.8; }
.backdrop__layer--near { opacity: 1; }

.backdrop__grain {
  position: absolute;
  inset: 0;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

.backdrop__veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(5, 3, 13, 0) 0%,
    rgba(5, 3, 13, 0.5) 34%,
    rgba(5, 3, 13, 0.95) 100%);
  opacity: var(--veil-o, 0);
  transition: opacity 220ms linear;
}

/* Interior pages run a quieter sky so long-form text stays legible. */
.backdrop[data-mode="quiet"] { --veil-o: 0.7; }
.backdrop[data-mode="quiet"] .backdrop__layer { opacity: 0.26; }
.backdrop[data-mode="quiet"] .backdrop__horizon { opacity: 0.45; }
.backdrop[data-mode="quiet"] .backdrop__bloom::before,
.backdrop[data-mode="quiet"] .backdrop__bloom::after { opacity: 0.55; }

/* --- Pointer light ----------------------------------------------------
   Lives in the fixed backdrop rather than inside a section, so it is never
   cut off at a section boundary — it only ever meets the viewport edge.
   -------------------------------------------------------------------- */

.pointerlight {
  position: absolute;
  top: 0;
  left: 0;
  width: 48rem;
  height: 48rem;
  margin: -24rem 0 0 -24rem;
  border-radius: 50%;
  background: radial-gradient(circle closest-side,
    rgba(124, 109, 255, 0.18) 0%,
    rgba(110, 96, 240, 0.075) 44%,
    rgba(124, 109, 255, 0) 72%);
  opacity: 0;
  transition: opacity 900ms var(--ease-out);
  will-change: transform;
  pointer-events: none;
}

.pointerlight.is-on { opacity: 1; }

/* --- Scroll-linked reveal --------------------------------------------
   --rv : entry progress   0 -> 1 as the element rises into view
   --rx : exit progress    0 -> 1 as it leaves through the top
   Both derive from scroll position, so the effect is fully reversible.
   Defaults are the "settled" state, so the page reads fine without JS.
   -------------------------------------------------------------------- */

[data-reveal] {
  --rv: 1;
  --rx: 0;
  --rv-y: 34px;
  opacity: calc(var(--rv) * (1 - var(--rx) * 0.92));
  transform:
    translate3d(0, calc((1 - var(--rv)) * var(--rv-y) - var(--rx) * 28px), 0)
    scale(calc(1 - var(--rx) * 0.035));
}

[data-reveal].is-live { will-change: transform, opacity; }

[data-reveal="soft"] { --rv-y: 16px; }
[data-reveal="lift"] { --rv-y: 60px; }

[data-reveal="blur"] {
  --rv-y: 26px;
  filter: blur(calc((1 - var(--rv)) * 8px + var(--rx) * 4px));
}

/* --- A11y ------------------------------------------------------------- */

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

.skip {
  position: absolute;
  top: 8px; left: 8px;
  z-index: 200;
  padding: 10px 14px;
  background: var(--violet-600);
  color: var(--paper);
  border-radius: var(--r-2);
  transform: translateY(-200%);
  transition: transform var(--t-fast) var(--ease-ui);
}

.skip:focus { transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
  .pointerlight { display: none; }
}

/* --- Stars & moon ------------------------------------------------------
   Both are rasterised once into a canvas, so nothing in the sky can blink.
   They sit behind the towers, which is what gives the backdrop its depth.
   -------------------------------------------------------------------- */

.backdrop__stars {
  position: absolute;
  left: 50%;
  top: -30px;
  transform: translate3d(-50%, 0, 0);
  will-change: transform;
}

.backdrop__moon {
  --moon: clamp(190px, 22vw, 380px);
  position: absolute;
  /* Parked in the gap between the headline column and the right-hand rail,
     and low enough that the towers eat its lower third. */
  top: clamp(110px, 35vh, 320px);
  left: 54%;
  margin-left: calc(var(--moon) / -2);
  width: var(--moon);
  height: var(--moon);
  will-change: transform;
}

.moon__glow,
.moon__halo {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
}

/* Wide bloom. It starts in the moon's own colour and only turns violet well
   away from the disc — a violet gradient hard against a bright disc is what
   reads as a dark ring around it. */
.moon__glow {
  width: 460%;
  height: 460%;
  background: radial-gradient(circle closest-side,
    rgba(212, 210, 224, 0.17) 0%,
    rgba(208, 205, 224, 0.13) 20%,
    rgba(196, 189, 232, 0.085) 27%,
    rgba(172, 158, 240, 0.05) 36%,
    rgba(142, 122, 244, 0.025) 50%,
    rgba(118, 100, 238, 0.01) 65%,
    rgba(104, 88, 226, 0) 80%);
}

/* Tight halo in the moon's own tone, picking up exactly where the disc starts
   dissolving. Same colour on both sides of the join, so there is no edge. */
.moon__halo {
  width: 240%;
  height: 240%;
  background: radial-gradient(circle closest-side,
    rgba(214, 212, 226, 0.34) 0%,
    rgba(214, 212, 226, 0.34) 34%,
    rgba(213, 210, 226, 0.25) 42%,
    rgba(210, 206, 226, 0.15) 50%,
    rgba(206, 200, 228, 0.08) 60%,
    rgba(200, 192, 230, 0.035) 72%,
    rgba(196, 186, 232, 0.012) 84%,
    rgba(196, 186, 232, 0) 100%);
}

.moon__disc {
  position: absolute;
  inset: 0;
}

/* The quieter sky on interior pages keeps them from competing with text. */
.backdrop[data-mode="quiet"] .backdrop__moon { opacity: 0.4; }
.backdrop[data-mode="quiet"] .backdrop__stars { opacity: 0.55; }

/* One column on small screens, so there is no gap to sit in: the moon drops
   to the tower line and dims, reading as atmosphere behind the copy. */
@media (max-width: 1080px) {
  .backdrop__moon {
    --moon: clamp(190px, 34vw, 340px);
    top: clamp(120px, 38vh, 340px);
    left: 62%;
  }
}

@media (max-width: 760px) {
  .backdrop__moon {
    --moon: clamp(180px, 56vw, 310px);
    /* Dropped to the tower line: the skyline occludes most of it, so the
       copy above never has to compete with a lit disc. */
    top: clamp(340px, 52vh, 470px);
    left: 70%;
  }

  /* The scroll arc writes opacity on the wrapper, so the small-screen dimming
     has to sit on the children — the two then multiply instead of clashing. */
  .moon__disc,
  .moon__glow { opacity: 0.5; }
}

/* --- Pointer light ----------------------------------------------------
   Lives in the fixed backdrop rather than inside a section, so it is never
   cut off at a section boundary — it only ever meets the viewport edge.
   -------------------------------------------------------------------- */

.pointerlight {
  position: absolute;
  top: 0;
  left: 0;
  width: 48rem;
  height: 48rem;
  margin: -24rem 0 0 -24rem;
  border-radius: 50%;
  background: radial-gradient(circle closest-side,
    rgba(124, 109, 255, 0.18) 0%,
    rgba(110, 96, 240, 0.075) 44%,
    rgba(124, 109, 255, 0) 72%);
  opacity: 0;
  transition: opacity 900ms var(--ease-out);
  will-change: transform;
  pointer-events: none;
}

.pointerlight.is-on { opacity: 1; }

/* --- Scroll-linked reveal --------------------------------------------
   --rv : entry progress   0 -> 1 as the element rises into view
   --rx : exit progress    0 -> 1 as it leaves through the top
   Both derive from scroll position, so the effect is fully reversible.
   Defaults are the "settled" state, so the page reads fine without JS.
   -------------------------------------------------------------------- */

[data-reveal] {
  --rv: 1;
  --rx: 0;
  --rv-y: 34px;
  opacity: calc(var(--rv) * (1 - var(--rx) * 0.92));
  transform:
    translate3d(0, calc((1 - var(--rv)) * var(--rv-y) - var(--rx) * 28px), 0)
    scale(calc(1 - var(--rx) * 0.035));
}

[data-reveal].is-live { will-change: transform, opacity; }

[data-reveal="soft"] { --rv-y: 16px; }
[data-reveal="lift"] { --rv-y: 60px; }

[data-reveal="blur"] {
  --rv-y: 26px;
  filter: blur(calc((1 - var(--rv)) * 8px + var(--rx) * 4px));
}

/* --- A11y ------------------------------------------------------------- */

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

.skip {
  position: absolute;
  top: 8px; left: 8px;
  z-index: 200;
  padding: 10px 14px;
  background: var(--violet-600);
  color: var(--paper);
  border-radius: var(--r-2);
  transform: translateY(-200%);
  transition: transform var(--t-fast) var(--ease-ui);
}

.skip:focus { transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
  .pointerlight { display: none; }
}
