/* ============================================================================
   CORIOLIS ONE design system
   ----------------------------------------------------------------------------
   Three layers: tokens, primitives, utilities.

   Served from /static/coriolis.css. Mission Control is the first consumer;
   the six domain pages migrate onto it one at a time, each dropping its own
   inline <style> and the Tailwind CDN as it goes.

   Type roles:
     display  Schibsted Grotesk   headlines, metric values. Variable 400-900.
     body     IBM Plex Sans       body copy, labels, UI. Variable 100-700.
     data     IBM Plex Mono       figures, units, coordinates, badges. Static.

   Self-hosted because the homepage opens on a timed entrance sequence, and
   two extra origins on the critical path make its start time depend on the
   network rather than on us.
   ========================================================================= */

/* ── Fonts ────────────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Schibsted Grotesk';
  src: url('/static/fonts/schibsted-grotesk-var.woff2') format('woff2-variations');
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}
/* Space Grotesk (first attempt at the mission-control direction) turned out
   to be the wrong reference: it's an open, evenly-spaced grotesk with wide
   letterforms, not what SpaceX's own site actually uses. The real thing is
   a heavily condensed, tightly kerned industrial grotesk -- tall, narrow
   letters, near-zero counter space. Oswald is the standard open-source
   stand-in for that exact look (condensed Alternate-Gothic-style caps),
   kept self-hosted like every other typeface here. Schibsted Grotesk stays
   defined and available even though nothing currently points at it. */
@font-face {
  font-family: 'Oswald';
  src: url('/static/fonts/oswald-var.woff2') format('woff2-variations');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Sans VF';
  src: url('/static/fonts/ibm-plex-sans-var.woff2') format('woff2-variations');
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/static/fonts/ibm-plex-mono-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/static/fonts/ibm-plex-mono-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ── Tokens ───────────────────────────────────────────────────────────────── */

:root {
  /* Ground. True black, not a dark-navy tint -- the platform's own base,
     independent of any domain's accent, so every domain's colour (and the
     white backglow below) reads against pure black rather than competing
     with a blue cast baked into the ground itself. */
  --bg:            #000000;
  --bg-raised:     #0d0d0d;
  --surface:       rgba(255, 255, 255, 0.042);
  --surface-hi:    rgba(255, 255, 255, 0.070);
  --line:          rgba(255, 255, 255, 0.13);
  --line-hi:       rgba(255, 255, 255, 0.26);

  /* Text ramp. Every value here clears 4.5:1 on --bg except --fg-faint,
     which is reserved for non-essential metadata at 12px and up. */
  --fg:            #eef2f8;
  --fg-muted:      #a8b3c4;
  --fg-dim:        #7c8799;
  --fg-faint:      #5a6376;

  /* Domain accents. These are the product's real identity colours and are
     deliberately unchanged from the existing site and the map markers. */
  --fisheries:     #00d4ff;
  --freshwater:    #3b82f6;
  --carbon:        #f59e0b;
  --forests:       #10b981;
  --mining:        #a8998a;
  --orbital:       #7c3aed;

  /* Status. Semantic, and kept separate from the accent hues on purpose:
     a zone's health must never be confused with which domain it belongs to. */
  --critical:      #ef4444;
  --warning:       #f59e0b;
  --stable:        #10b981;

  /* The single accent that carries interactive affordance -- Mission
     Control's own colour, deliberately distinct from any one domain's.
     This used to just be fisheries' cyan borrowed wholesale, which read as
     arbitrary favouritism on the one page meant to represent all six
     domains equally (the exact bias already fixed once in the domain CTAs
     and copy -- this closes the same gap in the colour system). A hot
     magenta was tried here and reads as too loud/branded a colour choice
     against SpaceX's own near-monochrome palette -- bright white, carried
     by glow rather than hue, reads as instrument light instead of a
     marketing accent, and still stays clear of every domain colour. */
  --accent:        #ffffff;
  --accent-ink:    #0a0a0a;

  /* The logo's own colour (Rotor Blue, assets/brand/coriolis-one.tokens.json).
     Not the site's general accent -- that's --accent, kept white on purpose
     (see above) -- this is specifically for moments that should read as
     "the brand mark's colour", like the hero gradient below, so they still
     feel connected to the mark itself now that it carries a real colour. */
  --brand-blue:    #2f7dff;

  /* Type */
  --font-display: 'Oswald', 'Segoe UI', system-ui, sans-serif;
  --font-body:    'IBM Plex Sans VF', 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, 'Cascadia Code', Consolas, monospace;

  --step--2: 0.6875rem;  /* 11px  micro labels */
  --step--1: 0.8125rem;  /* 13px  captions, chips */
  --step-0:  0.9375rem;  /* 15px  body */
  --step-1:  1.125rem;   /* 18px  lead */
  --step-2:  1.5rem;     /* 24px  tile headings */
  --step-3:  2rem;       /* 32px  section headings */
  --step-4:  clamp(2.25rem, 4.2vw, 3.5rem);   /* hero */

  /* Space. Dense end of the scale: this is a dashboard, not a brochure. */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;

  --r-sm: 8px; --r-md: 14px; --r-lg: 20px; --r-pill: 999px;

  /* Motion */
  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
  --dur-fast:    140ms;
  --dur-mid:     260ms;
  --dur-slow:    520ms;

  --maxw: 1440px;
}

/* ── Reset ────────────────────────────────────────────────────────────────── */

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

/* Smooth scroll for in-page anchors. The reduced-motion block near the end of
   this file overrides it back to auto, so this never fights that preference. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

/* Anchor targets clear the sticky nav instead of landing underneath it. */
[id] { scroll-margin-top: 84px; }

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.6;
  font-weight: 400;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.025em;
  text-wrap: balance;
}

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
img, svg, canvas, iframe { display: block; max-width: 100%; }

/* Every interactive element gets a visible ring. The old page set
   outline:none on its only form input with nothing in its place, which made
   the waitlist unreachable by keyboard. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  box-shadow: 0 0 12px rgba(255, 255, 255, 0.5);
  border-radius: var(--r-sm);
}

::selection { background: rgba(255, 255, 255, 0.24); color: #000; }

/* ── Atmosphere ───────────────────────────────────────────────────────────── */
/* Pre-blurred radial gradients, never filter: blur(). A blur(120px) on a
   600px element is the single most expensive thing this design could do on
   a phone; a soft-stopped gradient costs nothing and looks the same. */

.atmos {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
/* Corona-style, dialed back: bigger and dimmer than the first pass, and
   pushed further off-canvas at every edge. The point is a true-black page
   that reads as black first, with the glow felt at the margins rather
   than seen as a wash across the middle of the content. */
.atmos span {
  position: absolute;
  border-radius: 50%;
  opacity: 0.38;
}
.atmos .a1 {
  width: 78vw; height: 78vw; top: -34vw; left: -26vw;
  background: radial-gradient(circle closest-side, rgba(0, 212, 255, 0.13), rgba(0, 212, 255, 0) 70%);
}
.atmos .a2 {
  width: 70vw; height: 70vw; top: 6%; right: -32vw;
  background: radial-gradient(circle closest-side, rgba(124, 58, 237, 0.13), rgba(124, 58, 237, 0) 70%);
}
.atmos .a3 {
  width: 64vw; height: 64vw; bottom: -30vw; left: 14%;
  background: radial-gradient(circle closest-side, rgba(245, 158, 11, 0.08), rgba(245, 158, 11, 0) 70%);
}
.atmos .a4 {
  width: 58vw; height: 58vw; bottom: 2%; right: -8vw;
  background: radial-gradient(circle closest-side, rgba(16, 185, 129, 0.08), rgba(16, 185, 129, 0) 70%);
}
/* The white backglow: a wide, soft, low-opacity light source bathing the
   top of the page, independent of any domain's own accent colour so it
   reads as ambient light on true black rather than a fifth competing hue.
   Kept faint and wide on purpose -- this is meant to be felt more than
   seen, the same way the real thing works in the reference. */
.atmos .a5 {
  width: 86vw; height: 50vw; top: -24vw; left: 7vw;
  background: radial-gradient(circle closest-side, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0) 70%);
}

/* Ambient drift only runs once the entrance has settled, so the two never
   contend for the compositor. */
.settled .atmos .a1 { animation: drift-a 22s var(--ease-out) infinite alternate; }
.settled .atmos .a2 { animation: drift-b 27s var(--ease-out) infinite alternate; }
.settled .atmos .a3 { animation: drift-c 31s var(--ease-out) infinite alternate; }
.settled .atmos .a4 { animation: drift-b 25s var(--ease-out) infinite alternate-reverse; }
.settled .atmos .a5 { animation: drift-c 26s var(--ease-out) infinite alternate-reverse; }

@keyframes drift-a { to { transform: translate3d(4vw, 3vw, 0) scale(1.10); } }
@keyframes drift-b { to { transform: translate3d(-3vw, 4vw, 0) scale(1.07); } }
@keyframes drift-c { to { transform: translate3d(3vw, -3vw, 0) scale(1.12); } }

/* ── Layout ───────────────────────────────────────────────────────────────── */

.shell { position: relative; z-index: 1; }
.wrap  { max-width: var(--maxw); margin-inline: auto; padding: var(--sp-5) var(--sp-5) var(--sp-8); }

.bento {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--sp-4);
  /* Every shorter tile in a row now fills to match its tallest row-mate.
     align-items:start was the actual cause of a recurring class of gap: the
     row track height is always the tallest item regardless of this setting,
     so a shorter sibling under :start just sat at the top of that track and
     left bare page background below it. Fixed here rather than by hardcoding
     another one-off pixel height, since the same bug kept resurfacing in
     every new tile pair (hero/state, globe/rail, comparison/narrative,
     market/revenue) as the page grew. Tiles with a fixed height (not
     min-height) are unaffected: stretch only grows an item whose size is
     auto. */
  align-items: stretch;
}

/* ── Primitives ───────────────────────────────────────────────────────────── */

.tile {
  position: relative;
  overflow: hidden;
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background:
    linear-gradient(160deg, rgba(255,255,255,0.055), rgba(255,255,255,0.012) 42%),
    rgba(12, 12, 12, 0.62);
}

/* Hairline light catching the top-left edge. Gives the tile a physical edge
   without a heavy border. */
.tile::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(155deg, rgba(255,255,255,0.20), rgba(255,255,255,0) 45%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* Real backdrop blur is reserved for the nav and a couple of hero tiles.
   Applying it to all twenty would drop frames on mobile Safari. */
.tile-glass {
  -webkit-backdrop-filter: blur(22px);
          backdrop-filter: blur(22px);
  background:
    linear-gradient(160deg, rgba(255,255,255,0.075), rgba(255,255,255,0.02) 42%),
    rgba(12, 12, 12, 0.45);
}

.tile-pad-sm { padding: var(--sp-4); }

.tile-label {
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
.tile-sub {
  font-size: var(--step--2);
  color: var(--fg-faint);
  margin-top: 2px;
}

/* Status readout: dot + mono uppercase label + a hairline rule filling the
   remaining width. Mission-control instrument-panel language for the
   eyebrow line above a hero headline -- replaces a pair of .pill badges,
   which read as marketing-site chrome rather than a live status line. */
.status-readout {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.status-readout .dot {
  width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0;
  background: var(--stable);
  box-shadow: 0 0 6px var(--stable);
}
.status-readout .rule {
  flex: 1 1 auto;
  height: 1px;
  background: var(--line);
  min-width: 24px;
}

/* Chips */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  font-size: var(--step--2);
  font-weight: 600;
  color: var(--fg-muted);
  white-space: nowrap;
  transition: color var(--dur-fast), border-color var(--dur-fast), background var(--dur-fast);
}
.chip:hover { color: var(--fg); border-color: var(--line-hi); }
.chip[aria-pressed="true"] {
  color: var(--accent-ink); background: var(--accent); border-color: var(--accent);
  box-shadow: 0 0 14px rgba(255, 255, 255, 0.45);
}
.chip .dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: currentColor; }

/* Buttons. Minimum 44px tall for touch. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 0 var(--sp-5);
  /* Mission-control chrome: near-square, not the pill/rounded corner the
     rest of the site's smaller controls (.chip, .seg, .pill) still use --
     the sharper corner is what reads as instrument-panel hardware rather
     than a marketing-site button. */
  border-radius: 2px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: var(--step--1);
  font-weight: 600;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast), border-color var(--dur-fast);
}
.btn:active { transform: translateY(1px); }
.btn-primary {
  background: var(--fg);
  color: #000000;
  box-shadow: 0 0 0 rgba(255, 255, 255, 0);
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast), box-shadow var(--dur-mid);
}
/* White backglow made tangible on the one element every visitor's eye
   lands on first: a soft white halo plus a thin bright rim, not just a
   colour swap. */
.btn-primary:hover { background: #fff; box-shadow: 0 0 28px rgba(255, 255, 255, 0.35), 0 0 4px rgba(255, 255, 255, 0.6); }
.btn-ghost { border: 1px solid var(--line-hi); color: var(--fg); }
.btn-ghost:hover { background: var(--surface-hi); }

/* Segmented control (zone tabs, before/after switch) */
.seg {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, 0.28);
}
.seg button {
  min-height: 32px;
  padding: 0 var(--sp-4);
  border-radius: var(--r-pill);
  font-size: var(--step--2);
  font-weight: 600;
  color: var(--fg-dim);
  transition: color var(--dur-fast), background var(--dur-fast);
}
.seg button:hover { color: var(--fg); }
.seg button[aria-selected="true"] { color: var(--fg); background: var(--surface-hi); }

/* Bars */
.bar { height: 6px; border-radius: var(--r-pill); background: rgba(255,255,255,0.07); overflow: hidden; }
.bar > i { display: block; height: 100%; border-radius: inherit; transition: width var(--dur-slow) var(--ease-out); }

/* Status pills */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.pill-live     { background: rgba(16,185,129,0.16);  color: var(--stable); }
.pill-partial  { background: rgba(245,158,11,0.16);  color: var(--warning); }
.pill-static   { background: rgba(255,255,255,0.07); color: var(--fg-dim); }
.pill-none     { background: rgba(239,68,68,0.16);   color: var(--critical); }
.pill-critical { background: rgba(239,68,68,0.16);   color: var(--critical); }
.pill-warning  { background: rgba(245,158,11,0.16);  color: var(--warning); }
.pill-stable   { background: rgba(16,185,129,0.16);  color: var(--stable); }

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

/* ── Entrance ─────────────────────────────────────────────────────────────── */
/* The hidden start state exists ONLY under .anim, which an inline script in
   <head> adds. If JS never runs or throws before the sequencer starts, every
   tile renders visible. An invisible homepage is the worst failure mode
   available on a live public site, and default-hidden CSS puts it one typo
   away. */

.anim .reveal {
  opacity: 0;
  transform: translateY(14px) scale(0.985);
}
.anim .revealed .reveal,
.anim .reveal.revealed {
  animation: reveal var(--dur-slow) var(--ease-spring) forwards;
  animation-delay: calc(var(--i, 0) * 45ms);
}

@keyframes reveal {
  to { opacity: 1; transform: none; }
}

/* ── Utilities ────────────────────────────────────────────────────────────── */

.col-3  { grid-column: span 3; }
.col-4  { grid-column: span 4; }
.col-5  { grid-column: span 5; }
.col-7  { grid-column: span 7; }
.col-8  { grid-column: span 8; }
.col-12 { grid-column: span 12; }

.row    { display: flex; align-items: center; gap: var(--sp-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.wrap-row { flex-wrap: wrap; }
.stack-2 { display: flex; flex-direction: column; gap: var(--sp-2); }
.stack-3 { display: flex; flex-direction: column; gap: var(--sp-3); }
.stack-4 { display: flex; flex-direction: column; gap: var(--sp-4); }
.stack-5 { display: flex; flex-direction: column; gap: var(--sp-5); }

.mono   { font-family: var(--font-mono); }
/* Required on anything that counts up: without tabular figures the number's
   width changes every frame and reflows its tile sixty times a second. */
.tnum   { font-variant-numeric: tabular-nums; }
.muted  { color: var(--fg-muted); }
.dim    { color: var(--fg-dim); }
.faint  { color: var(--fg-faint); }
.display{ font-family: var(--font-display); }

.metric {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-3);
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

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

/* ── Responsive ───────────────────────────────────────────────────────────── */

@media (max-width: 1199px) {
  .bento { grid-template-columns: repeat(6, 1fr); }
  .col-3 { grid-column: span 3; }
  .col-4, .col-5, .col-7, .col-8, .col-12 { grid-column: span 6; }
}

@media (max-width: 767px) {
  .wrap { padding: var(--sp-4) var(--sp-4) var(--sp-7); }
  .bento { grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); }
  /* Metrics and the Why Now drivers stay two-up: four full-width cards in a
     column is a scroll wasteland. Everything else goes full width. */
  .col-3 { grid-column: span 1; }
  .col-4, .col-5, .col-7, .col-8, .col-12 { grid-column: span 2; }
  .tile { padding: var(--sp-4); }
}

/* ── Reduced motion ───────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .anim .reveal { opacity: 1; transform: none; }
}

/* ============================================================================
   Utility compatibility layer
   ----------------------------------------------------------------------------
   The six domain pages were built against the Tailwind CDN browser build. That
   is a render-blocking script that compiles CSS in the browser on every visit,
   and it was one of three external origins those pages loaded before showing
   anything.

   Auditing the markup found only 99 distinct class tokens in total across all
   six pages, and almost all of them plain single-purpose utilities. Defining
   them natively here is far less code than the CDN downloads, and lets the
   pages drop Tailwind, Chart.js and Google Fonts entirely while their existing
   markup keeps working untouched.

   This layer is deliberately additive: each page keeps its own <style> block
   with its custom classes (.card, .zone-pill, .stat and so on) and accent
   colour. Only the Tailwind dependency goes away.
   ========================================================================= */

/* Layout */
.flex { display: flex; }
.grid { display: grid; }
.hidden { display: none; }
.flex-col { flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.flex-nowrap { flex-wrap: nowrap; }
.flex-shrink-0 { flex-shrink: 0; }
.flex-1 { flex: 1 1 0%; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.min-w-0 { min-width: 0; }
.mx-auto { margin-inline: auto; }
.text-center { text-align: center; }
.sticky { position: sticky; }
.top-0 { top: 0; }
.z-50 { z-index: 50; }
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* Spacing. Tailwind's scale is 0.25rem per step. */
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.gap-5 { gap: 1.25rem; }
.gap-6 { gap: 1.5rem; }
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mb-8 { margin-bottom: 2rem; }
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-5 { margin-top: 1.25rem; }
.mt-6 { margin-top: 1.5rem; }
.p-4 { padding: 1rem; }
.p-5 { padding: 1.25rem; }
.px-6 { padding-inline: 1.5rem; }
.py-4 { padding-block: 1rem; }
.py-8 { padding-block: 2rem; }
.py-12 { padding-block: 3rem; }
.pt-3 { padding-top: 0.75rem; }

/* Type */
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.text-base { font-size: 1rem; line-height: 1.5rem; }
.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
.text-2xl { font-size: 1.5rem; line-height: 2rem; }
.text-3xl { font-size: 1.875rem; line-height: 2.25rem; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.uppercase { text-transform: uppercase; }
.italic { font-style: italic; }
.tracking-wide { letter-spacing: 0.025em; }
.tracking-widest { letter-spacing: 0.1em; }
.leading-relaxed { line-height: 1.625; }
.whitespace-nowrap { white-space: nowrap; }
.text-white { color: #fff; }

/* Sizing */
.max-w-2xl { max-width: 42rem; }
.max-w-7xl { max-width: 80rem; }

/* Borders */
.border-b { border-bottom-width: 1px; border-bottom-style: solid; }
.border-t { border-top-width: 1px; border-top-style: solid; }

/* The seven arbitrary-value utilities the pages actually use. Kept as literal
   hex rather than remapped to tokens, so this layer changes nothing visually:
   its only job is to remove the CDN dependency. */
.text-\[\#94a3b8\] { color: #94a3b8; }
.text-\[\#64748b\] { color: #64748b; }
.text-\[\#475569\] { color: #475569; }
.text-\[\#10b981\] { color: #10b981; }
.text-\[\#ef4444\] { color: #ef4444; }
.text-\[\#00d4ff\] { color: #00d4ff; }
.border-\[\#1a2640\] { border-color: #1a2640; }

@media (min-width: 640px) {
  .sm\:block { display: block; }
  .sm\:gap-6 { gap: 1.5rem; }
  .sm\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sm\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Nav links sit immediately after the brand, matching Mission Control.
   The domain pages set justify-between on the nav with only two children,
   which threw the links to the far right edge, so the whole nav appeared to
   jump sideways when moving between the homepage and a domain page. */
.nav-links-scroll { margin-right: auto; }

/* ============================================================================
   Visual hierarchy pass
   ----------------------------------------------------------------------------
   The page had become thirteen tiles with identical treatment: same border,
   same radius, same background, same padding. Uniform weight means no
   hierarchy, and a grid of equally-weighted rounded rectangles is exactly the
   generic dashboard look this redesign set out to avoid.

   Weight is now assigned by importance rather than applied evenly:
     feature   hero, globe, waitlist    full glass, visible edge
     panel     working data tiles       barely-there edge, sits back
     bare      the metric bar           no container at all
   ========================================================================= */

/* Working panels sit back. The edge is felt rather than seen. */
.tile { border-color: rgba(255, 255, 255, 0.07); }
.tile::before { opacity: 0.45; }

/* Feature surfaces keep the full treatment and now clearly outrank the rest. */
.tile-glass { border-color: rgba(255, 255, 255, 0.16); }
.tile-glass::before { opacity: 1; }

/* The metric bar loses its container entirely: four figures on the page,
   divided by rules, rather than a box of boxes. */
.statbar.tile {
  background: none;
  border: 0;
  border-radius: 0;
  padding: var(--sp-5) var(--sp-2);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.statbar.tile::before { display: none; }

/* Scale contrast. These were 32px, the same order as a tile heading, so the
   page's headline numbers carried no more weight than its labels. */
.statbar .metric {
  font-size: clamp(2.75rem, 4.4vw, 4rem);
  letter-spacing: -0.045em;
  line-height: 0.95;
  margin-top: var(--sp-3);
}
.statbar .tile-sub { margin-top: var(--sp-2); }

/* Air between bands, so the page reads as composed sections rather than a
   continuous mesh of panels. */
.bento { row-gap: var(--sp-6); }

/* ── Live status ──────────────────────────────────────────────────────────── */

/* An actual pulsing indicator. "Live" as plain text was indistinguishable at
   a glance from every other label on the page. */
.live-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--stable);
  box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);
  flex-shrink: 0;
  display: inline-block;
  margin-right: 6px;
  vertical-align: baseline;
}
.settled .live-dot { animation: livePulse 2.2s ease-out infinite; }
@keyframes livePulse {
  0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.65); }
  70%  { box-shadow: 0 0 0 7px rgba(16, 185, 129, 0); }
  100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}

/* Baseline is real cited data (FAO, UMD, CelesTrak), not a degraded state,
   and should not look like one. */
.pill-baseline { background: rgba(0, 212, 255, 0.13); color: #7dd3fc; }

/* Per-domain feed status inside the Platform State tile. */
.feed-grid { display: flex; flex-direction: column; gap: 7px; }
.feed-row { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--step--2); }
.feed-row .nm { color: var(--fg-muted); flex: 1; }
.feed-row .src { color: var(--fg-faint); font-size: 10px; }

/* ============================================================================
   Domain page surfaces
   ----------------------------------------------------------------------------
   The six domain pages are built on .card and .card-glow, which were flat
   panels with a hard 1px border and a 12px radius: the look Mission Control
   was rebuilt away from. Redefining them here rather than rewriting six pages
   of markup gives them the same treatment as the homepage tiles, and touches
   none of the interactive machinery those pages depend on.

   Each page's own <style> block has its .card rules removed so these apply,
   while everything else it defines (accent colour, zone pills, sliders,
   buttons) is left exactly as it was.
   ========================================================================= */

.card,
.card-glow {
  position: relative;
  border-radius: var(--r-lg);
  background:
    linear-gradient(160deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.012) 42%),
    rgba(12, 12, 12, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

/* Hairline light on the top-left edge, the same physical-edge cue the
   homepage tiles use instead of a drawn box. */
.card::before,
.card-glow::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(155deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0) 45%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* Feature surfaces outrank working panels, matching the homepage hierarchy. */
.card-glow {
  border-color: rgba(255, 255, 255, 0.16);
  -webkit-backdrop-filter: blur(22px);
          backdrop-filter: blur(22px);
  background:
    linear-gradient(160deg, rgba(255, 255, 255, 0.075), rgba(255, 255, 255, 0.02) 42%),
    rgba(12, 12, 12, 0.45);
}
.card-glow::before { background: linear-gradient(155deg, rgba(255,255,255,0.22), rgba(255,255,255,0) 45%); }

/* Content sits above the hairline pseudo-element. */
.card > *, .card-glow > * { position: relative; }

/* Domain pages get the same ambient ground as Mission Control, so moving
   between them no longer feels like moving between two different products. */
body.domain-page { position: relative; }
body.domain-page > * { position: relative; z-index: 1; }

/* Zone-switch polish: selectZone() (every domain page) used to swap these
   in with a hard instant cut, which read as more jarring once click-to-
   drill made zone switching happen far more often than the old pill-only
   flow did. #info-name/#info-sub get a brief opacity fade orchestrated in
   JS (a plain textContent swap can't animate itself); the stock bar needs
   no JS change at all -- it's already driven by inline style.width/
   style.background, so a transition here animates every future JS-driven
   change automatically. Covered by the global prefers-reduced-motion rule
   above like everything else in this file. */
#info-name, #info-sub { transition: opacity 0.15s ease; }
#info-stock-bar { transition: width 0.4s ease, background-color 0.4s ease; }

/* Per-stakeholder allocation reasoning. Was a single always-one-column flex
   list, which for a 5+ stakeholder zone stacked far enough down the page
   that the tile beside it (Projected Impact / AI Confidence) ended in a
   fraction of the height with a large empty void beneath it. auto-fit lets
   it use the width it actually has: two columns at the widths this section
   renders at, one column once the page narrows enough that two would be
   uncomfortably tight. */
.reasoning-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 10px;
  align-items: start;
}

/* Homepage intro sequence: turbine-style logo spin-up, then a zoom-through
   fade that reveals Mission Control underneath. z-index sits above the
   sticky nav (40) and the carousel controls (45) so it fully covers both
   while active. Controlling script (inline in index-v2.html) decides
   whether this ever plays at all: skipped under prefers-reduced-motion and
   gated to once per browser session via sessionStorage. */
.intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-5);
  background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.07) 0%, var(--bg) 60%);
  /* Opaque from the first frame, deliberately: only the logo/word/note fade
     in (each has its own animation below). If the overlay's own background
     faded in too, Mission Control would visibly bleed through underneath it
     for that first fraction of a second, undercutting the "closed curtain
     that then opens" effect this is meant to be. */
  /* Pure-CSS backstop, independent of the controlling script: guarantees
     this stops blocking clicks on its own after a few seconds even if the
     script's own removal timers were somehow never reached. Harmless in the
     normal path, since the script removes the element from the DOM well
     before this fires. Set past the script's own worst case (2.8s hold +
     0.9s leave + 1.2s fallback removal = 4.9s) with real margin. */
  animation: introForceClear 0s 7s forwards;
}
.intro-overlay.leaving {
  animation: introLeave 0.9s cubic-bezier(0.6, 0, 0.9, 0.4) both;
  pointer-events: none;
}
.intro-logo {
  /* 124px, not 108px: the viewBox fix below grew the SVG's own coordinate
     box (to center it on the mark's rotational pivot) without growing the
     mark itself, which shrinks the drawn mark's on-screen size at a fixed
     container size. This offsets that so the mark reads the same size as
     before -- see the viewBox comment in index-v2.html for why. */
  width: 124px;
  height: 124px;
  filter: drop-shadow(0 0 28px rgba(0, 212, 255, 0.35));
  animation: introSpinUp 1.9s cubic-bezier(0.36, 0, 0.24, 1) both;
  transform-origin: 50% 50%;
}
.intro-logo svg { width: 100%; height: 100%; display: block; }
.intro-word {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--step-3);
  letter-spacing: 0.08em;
  color: var(--fg);
  opacity: 0;
  animation: introRise 0.6s ease 0.75s both;
}
/* The CSS-only backstop referenced on .intro-overlay above: a zero-duration
   animation with a 4.5s delay whose only job is to force the overlay out of
   the way on its own timeline, independent of any JS state. */
@keyframes introForceClear {
  to { opacity: 0; visibility: hidden; pointer-events: none; }
}
@keyframes introRise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* The "engine starting up" feel: slow, heavy first turn that accelerates
   into a near-blur, exactly like a turbine spinning up to speed rather than
   a constant-rate rotation. Same 1.9s runtime as before (the word/note fade
   timings and the overlay's hold/leave sequence are tuned around it), but a
   steeper back half: by 65% it's already past where the old curve ended up
   at 75%, then covers more than half its total rotation in the final third
   -- noticeably faster top speed, not just a longer spin. */
@keyframes introSpinUp {
  0%   { transform: rotate(0deg) scale(0.92); opacity: 0; }
  8%   { opacity: 1; }
  25%  { transform: rotate(70deg) scale(1); }
  45%  { transform: rotate(320deg); }
  65%  { transform: rotate(820deg); }
  100% { transform: rotate(1900deg); }
}
@keyframes introLeave {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.6); }
}

@media (prefers-reduced-motion: reduce) {
  .intro-overlay, .intro-logo, .intro-word {
    animation: none !important;
    opacity: 1;
  }
}

@media (max-width: 640px) {
  .intro-logo { width: 96px; height: 96px; }
  .intro-word { font-size: var(--step-2); }
}

/* ============================================================================
   Mobile nav (shared across every page's top bar)
   ----------------------------------------------------------------------------
   The desktop nav packs 7 domain links plus About/More/Investor-Deck/Contact
   into one non-wrapping scrollable row (.navlinks / .nav-links-scroll /
   .nav-links, depending on the page). Below 768px there's no room for all of
   it, and the tail end (the About/More/CTA group) was overflowing off-screen
   or sitting unreachable inside the scroll row with no visual cue more
   content existed. Fix: hide that whole row below 768px and swap in a native
   <details>/<summary> disclosure -- no JS to wire up per page, works
   identically everywhere regardless of which of the three link-row class
   names a given page happens to use. Each page's own <details> block lists
   only the links that page's desktop nav already had, so this never invents
   navigation that wasn't already there. */
nav .navlinks, nav .nav-links-scroll, nav .nav-links { flex-shrink: 1; min-width: 0; }
.nav-mobile-toggle { display: none; }
@media (max-width: 767px) {
  nav { flex-wrap: nowrap; }
  /* .nav-right-group is nested inside the links row on domain pages (hiding
     the parent already hides it) but a sibling of .navlinks on index-v2.html
     -- listed explicitly too since hiding only the parent would leave it
     sitting alone on the sibling layout. */
  nav .navlinks, nav .nav-links-scroll, nav .nav-links, nav .nav-right-group { display: none !important; }
  .nav-mobile-toggle { display: block; margin-left: auto; flex-shrink: 0; }
  .nav-mobile-toggle > summary {
    list-style: none; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; border-radius: 8px;
    border: 1px solid rgba(255,255,255,0.16);
    color: #fff; font-size: 17px; line-height: 1;
    -webkit-tap-highlight-color: transparent;
  }
  .nav-mobile-toggle > summary::-webkit-details-marker,
  .nav-mobile-toggle > summary::marker { display: none; content: ""; }
  .nav-mobile-toggle[open] > summary { border-color: rgba(47,125,255,0.55); color: #2f7dff; background: rgba(47,125,255,0.1); }
  .nav-mobile-panel {
    position: fixed; top: 58px; left: 0; right: 0; z-index: 90;
    max-height: calc(100vh - 58px); overflow-y: auto;
    background: rgba(4,4,6,0.98);
    -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
    border-bottom: 1px solid rgba(255,255,255,0.1);
    padding: 8px 8px 28px;
    display: flex; flex-direction: column;
  }
  .nav-mobile-panel a {
    display: block; padding: 14px 16px; border-radius: 9px;
    color: #cbd5e1 !important; font-size: 15px; font-weight: 600;
    text-decoration: none !important; background: none !important;
    border: none !important; white-space: normal !important;
  }
  .nav-mobile-panel a[aria-current="page"], .nav-mobile-panel a.active { color: #2f7dff !important; }
  .nav-mobile-panel a:active { background: rgba(255,255,255,0.06) !important; }
  .nav-mobile-divider { height: 1px; background: rgba(255,255,255,0.08); margin: 8px 12px; }
}
