/* ============================================================================
   BASE — reset, the dotted ground, typography, and the shared primitives
   (.label, .box, .rule, .link) that every section is assembled from.
   ========================================================================= */

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

* { margin: 0; padding: 0; }

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--t-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

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

/* --- The dotted ground -----------------------------------------------------
   Fixed rather than scrolling: the paper stays put and the content moves
   across it, which also keeps the lattice in register with sticky panels. */
.ground {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: radial-gradient(
    circle at center,
    var(--dot-ink) var(--dot-size),
    transparent var(--dot-size)
  );
  background-size: var(--dot-pitch) var(--dot-pitch);
}

/* --- Typography primitives ------------------------------------------------- */

/* Uppercase mono label. Figure numbers, section markers, metadata keys. */
.label {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  line-height: 1.4;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mid);
  font-weight: 500;
}

.label--ink { color: var(--ink); }

/* The section headings. Same machine voice as .label — mono, uppercase,
   tracked — but set as a heading rather than as metadata, because with the
   right-hand column of each head row gone and the serif title in the team
   section gone with it, this line IS the heading now. */
.marker {
  font-family: var(--font-mono);
  font-size: var(--t-marker);
  line-height: 1.3;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 500;
}

/* Editorial voice. */
.serif {
  font-family: var(--font-serif);
  font-weight: 400;
}

.mono { font-family: var(--font-mono); }

h1, h2, h3 {
  font-weight: 400;
  text-wrap: balance;
  letter-spacing: -0.015em;
}

h2 {
  font-family: var(--font-serif);
  font-size: var(--t-h2);
  line-height: 1.1;
}

h3 {
  font-size: var(--t-h3);
  line-height: 1.25;
}

p { text-wrap: pretty; }

/* --- Justified prose -------------------------------------------------------
   The mission only. It is the one block on the page with a measure wide enough
   to justify cleanly; everywhere else — panel notes at ~30 characters, news
   standfirsts, the team copy — the columns are too narrow and justifying them
   opens rivers of whitespace. Those stay ragged-right. */
.mission p {
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}

/* --- Rules -----------------------------------------------------------------
   A full-bleed hairline. The separator between every chapter. */
.rule {
  border: 0;
  border-top: var(--rule);
  width: 100%;
}

.rule--soft { border-top: var(--rule-soft); }

/* --- Box -------------------------------------------------------------------
   A thin black rectangle drawn on the dot lattice. The universal container.
   Filled, so the dots stop at its edge. */
.box {
  border: var(--rule);
  background: var(--box-fill);
}

/* --- Links ----------------------------------------------------------------- */
a {
  color: inherit;
  text-decoration: none;
}

.link {
  display: inline-block;
  border-bottom: var(--line-weight) solid var(--line-soft);
  transition: color var(--dur-ui) var(--ease),
              border-color var(--dur-ui) var(--ease);
}

.link:hover,
.link:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

:focus-visible {
  outline: var(--line-weight) solid var(--accent);
  outline-offset: 3px;
}

/* --- Layout shell ---------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-pad);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Reveal ----------------------------------------------------------------
   The single mechanism behind every entrance on the page. Elements carry
   [data-reveal]; JS adds .is-in when the chapter's scroll progress passes the
   element's threshold (or when it enters view, outside sticky chapters).
   --i staggers members of a group.

   Everything here is scoped to .js, which the inline script in <head> sets.
   Without it the hidden state never applies, so a failed or blocked script
   leaves the page fully readable instead of blank.                          */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--grid-2));
  transition:
    opacity var(--dur-reveal) var(--ease),
    transform var(--dur-reveal) var(--ease);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}

.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
}

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