/* ============================================================================
   PRECISION MINIMAL — the design language spec, shipped as tokens.
   ----------------------------------------------------------------------------
   Every value in the spec lives here. No component hardcodes anything.

   Colour is Option A (light), as chosen. One deviation, and it is deliberate:

     The spec's `--ink-tertiary #A1A1AA` measures 2.50:1 on the canvas. That
     fails AA for body text (4.5) and for large text (3.0) — while the spec
     assigns eyebrows, labels, metadata and table headers to it, all of which
     are small text. It is replaced by #6B6B75 at 5.14:1. #A1A1AA survives as
     `--ink-disabled`, named so it can only be reached for non-text use.

   Verified by `node tools/contrast.mjs` on every build.
   ========================================================================= */

:root {
  /* --- COLOUR · Option A, light ---------------------------------------- */
  --canvas: #fcfcfc;           /* page background                          */
  --surface: #ffffff;          /* cards, raised panels                     */
  --surface-sunken: #f5f5f4;   /* alternating bands                        */

  --ink: #0a0a0a;              /* 19.30:1 — primary text                   */
  --ink-secondary: #52525b;    /*  7.53:1 — body, descriptions             */
  --ink-tertiary: #6b6b75;     /*  5.14:1 — captions, labels, metadata     */
  --ink-disabled: #a1a1aa;     /*  2.50:1 — NON-TEXT ONLY                  */

  /* Hairlines are always 1px at low opacity, never a heavier line at higher
     lightness. Opacity is what makes them sit correctly on every ground and
     survive a theme swap. */
  --hairline: rgba(0, 0, 0, 0.08);
  --hairline-strong: rgba(0, 0, 0, 0.14);

  /* One accent per page. Budget: primary CTA, one data highlight, link
     hover. Nothing else. */
  --accent: #3b3e9e;           /*  8.64:1 on canvas                        */
  --accent-quiet: #5b5fc7;     /*  5.24:1 on canvas                        */
  --accent-contrast: #ffffff;  /*  8.87:1 on accent                        */
  --accent-tint: rgba(59, 62, 158, 0.08);

  /* --- TYPEFACES -------------------------------------------------------- */
  --font-sans: 'Geist', ui-sans-serif, system-ui, -apple-system, 'Segoe UI',
    Roboto, sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, 'SF Mono', SFMono-Regular, Menlo,
    Consolas, monospace;

  /* --- TYPE SCALE -------------------------------------------------------
     Negative tracking scales with size — large type at default tracking is
     the single clearest tell of an amateur build. Weight ceiling is 600;
     contrast comes from size and space, never from boldness. */
  --text-display: clamp(2.5rem, 6vw, 4.75rem);   /* 40 → 76 */
  --text-h2: clamp(2rem, 4vw, 3.25rem);          /* 32 → 52 */
  --text-h3: clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  --text-lede: clamp(1.125rem, 1.05rem + 0.3vw, 1.25rem);
  --text-body: 1.0625rem;
  --text-small: 0.875rem;
  --text-label: 0.75rem;

  --leading-display: 1.04;
  --leading-h2: 1.1;
  --leading-h3: 1.25;
  --leading-lede: 1.55;
  --leading-body: 1.6;
  --leading-label: 1.2;

  --tracking-display: -0.03em;
  --tracking-h2: -0.025em;
  --tracking-h3: -0.01em;
  --tracking-lede: -0.005em;
  --tracking-label: 0.08em;

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;   /* ceiling — nothing heavier, anywhere */

  /* --- SPACING · strict 4px base ---------------------------------------- */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-6: 24px;
  --s-8: 32px;
  --s-12: 48px;
  --s-16: 64px;
  --s-24: 96px;
  --s-32: 128px;
  --s-40: 160px;
  --s-50: 200px;

  /* Space BETWEEN sections must exceed space WITHIN them by at least 2×.
     Cramped section breaks are what make a minimal page read as cheap. */
  --section-pad: 72px;
  --gutter: 24px;

  --eyebrow-gap: var(--s-4);   /* eyebrow → heading */
  --heading-gap: var(--s-6);   /* heading → body    */
  --cta-gap: 40px;             /* body → CTA        */
  --card-pad: var(--s-8);
  --grid-gap: var(--s-6);

  /* --- LAYOUT ------------------------------------------------------------ */
  --container: 1200px;
  --container-wide: 1400px;
  --measure: 66ch;             /* hard cap on prose — 62–68ch */

  /* --- RADIUS · scales with element size -------------------------------- */
  --radius-sm: 6px;            /* buttons, chips, inputs   */
  --radius-md: 12px;           /* cards, panels            */
  --radius-lg: 16px;           /* product UI frames        */
  --radius-pill: 999px;        /* tags only                */

  /* --- MOTION ------------------------------------------------------------ */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --fast: 150ms;
  --base: 250ms;
  --slow: 450ms;
  --stagger: 60ms;
  --travel: 16px;              /* reveal distance — never exceed 24px */
  /* SMOOTH FADE — opacity and a short rise, and nothing else.

     Blur Fade was here and is gone. It was the only expensive thing on the
     page: `filter: blur()` re-rasters a composited layer on every frame it
     changes, and with several blocks arriving at once mid-scroll that was
     measured at 24fps on a throttled CPU. Everything else the reveal does is
     opacity and transform, which the compositor handles without touching
     raster at all.

     The curve matters more than the duration here. `--ease-out` is
     cubic-bezier(0.16, 1, 0.3, 1), an expo-out that covers most of its
     distance in the first fifth — which reads as a pop, and is right for a
     button and wrong for a paragraph arriving. This one is close to even, so
     the block simply rises and resolves. */
  --reveal-dur: 620ms;
  --reveal-ease: cubic-bezier(0.33, 0, 0.2, 1);
}

@media (min-width: 48em) {
  :root { --section-pad: 96px; --gutter: 40px; }
}

@media (min-width: 64em) {
  :root { --section-pad: 128px; --gutter: 64px; }
}

/* ============================================================================
   RESET
   ========================================================================= */

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

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

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  font-weight: var(--weight-regular);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Misaligned digits in a data table undo a lot of careful work. */
  font-variant-numeric: tabular-nums;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd, table { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
table { border-collapse: collapse; width: 100%; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

/* ============================================================================
   PRIMITIVES — the page is assembled from these, never authored ad hoc.
   ========================================================================= */

/* --- Section: full-bleed band, contained content ------------------------
   The single most important structural move in this idiom. Backgrounds and
   hairlines run edge to edge; content sits in the contained column. */
.p-section {
  padding-block: var(--section-pad);
  border-top: 1px solid var(--hairline-strong);
  /* The nav is sticky and 64px tall, so an anchored jump would land the
     section's top edge underneath it. */
  scroll-margin-top: 80px;
}

.p-section:first-child { border-top: 0; }
.p-section--sunken { background: var(--surface-sunken); }
.p-section--flush { padding-block: 0; border-top: 0; }

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

.p-container--wide { max-width: var(--container-wide); }

.p-measure { max-width: var(--measure); }

/* --- Eyebrow ------------------------------------------------------------
   Mono, uppercase, tertiary ink. Often paired with a numeral — cheap to
   build, disproportionately effective at making a page feel systematic. */
.p-eyebrow {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  line-height: var(--leading-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: var(--eyebrow-gap);
}

.p-eyebrow__num { color: var(--accent); }

.p-eyebrow__rule {
  flex: 1;
  height: 1px;
  background: var(--hairline);
  margin-left: var(--s-2);
}

/* --- Headings ----------------------------------------------------------- */
.p-display {
  font-size: var(--text-display);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

.p-h2 {
  font-size: var(--text-h2);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-h2);
  letter-spacing: var(--tracking-h2);
  text-wrap: balance;
}

.p-h3 {
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
  line-height: var(--leading-h3);
  letter-spacing: var(--tracking-h3);
}

.p-lede {
  font-size: var(--text-lede);
  line-height: var(--leading-lede);
  letter-spacing: var(--tracking-lede);
  color: var(--ink-secondary);
  max-width: var(--measure);
}

.p-body {
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--ink-secondary);
  max-width: var(--measure);
}

.p-small {
  font-size: var(--text-small);
  line-height: 1.5;
  color: var(--ink-tertiary);
}

.p-mono {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  line-height: var(--leading-label);
}

strong, b { font-weight: var(--weight-semibold); color: var(--ink); }

/* --- Buttons ------------------------------------------------------------
   No scale transforms — they read as toylike. Primary lifts 1px. */
.p-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  line-height: 1.2;
  text-decoration: none;
  transition: background-color var(--fast) var(--ease-out),
    border-color var(--fast) var(--ease-out),
    color var(--fast) var(--ease-out),
    transform var(--fast) var(--ease-out);
}

.p-btn__arrow { transition: transform var(--fast) var(--ease-out); }
.p-btn:hover .p-btn__arrow { transform: translate3d(2px, 0, 0); }

.p-btn--primary {
  background: var(--ink);
  color: var(--canvas);
}

.p-btn--primary:hover {
  background: var(--accent);
  color: var(--accent-contrast);
  transform: translate3d(0, -1px, 0);
}

.p-btn--secondary {
  border: 1px solid var(--hairline-strong);
  color: var(--ink);
}

.p-btn--secondary:hover {
  border-color: var(--ink);
  background: var(--surface);
}

/* Tertiary: text plus arrow, no border. */
.p-btn--tertiary {
  padding: 12px 0;
  color: var(--ink);
}

.p-btn--tertiary:hover { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .p-btn:hover { transform: none; }
}

/* --- Links --------------------------------------------------------------- */
a { color: var(--accent); }

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

/* Focus is always visible, never removed. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

:focus:not(:focus-visible) { outline: none; }

.p-skip {
  position: absolute;
  left: var(--s-4);
  top: var(--s-4);
  z-index: 100;
  padding: var(--s-2) var(--s-4);
  background: var(--ink);
  color: var(--canvas);
  border-radius: var(--radius-sm);
  font-size: var(--text-small);
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform var(--fast) var(--ease-out);
}

.p-skip:focus { transform: translateY(0); }

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

/* --- Card ---------------------------------------------------------------
   Elevation is border and background. Never a shadow, and never a shadow
   AND a border on the same element. Nothing moves on hover. */
.p-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-md);
  padding: var(--card-pad);
  transition: border-color var(--fast) var(--ease-out),
    background-color var(--fast) var(--ease-out);
}

.p-card:hover {
  border-color: var(--hairline-strong);
  background: var(--surface-sunken);
}

/* ============================================================================
   REVEAL — default to visible.
   The hidden state is applied only under `html.js`, which the head script
   sets and withdraws if the reveal script never loads. With JS off,
   everything simply renders.
   ========================================================================= */
.js .p-reveal {
  opacity: 0;
  transform: translate3d(0, var(--travel), 0);
  transition:
    opacity var(--reveal-dur) var(--reveal-ease),
    transform var(--reveal-dur) var(--reveal-ease);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

.js .p-reveal.is-revealed {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* ============================================================================
   REDUCED MOTION — everything must remain fully comprehensible with motion off
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .js .p-reveal { opacity: 1; transform: none; }
}
