/**
 * fonts.css — self-hosted webfonts.
 *
 * Self-hosted rather than loaded from Google Fonts, for three reasons: one
 * fewer third-party origin on the critical path, no dependency on a service
 * that may be blocked on some networks (which is exactly how a beautifully
 * typeset page silently degrades to Arial without anyone noticing), and a
 * measurably faster LCP because the files come from the same connection.
 *
 * Files live in public/fonts/ and are copied to dist/ by the build. The
 * single-file and embed builds inline them as data URIs instead.
 *
 * Both families are licensed under the SIL Open Font License 1.1; the licence
 * texts ship alongside the files in public/fonts/.
 *   Instrument Sans — Rhode Island Type Foundry
 *   IBM Plex Mono   — IBM
 *
 * Latin subset only. If the site ever needs Norwegian-specific glyphs beyond
 * latin (æ ø å are all in the latin subset, so it does not today), swap in the
 * latin-ext files from the same packages.
 */

@font-face {
  font-family: 'Instrument Sans';
  src: url('/fonts/instrument-sans-var.woff2') format('woff2-variations');
  font-weight: 400 700; /* variable axis — 400, 500 and 600 all come from this one file */
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/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('/fonts/ibm-plex-mono-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/**
 * tokens.css — the only place raw design values live.
 *
 * Token NAMES are fixed across every site built on this architecture.
 * Token VALUES are where a site differentiates. Changing this one file
 * changes the entire character of the page without touching a section.
 *
 * Direction: "Instrument" — a precision readout printed on paper. The register
 * concept is unchanged; it now has CHANNELS. Colour is information here, not
 * decoration: each discipline is a numbered channel with its own trace colour,
 * venture status is colour-coded, and the hero carries a live signal band.
 *
 * Apple's lesson applied here is structural: brutal hierarchy, enormous
 * negative space, one idea per screen, tight optical typography, motion smooth
 * enough to disappear.
 *
 * The palette is deliberately scope-trace — blue, teal, magenta, amber — and
 * deliberately NOT the indigo-to-violet gradient that marks generic AI sites.
 */

:root {
  /* ---- Colour ---------------------------------------------------------
     Cool instrument paper, graphite ink, one electric blue for action, and
     four channel colours that exist to ENCODE something — never to fill a
     slot. If a colour here is not carrying meaning, it should not be used. */
  --surface: #f6f7f9;
  --surface-raised: #ffffff;
  --surface-sunken: #eceef2;
  --surface-ink: #0f1319;

  --text-primary: #0f1319;
  --text-secondary: #4a515c;
  --text-muted: #666d79; /* 5.0:1 on --surface — verified, not assumed */
  --text-inverse: #f6f7f9;

  --border-subtle: #e2e5ea;
  --border-mid: #cdd2da;
  --border-strong: #a8afba;
  --grid-line: #dfe3e9; /* graph-paper ruling */

  --accent: #1450f0; /* action, and only action */
  --accent-hover: #0f3fc4;
  --accent-contrast: #ffffff;

  /* Channel traces. Text-safe variants are used for labels; the -mark
     variants are for chips and canvas strokes, where 3:1 is the bar. */
  --ch-1: #1450f0;
  --ch-2: #007d86;
  --ch-3: #b81b74;
  --ch-4: #9a6100;
  --ch-1-mark: #3f74ff;
  --ch-2-mark: #00949e;
  --ch-3-mark: #e5399b;
  --ch-4-mark: #c47e0c;

  /* Status semantics, kept separate from the channel palette. */
  --status-building: #9a6100;
  --status-live: #1a7f37;
  --status-reserved: #7b8290;

  /* Metal ramp — the emblem and the portrait's descent from paper to ink.
     Monochrome by rule: pearl, silver, graphite, charcoal, black. Used for
     brand surfaces only; it must never carry a status or a channel. */
  --metal-pearl: #eef0f3;
  --metal-silver: #c3c9d2;
  --metal-graphite: #6b7380;
  --metal-charcoal: #2a2f37;
  --metal-ink: #000000;

  --focus-ring: var(--accent);

  /* ---- Typography -----------------------------------------------------
     Two families. Instrument Sans carries display and body: a tight
     grotesque that holds up at 8rem where a neutral UI face goes limp.
     IBM Plex Mono carries every label and numeral — it is the manifest's
     voice and the reason the page still reads as a document, not a deck. */
  --font-display: 'Instrument Sans', 'Helvetica Neue', system-ui, sans-serif;
  --font-body: 'Instrument Sans', system-ui, -apple-system, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --step--2: clamp(0.68rem, 0.66rem + 0.1vw, 0.74rem);
  --step--1: clamp(0.8rem, 0.77rem + 0.15vw, 0.88rem);
  --step-0: clamp(1.02rem, 0.98rem + 0.2vw, 1.14rem);
  --step-1: clamp(1.22rem, 1.1rem + 0.55vw, 1.5rem);
  --step-2: clamp(1.5rem, 1.25rem + 1.2vw, 2.15rem);
  --step-3: clamp(2rem, 1.5rem + 2.5vw, 3.5rem);
  --step-4: clamp(2.6rem, 1.7rem + 4.4vw, 5.2rem);
  --step-5: clamp(2.65rem, 1.2rem + 6.2vw, 5.6rem);

  /* Tight leading and negative tracking are what make large sans type read
     as designed rather than merely large. */
  --leading-display: 1.03;
  --leading-tight: 1.18;
  --leading-body: 1.6;

  --tracking-display: -0.035em;
  --tracking-body: -0.006em;
  --tracking-label: 0.13em;

  --weight-body: 400;
  --weight-medium: 500;
  --weight-display: 600;
  /* The hero sits a step lighter than other headings: at 90px, 600 reads as a
     poster and 500 reads as designed. Weight is optical, not fixed. */
  --weight-hero: 500;

  --measure: 62ch;
  --measure-narrow: 46ch;

  /* ---- Space ----------------------------------------------------------
     One scale. Section padding varies by section WEIGHT: the statement
     screen gets --space-4xl, the dense register gets --space-2xl.
     Uniform padding is what flattens a page, so it is avoided on purpose. */
  --space-3xs: 0.125rem;
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-s: 1rem;
  --space-m: 1.5rem;
  --space-l: 2.5rem;
  --space-xl: 4rem;
  --space-2xl: clamp(4rem, 8vw, 7rem);
  --space-3xl: clamp(6rem, 12vw, 11rem);
  --space-4xl: clamp(8rem, 17vw, 16rem);

  /* ---- Motion ---------------------------------------------------------
     Longer and softer than the dark direction: on a light ground a sharp
     mechanical wipe reads as a glitch, a slow one reads as expensive. */
  --ease-out: cubic-bezier(0.22, 1, 0.28, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);

  --dur-fast: 160ms;
  --dur-base: 320ms;
  --dur-slow: 1000ms;
  --stagger: 80ms;

  /* ---- Structure ------------------------------------------------------
     A small radius rather than 0 or a pill: softened enough to feel modern,
     short of the rounded-everything look that marks generated pages. */
  --radius: 7px;
  --radius-sm: 4px;

  --container: 1280px;
  --container-text: 720px;
  --gutter: clamp(1.25rem, 5vw, 5rem);
  --rail: clamp(2.75rem, 7vw, 7.5rem);

  --hairline: 1px;
  --header-height: 4.25rem;
  /* Band height lives here so the hero can reserve exactly the right space
     for it — the caption was being clipped when the two were guessed apart. */
  --signal-canvas: clamp(110px, 15vh, 168px);
  --signal-caption: 2.9rem;

  --z-rules: 1;
  --z-content: 2;
  --z-header: 100;
}

@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;
  }
}

/* base.css — reset, typography defaults, shared primitives (rule, eyebrow, cta). */

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

* {
  margin: 0;
}

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

body {
  background: var(--surface);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--leading-body);
  letter-spacing: var(--tracking-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

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

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

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 3px;
}

/* Skip link ---------------------------------------------------------- */
.skip {
  position: absolute;
  left: var(--space-s);
  top: -100%;
  z-index: 999;
  padding: var(--space-xs) var(--space-s);
  background: var(--accent);
  color: var(--accent-contrast);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  transition: top var(--dur-fast) var(--ease-out);
}
.skip:focus {
  top: var(--space-s);
}

/* Layout ------------------------------------------------------------- */
.shell {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* The index rail: content hangs off a left gutter carrying mono numerals.
   Collapses to a single column below 900px, where the rail becomes a
   stacked label instead of a side column. */
.railed {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  column-gap: clamp(1rem, 3vw, 3rem);
}

@media (max-width: 900px) {
  .railed {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--space-m);
  }
}

/* Typography primitives ---------------------------------------------- */
.display {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

/* Two-tone headline: the emphasised clause drops to secondary ink rather than
   changing style. It separates the claim from its qualifier without adding a
   colour, which is the quietest way to build hierarchy inside one sentence. */
.display em {
  font-style: normal;
  color: var(--text-muted);
}

.mono {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.eyebrow {
  color: var(--text-muted);
}

.lede {
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--text-secondary);
  max-width: var(--measure-narrow);
}

.body {
  color: var(--text-secondary);
  max-width: var(--measure);
}

/* The measurement rule ------------------------------------------------
   The site's repeating motif: a hairline with tick marks and a section label.
   It divides every section and is the device that makes the page recognisable
   with the wordmark removed. The index numeral it used to carry was removed:
   section numbers are meaningful to whoever built the page and to nobody
   reading it, and beside every heading they competed with the headings. */
.rule {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-m);
  padding-block: var(--space-s);
}

.rule__track {
  position: relative;
  height: 9px;
  border-bottom: var(--hairline) solid var(--border-mid);
  transform-origin: left center;
}

.rule__track::before {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 5px;
  background-image: repeating-linear-gradient(
    to right,
    var(--border-mid) 0 1px,
    transparent 1px 12px
  );
}

.rule__label {
  color: var(--text-muted);
  white-space: nowrap;
}

/* Actions ------------------------------------------------------------- */
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s) var(--space-m);
  align-items: center;
}

/* A status dot, shared by the hero meta row and the register. Colour comes
   from whatever sets --dot, so one primitive serves every state. */
.dot {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: 50%;
  background: var(--dot, var(--accent));
}

/* Primary action carries the one electric blue on the page. Every other
   colour here encodes a channel or a state, so action stays unambiguous. */
.cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
  padding: 0.9em 1.5em;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-contrast);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border: var(--hairline) solid var(--accent);
  transition: background var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.cta:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

.cta:active {
  transform: translateY(1px);
}

.cta__arrow {
  transition: transform var(--dur-base) var(--ease-out);
}

.cta:hover .cta__arrow {
  transform: translateX(0.3em);
}

.cta--ghost {
  background: transparent;
  color: var(--text-primary);
  border-color: var(--border-mid);
}

.cta--ghost:hover {
  background: var(--surface-raised);
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.cta--bare {
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--text-primary);
  border-bottom: var(--hairline) solid var(--border-strong);
  padding-bottom: 0.35em;
}

.cta--bare:hover {
  background: transparent;
  color: var(--accent);
  border-color: var(--accent);
}

.reassurance {
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.06em;
}

/* Reveals -------------------------------------------------------------
   Unmasking, not fading up. Elements are clipped from the bottom and
   wiped open; hairlines draw themselves horizontally. Content is fully
   present in the DOM and visible if JS never runs. */
/* The wipe is done with a mask, not clip-path, and that choice is load-bearing:
   an element hidden with clip-path reports an intersectionRatio of 0, so it can
   never trigger its own IntersectionObserver and would stay hidden forever.
   Masks do not affect intersection geometry. */
.js [data-reveal] {
  opacity: 0;
  -webkit-mask-image: linear-gradient(to bottom, #000, #000);
  mask-image: linear-gradient(to bottom, #000, #000);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: 100% 0%;
  mask-size: 100% 0%;
  transition: opacity var(--dur-slow) var(--ease-out),
    -webkit-mask-size var(--dur-slow) var(--ease-out),
    mask-size var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

.js [data-reveal].is-revealed {
  opacity: 1;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
}

/* Once the wipe has finished the mask has no further purpose, so it is dropped
   to release the compositing layer it was holding. */
.js [data-reveal].is-settled {
  -webkit-mask-image: none;
  mask-image: none;
}

.js [data-draw] {
  transform: scaleX(0);
  transition: transform var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

.js [data-draw].is-revealed {
  transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js [data-draw] {
    opacity: 1;
    clip-path: none;
    transform: none;
  }
}

/* sections.css — one block per section, in page order.
   Section padding varies by section WEIGHT on purpose; see tokens.css. */

/* ── Header ───────────────────────────────────────────────────────────── */
.header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--z-header);
  height: var(--header-height);
  display: flex;
  align-items: center;
  border-bottom: var(--hairline) solid transparent;
  transition: background var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out);
}

.header.is-stuck {
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  border-bottom-color: var(--border-subtle);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  width: 100%;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.65em;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* The emblem. Optically a touch below the cap height of the name, so the name
   stays the readable element and the mark reads as its seal, not its equal. */
.mark {
  display: block;
  flex: none;
  height: 1.35em;
  width: auto;
  transform: translateY(-0.02em);
}

.mark__gap {
  stroke: var(--surface);
}

.header.is-stuck .mark__gap {
  /* the stuck header is a translucent tint of the surface; same colour */
  stroke: var(--surface);
}

.header__nav {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.25rem);
}

.header__link {
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-out);
}

.header__link:hover {
  color: var(--text-primary);
}

@media (max-width: 640px) {
  /* One line, always: a lockup that wraps stops being a lockup. */
  .wordmark {
    gap: 0.55em;
    font-size: var(--step--2);
    white-space: nowrap;
  }
  .header__nav {
    gap: 1rem;
  }
  .header__link {
    font-size: var(--step--2);
  }
  .header__link[data-optional] {
    display: none;
  }
}

/* ── Hero ─────────────────────────────────────────────────────────────── */
.hero {
  position: relative;
  padding-block: calc(var(--header-height) + var(--space-2xl))
    calc(var(--space-l) + var(--signal-canvas) + var(--signal-caption));
  min-height: 100svh;
  display: flex;
  align-items: center;
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-l);
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-s) var(--space-m);
  color: var(--text-muted);
}

.hero__meta span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-m);
}

.hero__meta span:not(:last-child)::after {
  content: '';
  width: clamp(1rem, 4vw, 2.5rem);
  height: var(--hairline);
  background: var(--border-mid);
}

/* Same state, same colour as the register row it refers to. A status hue that
   changes meaning between two sections is worse than no hue at all. */
.hero__meta .is-live {
  --dot: var(--status-building);
  color: var(--status-building);
}

.hero__title {
  font-size: var(--step-5);
  font-weight: var(--weight-hero);
  max-width: 21ch;
}

.hero__lede {
  max-width: 52ch;
  text-align: center;
  text-wrap: balance; /* prevents a two-word orphan on the last line */
  font-size: var(--step-1);
  color: var(--text-secondary);
}

.hero__actions {
  justify-content: center;
  margin-top: var(--space-s);
}

/* Below 900px the band stops being an absolutely-positioned strip and becomes
   the last item in the stack. The hero has to switch to a column for that —
   otherwise the figure joins the flex row and sits beside the content. */
@media (max-width: 900px) {
  .hero {
    min-height: auto;
    flex-direction: column;
    align-items: stretch;
    padding-block: calc(var(--header-height) + var(--space-2xl)) 0;
  }

  .signal {
    position: static;
    width: 100%;
    margin-top: var(--space-2xl);
  }
}

/* ── Signal band ──────────────────────────────────────────────────────
   A generative readout closing the hero. Full-bleed so it reads as an
   instrument strip rather than a chart dropped into the layout. */
.signal {
  position: absolute;
  inset: auto 0 0 0;
  margin: 0;
  border-top: var(--hairline) solid var(--border-subtle);
  border-bottom: var(--hairline) solid var(--border-subtle);
  background: var(--surface-raised);
}

.signal__canvas {
  display: block;
  width: 100%;
  height: var(--signal-canvas);
}

.signal__caption {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  min-height: var(--signal-caption);
  padding-inline: var(--gutter);
  color: var(--text-muted);
  border-top: var(--hairline) solid var(--border-subtle);
}

.signal__channels {
  display: inline-flex;
  gap: 5px;
}

.signal__channels i {
  width: 14px;
  height: 3px;
  background: var(--c);
}

/* The connector hairlines only read correctly on one line; once the meta row
   wraps they leave a rule dangling at the line end, so drop them on small
   screens and let the gap do the separating. */
@media (max-width: 700px) {
  .hero__meta span:not(:last-child)::after {
    display: none;
  }
  .hero__meta {
    gap: var(--space-xs) var(--space-m);
  }
}

/* ── Statement (the quiet screen) ─────────────────────────────────────── */
/* Asymmetric on purpose: the hero already supplies the air above, so the
   quiet belongs below the sentence, where it separates position from register. */
.statement {
  padding-block: var(--space-3xl) var(--space-4xl);
}

.statement__text {
  font-size: var(--step-3);
  font-weight: var(--weight-hero);
  max-width: 26ch;
  color: var(--text-primary);
}

/* ── Register ─────────────────────────────────────────────────────────── */
.register {
  padding-block: var(--space-2xl) var(--space-3xl);
}

.register__list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.register__note {
  color: var(--text-muted);
  max-width: var(--measure-narrow);
  margin-bottom: var(--space-xl);
  font-size: var(--step--1);
}

/* Numbered-row pattern, shared with .principle: a narrow numeral column so the
   index reads as belonging to its row rather than floating in the gutter. */
.entry {
  padding-block: var(--space-l);
  border-top: var(--hairline) solid var(--border-subtle);
  transition: border-color var(--dur-base) var(--ease-out);
}

.entry:last-child {
  border-bottom: var(--hairline) solid var(--border-subtle);
}

.entry__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-m);
  margin-bottom: var(--space-s);
}

.entry__name {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--weight-display);
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
}

/* Status is colour-coded as well as worded: state should read at a glance,
   and colour alone is never the only carrier — the label always says it too. */
.entry__status {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  white-space: nowrap;
  color: var(--text-secondary);
}

.entry__status[data-tone='building'] {
  --dot: var(--status-building);
  color: var(--status-building);
}

.entry__status[data-tone='live'] {
  --dot: var(--status-live);
  color: var(--status-live);
}

.entry__status[data-tone='neutral'] {
  --dot: var(--status-reserved);
  color: var(--text-muted);
}

.entry__summary {
  color: var(--text-secondary);
  max-width: var(--measure);
  margin-bottom: var(--space-m);
}

.entry__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-m);
  color: var(--text-muted);
  list-style: none;
  padding: 0;
}

@media (hover: hover) {
  .entry:hover {
    border-color: var(--border-strong);
  }
}

/* ── Portrait (the one dark screen) ──────────────────────────────────────
   Inversion by token override, not by a parallel stylesheet: every component
   inside goes on reading the same token names it reads everywhere else.

   Note what is NOT overridden here: --surface. The fade bands need the page's
   paper colour, and if the section reassigned --surface they would fade black
   into black. The ground is set explicitly instead. */
.portrait {
  --portrait-ground: var(--metal-ink); /* the image's own border is pure black */
  /* The descent from paper to ink is the page's one brand gesture, so it is
     long and it is graded: pearl, silver, graphite, charcoal, black. A short
     two-stop fade read as paper glued to a black rectangle. */
  --fade: clamp(9rem, 17vw, 16rem);

  --surface-raised: #161b23;
  --text-primary: #f6f7f9;
  --text-secondary: #b9c0cb;
  --text-muted: #939ba7;
  --border-subtle: #232a35;
  --border-mid: #2f3846;
  --border-strong: #46505f;
  /* The action blue is unreadable on ink, so the dark section lifts it.
     Same role, same token name, a value that clears AA on this ground. */
  --accent: #5b8cff;

  position: relative;
  background: var(--portrait-ground);
  color: var(--text-primary);
  padding-block: var(--fade);
  margin-top: var(--space-xl);
}

/* The fade lives on bands sized to the padding, not on the section background.
   A gradient behind the picture is invisible — the picture covers it — and the
   result is the hard seam this is here to avoid. These end exactly where the
   image begins. */
.portrait::before,
.portrait::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: var(--fade);
  pointer-events: none;
}

.portrait::before {
  top: 0;
  background: linear-gradient(
    to bottom,
    var(--surface) 0%,
    var(--metal-pearl) 12%,
    var(--metal-silver) 34%,
    var(--metal-graphite) 58%,
    var(--metal-charcoal) 80%,
    var(--metal-ink) 100%
  );
}

.portrait::after {
  bottom: 0;
  background: linear-gradient(
    to top,
    var(--surface) 0%,
    var(--metal-pearl) 12%,
    var(--metal-silver) 34%,
    var(--metal-graphite) 58%,
    var(--metal-charcoal) 80%,
    var(--metal-ink) 100%
  );
}

.portrait__figure {
  margin: 0;
}

.portrait__figure img {
  display: block;
  width: 100%;
  height: auto;
  /* Feather the picture's own top and bottom edges into the ink so the
     photograph and the gradient are one surface, not a rectangle on a ramp. */
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
}

/* ── Method ───────────────────────────────────────────────────────────── */
.method {
  padding-block: var(--space-2xl) var(--space-3xl);
}

.method__label {
  color: var(--text-muted);
}

@media (min-width: 901px) {
  .method__label {
    position: sticky;
    top: calc(var(--header-height) + var(--space-l));
    align-self: start;
  }
}

.principle {
  padding-block: var(--space-l);
  border-top: var(--hairline) solid var(--border-subtle);
}

.principle:first-child {
  border-top: 0;
  padding-top: 0;
}

.principle__title {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--weight-display);
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
  margin-bottom: var(--space-xs);
}

.principle__body {
  color: var(--text-secondary);
  max-width: var(--measure);
}

/* ── Disciplines (spec table, deliberately not icon cards) ────────────── */
.disciplines {
  padding-block: var(--space-2xl) var(--space-3xl);
}

.spec {
  border-top: var(--hairline) solid var(--border-subtle);
}

.disciplines__heading {
  margin-bottom: var(--space-l);
}

.spec__row {
  display: grid;
  grid-template-columns: 6.5rem minmax(9rem, 18%) minmax(0, 1fr);
  column-gap: clamp(0.75rem, 3vw, 3rem);
  padding-block: var(--space-m);
  border-bottom: var(--hairline) solid var(--border-subtle);
  transition: background var(--dur-fast) var(--ease-out);
}

@media (hover: hover) {
  .spec__row:hover {
    background: var(--surface-raised);
    box-shadow: 0 1px 0 var(--border-subtle);
  }
}

.spec__channel {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  color: var(--text-muted);
}

/* Channel colour comes from position, not from the config: colour is a design
   decision here, and reordering the list should renumber and recolour it. */
.spec__swatch {
  width: 14px;
  height: 3px;
  flex: none;
  background: var(--ch);
}

.spec__row:nth-child(1) { --ch: var(--ch-1-mark); }
.spec__row:nth-child(2) { --ch: var(--ch-2-mark); }
.spec__row:nth-child(3) { --ch: var(--ch-3-mark); }
.spec__row:nth-child(4) { --ch: var(--ch-4-mark); }

.spec__row:nth-child(1) .spec__key { color: var(--ch-1); }
.spec__row:nth-child(2) .spec__key { color: var(--ch-2); }
.spec__row:nth-child(3) .spec__key { color: var(--ch-3); }
.spec__row:nth-child(4) .spec__key { color: var(--ch-4); }

.spec__key {
  font-weight: var(--weight-medium);
}

.spec__value {
  color: var(--text-secondary);
  margin: 0;
}

@media (max-width: 760px) {
  .spec__row {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--space-2xs);
  }
}

/* ── Contact (centred, bookending the hero) ──────────────────────────── */
.contact {
  padding-block: var(--space-3xl) var(--space-4xl);
}

.contact__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-m);
}

.contact__title {
  font-size: var(--step-4);
  font-weight: var(--weight-hero);
  max-width: 17ch;
}

.contact__body {
  max-width: 46ch;
  text-wrap: balance;
  color: var(--text-secondary);
}

.contact__actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-m);
  margin-top: var(--space-m);
}

/* ── Footer ───────────────────────────────────────────────────────────── */
.footer {
  border-top: var(--hairline) solid var(--border-subtle);
  padding-block: var(--space-l) var(--space-xl);
}

.footer__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.65em;
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-m) var(--space-l);
  color: var(--text-muted);
}

.footer__group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m) var(--space-l);
}

.footer a {
  transition: color var(--dur-fast) var(--ease-out);
}

.footer a:hover {
  color: var(--accent);
}

/* ── Legal (content page: /terms/) ────────────────────────────────────── */
/* The same rail as Method, so a contract reads as one of the instrument's
   own readouts rather than a pasted PDF. The body sits at reading measure;
   the rail carries a sticky contents list on wide screens. */
.legal {
  padding-block: calc(var(--header-height) + var(--space-xl)) var(--space-3xl);
}

.legal__head {
  padding-block: var(--space-l) var(--space-2xl);
}

.legal__title {
  font-size: var(--step-4);
  font-weight: var(--weight-hero);
  max-width: 14ch;
}

.legal__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-l);
  margin-top: var(--space-m);
  color: var(--text-muted);
}

.legal__toc-label {
  color: var(--text-muted);
  margin-bottom: var(--space-s);
}

.legal__toc-list {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  font-size: var(--step--1);
  color: var(--text-secondary);
}

.legal__toc-list a {
  display: inline-block;
  padding-block: 0.15em;
  border-bottom: var(--hairline) solid transparent;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

.legal__toc-list a:hover {
  color: var(--accent);
  border-color: var(--accent);
}

@media (min-width: 901px) {
  .legal__toc {
    position: sticky;
    top: calc(var(--header-height) + var(--space-l));
    align-self: start;
  }
}

@media (max-width: 900px) {
  /* On a phone the contents list is a short row of links, not a column that
     pushes the text below the fold. */
  .legal__toc-list {
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--space-xs) var(--space-m);
  }
}

.legal__body {
  max-width: var(--measure);
  color: var(--text-secondary);
}

.legal__intro {
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--text-primary);
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding-bottom: var(--space-l);
  border-bottom: var(--hairline) solid var(--border-subtle);
}

.legal__section {
  padding-block: var(--space-l);
  border-bottom: var(--hairline) solid var(--border-subtle);
  scroll-margin-top: calc(var(--header-height) + var(--space-m));
}

.legal__section:last-child {
  border-bottom: 0;
}

.legal__h {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--weight-display);
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
  color: var(--text-primary);
  margin-bottom: var(--space-s);
}

.legal__section p,
.legal__section ul,
.legal__section ol {
  margin-bottom: var(--space-s);
}

.legal__section :last-child {
  margin-bottom: 0;
}

.legal__section ul,
.legal__section ol {
  padding-left: 1.3em;
}

.legal__section li {
  padding-left: 0.25em;
  margin-bottom: var(--space-2xs);
}

.legal__section li::marker {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--text-muted);
}

.legal__section strong {
  font-weight: var(--weight-medium);
  color: var(--text-primary);
}

.legal__table {
  width: 100%;
  border-collapse: collapse;
  margin-block: var(--space-s) var(--space-m);
}

.legal__table th,
.legal__table td {
  text-align: left;
  vertical-align: top;
  padding: var(--space-s) 0;
  border-top: var(--hairline) solid var(--border-subtle);
}

.legal__table tr:last-child th,
.legal__table tr:last-child td {
  border-bottom: var(--hairline) solid var(--border-subtle);
}

.legal__table th {
  width: 11rem;
  padding-right: var(--space-m);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  white-space: nowrap;
}

@media (max-width: 520px) {
  .legal__table th,
  .legal__table td {
    display: block;
    width: auto;
    padding-right: 0;
  }
  .legal__table td {
    border-top: 0;
    padding-top: 0;
  }
}

.legal__address {
  font-style: normal;
  line-height: 1.7;
  margin-bottom: var(--space-s);
}

.legal__address a {
  color: var(--accent);
}
