/* ==========================================================================
   CORE — tokens, reset, typography, chrome.

   Three stylesheets, no build step. This one holds everything both page
   types share: colour and type scales, the fixed background, the header,
   the index rail, the reveal choreography, and the footer.

   Three rules run through all of it.

   1. Nothing is ever painted on top of the text. Every atmospheric layer
      lives below --z-page, so a headline is white on near-black and stays
      that way.

   2. Motion is composited or it does not ship. The background moves with
      transform only, reveals are transform + opacity, and nothing measures
      layout on a frame.

   3. Few things, large. Small type, tiny boxes and moving decoration all
      compete with the content. Where a choice existed between more
      elements and bigger ones, this file picks bigger ones.
   ========================================================================== */

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

:root {
  /* Ground. Near-black is the contrast floor that keeps white type legible
     no matter what the gradient above it is doing. */
  --ink:        #08080b;
  --ink-raised: #0e0e13;
  --ink-sunk:   #050507;

  /* Type. Percentages of white, so contrast is predictable. These are all
     deliberately high: the previous scale bottomed out at 30% white, which
     is a hairline on a dark ground, not text. */
  --paper:     #ffffff;
  --paper-88:  rgba(255, 255, 255, 0.88);
  --paper-74:  rgba(255, 255, 255, 0.74);
  --paper-60:  rgba(255, 255, 255, 0.60);
  --paper-46:  rgba(255, 255, 255, 0.46);

  /* One signal colour. State only, never body text. */
  --signal:      #ff5a2c;
  --signal-lift: #ff7a52;
  --signal-dim:  rgba(255, 90, 44, 0.16);

  /* Rules. Two weights. */
  --line:      rgba(255, 255, 255, 0.16);
  --line-soft: rgba(255, 255, 255, 0.09);

  /* Families */
  --display: "Bricolage Grotesque", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --body:    "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono:    "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Type scale. Everything moved up a step; the old scale was set for a
     laptop and read as fine print on a large monitor. */
  --t-mega:  clamp(3rem, 12vw, 15rem);
  --t-xl:    clamp(2.5rem, 5.4vw, 4.75rem);
  --t-lg:    clamp(2rem, 3.4vw, 3.25rem);
  --t-md:    clamp(1.5rem, 2vw, 2rem);
  --t-sm:    clamp(1.2rem, 1.5vw, 1.5rem);
  --t-body:  clamp(1.0625rem, 0.32vw + 1rem, 1.1875rem);
  --t-label: 0.8125rem;

  /* Layout. There is no max width any more. The page is measured from the
     window and inset by a gutter, so a large monitor gets a large site
     instead of a narrow column stranded in the middle of the screen.
     Line length is held by max-width in ch on the text itself, which is
     where it belongs. */
  --gutter: clamp(1.25rem, 3.4vw, 4.5rem);
  --rail-w: 17rem;
  --rail-gap: 4rem;
  --head-h: 5.25rem;

  /* Rhythm */
  --band-y: clamp(6rem, 14vh, 12rem);

  /* Motion */
  --ease:    cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in: cubic-bezier(0.62, 0.05, 0.36, 1);

  /* Stacking. Atmosphere below, page above, chrome on top. */
  --z-bg:       0;
  --z-page:     2;
  --z-rail:     40;
  --z-head:     60;
  --z-progress: 70;
  --z-skip:     100;
}

/* ------------------------------------------------------------------- reset */

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

html {
  /* clip, not hidden: hidden on the root turns position:sticky into a
     scroll container and every sticky element on the site stops sticking. */
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--ink);
  color: var(--paper-74);
  font-family: var(--body);
  font-size: var(--t-body);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

h1, h2, h3, h4, p, figure, blockquote, dl, dd, ol, ul { margin: 0; }
ol, ul { padding: 0; list-style: none; }
img, svg, video { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }

:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; border-radius: 2px; }
::selection { background: var(--signal); color: #fff; }

.skip-link {
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: var(--z-skip);
  padding: 0.75rem 1.2rem;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: 3px;
  transform: translateY(-160%);
  transition: transform 0.24s var(--ease);
}
.skip-link:focus-visible { transform: none; }

/* -------------------------------------------------------------- background

   A calm gradient and nothing else. Three wide radial fields on a
   near-black ground, each drifting on its own period so they never line up
   twice. Only `transform` animates, so the browser rasterises each field
   once and composites it from then on: no repaint, no layout, no
   measurable cost on a frame.                                              */

.bg {
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  pointer-events: none;
  overflow: hidden;
  background:
    radial-gradient(140% 90% at 50% -10%, #101019 0%, transparent 60%),
    linear-gradient(180deg, var(--ink) 0%, var(--ink-sunk) 100%);
}

.bg__field {
  position: absolute;
  border-radius: 50%;
  will-change: transform;
  /* No blur filter anywhere: a radial gradient is already soft, and a large
     blur is the one paint cost that would undo the point of this. */
}

.bg__field--a {
  width: 92vmax; height: 92vmax;
  top: -34vmax; left: -22vmax;
  background: radial-gradient(circle, rgba(86, 106, 255, 0.20) 0%, rgba(86, 106, 255, 0.07) 38%, transparent 68%);
  animation: driftA 61s var(--ease-in) infinite alternate;
}

.bg__field--b {
  width: 78vmax; height: 78vmax;
  bottom: -32vmax; right: -20vmax;
  background: radial-gradient(circle, rgba(255, 90, 44, 0.17) 0%, rgba(255, 90, 44, 0.06) 40%, transparent 70%);
  animation: driftB 79s var(--ease-in) infinite alternate;
}

.bg__field--c {
  width: 66vmax; height: 66vmax;
  top: 22%; left: 38%;
  background: radial-gradient(circle, rgba(140, 96, 236, 0.13) 0%, rgba(140, 96, 236, 0.04) 42%, transparent 72%);
  animation: driftC 97s var(--ease-in) infinite alternate;
}

@keyframes driftA {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(14vw, 9vh, 0) scale(1.16); }
}
@keyframes driftB {
  from { transform: translate3d(0, 0, 0) scale(1.1); }
  to   { transform: translate3d(-13vw, -8vh, 0) scale(1); }
}
@keyframes driftC {
  from { transform: translate3d(-8vw, 4vh, 0) scale(0.94); }
  to   { transform: translate3d(9vw, -7vh, 0) scale(1.12); }
}

/* One low vignette keeps the corners quiet so the rail and the header
   always sit on the darkest part of the frame. */
.bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 80% at 50% 45%, transparent 30%, rgba(0, 0, 0, 0.58) 100%);
}

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

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

/* The measure. Window width less a gutter, with no cap — a large screen
   gets a large site. */
.wrap {
  width: calc(100% - var(--gutter) * 2);
  margin-inline: auto;
}

@media (min-width: 1200px) {
  .wrap { padding-left: calc(var(--rail-w) + var(--rail-gap)); }
}

/* A band is one numbered chapter of the page. */
.band {
  position: relative;
  padding-block: var(--band-y);
  scroll-margin-top: var(--head-h);
}
.band + .band { border-top: 1px solid var(--line-soft); }

/* --------------------------------------------------------------- typography

   Bricolage is set at full width rather than condensed. Squeezed display
   type reads as pressure; at 100% it reads as confidence, and it is easier
   on the eye at every size.                                                */

.display {
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 100%;
  line-height: 0.94;
  letter-spacing: -0.03em;
  color: var(--paper);
  text-wrap: balance;
}

.display--mega {
  font-size: var(--t-mega);
  letter-spacing: -0.04em;
  line-height: 0.88;
}

.h2 {
  font-family: var(--display);
  font-weight: 650;
  font-stretch: 100%;
  font-size: var(--t-lg);
  line-height: 1.04;
  letter-spacing: -0.024em;
  color: var(--paper);
  text-wrap: balance;
}

.h3 {
  font-family: var(--display);
  font-weight: 600;
  font-stretch: 100%;
  font-size: var(--t-md);
  line-height: 1.16;
  letter-spacing: -0.015em;
  color: var(--paper);
}

.lede {
  font-size: var(--t-sm);
  line-height: 1.5;
  color: var(--paper-88);
  max-width: 40ch;
  text-wrap: pretty;
}

.prose p {
  max-width: 60ch;
  color: var(--paper-74);
  text-wrap: pretty;
}
.prose p + p { margin-top: 1.1em; }
.prose--lead p { color: var(--paper-88); font-size: var(--t-sm); line-height: 1.5; }

.pull {
  font-family: var(--display);
  font-weight: 500;
  font-stretch: 100%;
  font-size: clamp(1.5rem, 2.3vw, 2.25rem);
  line-height: 1.24;
  letter-spacing: -0.018em;
  color: var(--paper);
  max-width: 24ch;
  padding-left: clamp(1.25rem, 2vw, 2rem);
  border-left: 2px solid var(--signal);
  text-wrap: pretty;
}

.mono { font-family: var(--mono); font-size: 0.9375rem; letter-spacing: -0.01em; }
.tabular { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* Labels. Bigger and less tracked out than before — 11px at 0.16em is a
   texture, not a word you can read. */
.micro {
  font-family: var(--mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--paper-60);
  line-height: 1.4;
}

.hot { color: var(--signal); }
em, .w--em { font-style: normal; color: var(--paper); }

/* Section label: number, name, then a rule running to the edge. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 1rem;
  font-family: var(--mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-74);
  margin-bottom: clamp(2rem, 4vw, 3.25rem);
}

.eyebrow__num { color: var(--signal); font-variant-numeric: tabular-nums; }

.eyebrow__rule {
  flex: 1;
  height: 1px;
  background: var(--line-soft);
  transform-origin: left;
}

/* ----------------------------------------------------------------- controls */

.link {
  color: var(--paper);
  background-image: linear-gradient(var(--signal), var(--signal));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 1px;
  transition: background-size 0.3s var(--ease), color 0.3s var(--ease);
}
.link:hover { color: var(--signal-lift); background-size: 100% 2px; }

/* Stack lists. These used to be a scatter of small bordered pills; one
   line of separated words says the same thing and reads at a glance. */
.tags {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.9rem;
  font-family: var(--mono);
  font-size: 0.875rem;
  color: var(--paper-60);
}
.tags li { display: inline-flex; align-items: baseline; gap: 0.9rem; }
.tags li:not(:first-child)::before { content: "/"; color: var(--paper-46); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper-88);
}
.chip::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
}
.chip--live   { color: var(--signal); }
.chip--run    { color: #6fd3a8; }
.chip--active { color: #86adff; }

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

.head {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-head);
  height: var(--head-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-inline: var(--gutter);
  /* A real backdrop rather than a blend mode: the wordmark used to invert
     itself against white headlines, and a gradient scrim cannot. */
  background: linear-gradient(180deg, rgba(8, 8, 11, 0.94) 0%, rgba(8, 8, 11, 0.74) 58%, transparent 100%);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  font-family: var(--display);
  font-weight: 650;
  font-stretch: 100%;
  font-size: 1.15rem;
  letter-spacing: -0.02em;
  color: var(--paper);
}

.wordmark__dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--signal);
}

.nav {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 2.4vw, 2.5rem);
  font-family: var(--mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.nav__link {
  position: relative;
  color: var(--paper-74);
  padding-block: 0.4rem;
  transition: color 0.28s var(--ease);
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--signal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.34s var(--ease);
}
.nav__link:hover { color: var(--paper); }
.nav__link:hover::after,
.nav__link[aria-current="true"]::after { transform: scaleX(1); }
.nav__link[aria-current="true"] { color: var(--paper); }

.readout { font-variant-numeric: tabular-nums; color: var(--paper-46); }
@media (max-width: 640px) { .readout { display: none; } }

/* ---------------------------------------------------------- progress line */

.progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  z-index: var(--z-progress);
  pointer-events: none;
}

.progress__fill {
  height: 100%;
  transform-origin: left;
  transform: scaleX(var(--p, 0));
  background: var(--signal);
}

/* -------------------------------------------------------------- index rail

   Sits on the left edge of the same box the content uses, so on a wide
   monitor it is beside the page rather than stranded far away from it.     */

.rail { display: none; }

@media (min-width: 1200px) {
  .rail {
    position: fixed;
    left: 0; right: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: var(--z-rail);
    display: block;
    pointer-events: none;
  }

  /* Measured exactly like .wrap, so the rail and the content share an edge. */
  .rail__box {
    width: calc(100% - var(--gutter) * 2);
    margin-inline: auto;
  }

  .rail__col {
    position: relative;
    width: var(--rail-w);
    padding-left: 1.5rem;
    border-left: 1px solid var(--line-soft);
    pointer-events: auto;
  }

  /* The spine, filling with overall scroll progress. */
  .rail__col::before {
    content: "";
    position: absolute;
    left: -1px;
    top: 0;
    width: 1px;
    height: 100%;
    background: var(--signal);
    transform-origin: top;
    transform: scaleY(var(--p, 0));
  }

  .rail__head {
    padding-bottom: 1rem;
    margin-bottom: 1rem;
    border-bottom: 1px solid var(--line-soft);
  }

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

  .rail__item {
    display: grid;
    grid-template-columns: 2.4rem 1fr;
    align-items: baseline;
    gap: 0.5rem;
    padding-block: 0.6rem;
    font-family: var(--mono);
    font-size: 1rem;
    letter-spacing: -0.01em;
    color: var(--paper-46);
    transition: color 0.3s var(--ease);
  }

  .rail__num {
    font-variant-numeric: tabular-nums;
    font-size: 0.8125rem;
    color: var(--paper-46);
    transition: color 0.3s var(--ease);
  }

  .rail__label {
    position: relative;
    justify-self: start;
    transition: transform 0.36s var(--ease);
  }

  .rail__item:hover { color: var(--paper); }
  .rail__item:hover .rail__label { transform: translateX(3px); }

  .rail__item[aria-current="true"] { color: var(--paper); }
  .rail__item[aria-current="true"] .rail__num { color: var(--signal); }
  .rail__item[aria-current="true"] .rail__label::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: -4px;
    height: 1px;
    background: var(--signal);
  }
}

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

.foot {
  position: relative;
  z-index: var(--z-page);
  border-top: 1px solid var(--line);
  background: rgba(5, 5, 7, 0.72);
  padding-block: 2.75rem;
}

.foot__inner {
  width: calc(100% - var(--gutter) * 2);
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2.5rem;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: 0.875rem;
  /* The footer is content, not decoration. It used to be set at 30% white
     and was effectively invisible. */
  color: var(--paper-74);
}
.foot__inner a { color: var(--paper); }
.foot__inner a:hover { color: var(--signal-lift); }

@media (min-width: 1200px) {
  .foot__inner { padding-left: calc(var(--rail-w) + var(--rail-gap)); }
}

/* ================================================================= reveals

   Applied only under the `js` class, which the page sets itself, so with
   scripting off the markup renders complete and fully visible.             */

.js [data-rise] {
  opacity: 0;
  transform: translate3d(0, 24px, 0);
  transition:
    opacity 0.66s var(--ease) var(--d, 0ms),
    transform 0.72s var(--ease) var(--d, 0ms);
}
.js [data-rise].is-in { opacity: 1; transform: none; }

.js [data-wipe] { overflow: hidden; }
.js [data-wipe] > * {
  opacity: 0;
  transform: translate3d(0, 100%, 0);
  transition: opacity 0.5s var(--ease), transform 0.62s var(--ease);
}
.js [data-wipe] > *:nth-child(2) { transition-delay: 60ms; }
.js [data-wipe].is-in > * { opacity: 1; transform: none; }

.js [data-wipe] .eyebrow__rule { transform: translate3d(0, 0, 0) scaleX(0); }
.js [data-wipe].is-in .eyebrow__rule {
  transform: scaleX(1);
  transition: transform 0.9s var(--ease) 0.18s;
}

/* Line-split text. reveal.js wraps each visual line in .split__line and
   masks it; the inner span is what moves. */
.split__line {
  display: block;
  overflow: hidden;
  /* Descenders get clipped by an exact-height mask, so give it slack and
     pull the same amount back. */
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}

.split__inner { display: block; }

.js .split .split__inner {
  opacity: 0;
  transform: translate3d(0, 105%, 0);
  transition:
    opacity 0.6s var(--ease) var(--d, 0ms),
    transform 0.94s var(--ease) var(--d, 0ms);
}
.js .split.is-in .split__inner { opacity: 1; transform: none; }

.w { display: inline-block; }

/* Per-character entrance, short display strings only. */
.split--chars .c { display: inline-block; white-space: pre; }
.js .split--chars .split__inner { transform: none; opacity: 1; transition: none; }
.js .split--chars .c {
  opacity: 0;
  transform: translate3d(0, 0.42em, 0);
  transition:
    opacity 0.5s var(--ease) var(--cd, 0ms),
    transform 0.78s var(--ease) var(--cd, 0ms);
}
.js .split--chars.is-in .c { opacity: 1; transform: none; }

/* ------------------------------------------------------------------- a11y */

@media (prefers-reduced-motion: reduce) {
  .bg__field { animation: none !important; }
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .js [data-rise],
  .js .split .split__inner,
  .js .split--chars .c,
  .js [data-wipe] > * {
    opacity: 1 !important;
    transform: none !important;
  }
  .js .eyebrow__rule { transform: scaleX(1) !important; }
}

@media print {
  .bg, .rail, .progress, .head { display: none !important; }
  body { background: #fff; color: #000; }
  .wrap, .foot__inner { padding-left: 0; }
}
