/* ============================================================
   Constellation Labs. One-page site. "The Prism" register.
   Porcelain + ink, one jewel-teal accent used only as hairlines
   and rays (never a fill). Self-hosted type only, no CDN.
   Nothing loops, blinks, or pulses. Static hero is a portrait,
   not a live feed. A later phase mounts WebGL over #prism-stage.
   Phase 1 ships ZERO JavaScript and ZERO 3D.
   See THEME TOKENS block for the color system.
   ============================================================ */

/* ---- Self-hosted type. Three families, latin woff2, no CDN.
        Inter (body, variable 400-800), Space Grotesk (display,
        500/600), JetBrains Mono (labels/captions, 400/500). ---- */
@font-face {
  font-family:"Inter";
  font-style:normal;
  font-weight:400 800;
  font-display:swap;
  src:url("../fonts/inter-latin-var.woff2") format("woff2");
}
@font-face {
  font-family:"Space Grotesk";
  font-style:normal;
  font-weight:500;
  font-display:swap;
  src:url("../fonts/space-grotesk-latin-500.woff2") format("woff2");
}
@font-face {
  font-family:"Space Grotesk";
  font-style:normal;
  font-weight:600;
  font-display:swap;
  src:url("../fonts/space-grotesk-latin-600.woff2") format("woff2");
}
@font-face {
  font-family:"JetBrains Mono";
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url("../fonts/jetbrains-mono-latin-400.woff2") format("woff2");
}
@font-face {
  font-family:"JetBrains Mono";
  font-style:normal;
  font-weight:500;
  font-display:swap;
  src:url("../fonts/jetbrains-mono-latin-500.woff2") format("woff2");
}

/* ============================================================
   THEME TOKENS (Porcelain & Ink)
   All color rules reference these tokens, never raw hex.
   Committed single light register; every color painted explicitly.
     --porcelain  page field (bright)
     --surface    soft raised band
     --ink        primary text / wordmark
     --graphite   secondary text
     --teal       the one accent, hairlines + rays only, never a fill
     --panel      deep panel (reserved; near-white text lives only here)
     --hairline   quiet rule on the porcelain field
   ============================================================ */
:root {
  --porcelain:       #F7F6F3;
  --surface:         #E9E7E1;
  --ink:             #111114;
  --graphite:        #3A3A3F;
  --teal:            #00A88F;
  --panel:           #0B0B0D;

  --ink-soft:        #45454B;
  --hairline:        #D8D5CD;
  --hairline-strong: #C4C0B6;
  --teal-hair:       rgba(0,168,143,0.55);
  --teal-wash:       rgba(0,168,143,0.10);
  --focus:           #111114;
  --on-panel:        #F1F0EC;
}
/* ============================================================
   END THEME TOKENS
   ============================================================ */

/* ---- Structural tokens (register mechanics, not theme) ---- */
:root {
  --sans:"Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --display:"Space Grotesk", "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono:"JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  --maxw: 880px;
  --gutter: 20px;
  --header-h: 72px;
}
@media (min-width:760px) { :root { --gutter:40px; } }

/* ---- Reset / base ---- */
*,*::before,*::after { box-sizing:border-box; }
html,body { margin:0; }
/* scroll-behavior:smooth gives the nav anchors a smooth glide (and drives
   the System link into the immersive region); the reduced-motion guard
   below overrides it back to auto. */
html { -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
body {
  background:var(--porcelain); color:var(--ink);
  font-family:var(--sans); font-size:16px; line-height:1.5;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  /* clip (not hidden) contains horizontal overflow WITHOUT making <body> a
     scroll container, which would otherwise break the immersive region's
     position:sticky pin. */
  overflow-x:clip;
}
a { color:inherit; text-decoration:none; }
img,svg { display:block; max-width:100%; height:auto; }
h1,h2,h3 { margin:0; font-family:var(--display); font-weight:600; }
p { margin:0 0 1em; }
ul,ol { margin:0; padding:0; list-style:none; }
dl,dd { margin:0; }

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

/* Reduced-motion guard: no motion ships in Phase 1; this holds the
   line for later phases so any transition/animation is disarmed. */
@media (prefers-reduced-motion: reduce) {
  *,*::before,*::after {
    animation-duration:0.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:0.001ms !important;
    scroll-behavior:auto !important;
  }
}

.skip-link {
  position:absolute; left:12px; top:-60px; z-index:100;
  background:var(--ink); color:var(--porcelain); border-radius:4px;
  padding:10px 16px; font-weight:600;
}
.skip-link:focus { top:12px; }
@media (prefers-reduced-motion: no-preference) { .skip-link { transition:top .15s ease; } }

.wrap { max-width:var(--maxw); margin:0 auto; padding:0 var(--gutter); }

/* Mono kicker: the "instrument" tell. Coordinates, index labels. */
.kicker {
  display:block; font-family:var(--mono); font-weight:400; font-size:11.5px;
  letter-spacing:0.22em; text-transform:uppercase; color:var(--graphite);
  margin:0 0 14px;
}

/* ---- Header (Orbit Core mark beside the wordmark) ---- */
.site-header {
  position:sticky; top:0; z-index:50;
  background:var(--porcelain);
  border-bottom:1px solid var(--hairline);
}
.site-header .wrap { min-height:var(--header-h); display:flex; align-items:center; justify-content:space-between; gap:16px; padding-top:10px; padding-bottom:10px; }
.lockup { display:flex; align-items:center; gap:10px; }
.wordmark { display:flex; align-items:baseline; gap:0.28em; flex-wrap:nowrap; }
.wm-1, .wm-2 { font-family:var(--display); font-weight:600; font-size:clamp(15px, 3.4vw, 18px); letter-spacing:0.04em; text-transform:uppercase; white-space:nowrap; }

/* Orbit Core mark: black master reads on the bright porcelain field
   unchanged (never redrawn). Square canvas (viewBox 0 0 300 300),
   never stretched (height set, width:auto holds the native 1:1).
   The kit's 32px floor is measured on the mark's tight ink geometry
   (236.9x200.6 bbox, ~79% of the canvas width), so the canvas renders
   bigger than 32px to clear it: 48px canvas -> ~38px ink width here. */
.site-header .mark { flex:none; width:auto; height:48px; display:block; }

/* Orbit Core scroll-progress map. The header mark is inlined as SVG so
   its five fixed contact dots can double as a live scroll-progress
   indicator: the logo-progress module adds .is-lit to a dot as its
   mapped section is reached on the way down, so the mark is unlit at
   the hero and fully lit at Contact. Base state is the untouched black
   mark (the dots inherit #000 from the mark's own group), so with no
   JS the header shows the normal black mark and nothing is lost. The
   lit treatment is restrained: the dot fills the jewel teal and gains
   a soft teal glow; the arcs and faceted core stay black throughout.
   The glow radius is in the mark's 300-unit user space (drop-shadow on
   an SVG element scales with the viewBox), tuned to read as a soft
   halo at the 48px header size. */
.orbit-dot.is-lit { fill:var(--teal); filter:drop-shadow(0 0 9px rgba(0,168,143,0.65)); }
/* Motion only under no-preference; the reduced-motion guard above
   disarms the transition-duration, so the fill flips instantly (still
   reflecting position) with no animation. */
@media (prefers-reduced-motion: no-preference) { .orbit-dot { transition:fill .3s ease, filter .3s ease; } }

/* Orbit satellite: keeps the header "Orbit Core" mark gently alive on a
   continuous subtle loop, independent of and layered under the five
   scroll-lit contact dots. One small jewel-teal dot (r 10 in the 300-unit
   view space, ~1.6px at the 48px header size, slightly smaller than the
   r 12 contact dots), authored at cx 150 cy 66 = radius 84 from the mark
   centre, so it rides between the faceted-core edge and the contact dots
   (which sit ~100+ user-units out) with no collision. The group is rotated
   0->360deg about the MARK CENTRE 150,150 by an SVG SMIL <animateTransform
   type="rotate" from="0 150 150" to="360 150 150"> inlined in the mark (see
   index.html), NOT a CSS animation: SMIL's rotate carries its pivot in the
   attribute itself, so it locks to 150,150 reliably on every engine
   including older Safari, where a CSS transform-box:view-box + pixel
   transform-origin pivot was historically flaky. Reduced motion is handled
   in JS (logo-progress.js pauses the mark's SMIL timeline at t=0) because
   SMIL ignores the CSS prefers-reduced-motion media query; the page-wide
   reduced-motion guard above cannot stop SMIL. Negligible cost, no external
   request. Only the jewel's static paint lives here. */
.orbit-jewel { fill:var(--teal); opacity:0.9; filter:drop-shadow(0 0 6px var(--teal-hair)); }

.nav { display:flex; align-items:center; }
.nav-links { display:flex; align-items:center; gap:8px 22px; }
.nav-links a { font-family:var(--mono); font-size:12px; font-weight:400; letter-spacing:0.06em; color:var(--graphite); display:inline-flex; align-items:center; min-height:44px; white-space:nowrap; }
@media (prefers-reduced-motion: no-preference) { .nav-links a { transition:color .15s ease; } }
.nav-links a:hover { color:var(--ink); }

/* Zero-JS mobile pattern: no hamburger, so the in-page nav rows hide
   under the breakpoint where five links would crowd the header; the
   wordmark alone always fits. */
@media (max-width:759px) { .nav { display:none; } }
@media (max-width:600px) { :root { --header-h:68px; } }

/* Anchor scroll offset for the sticky header. */
[id] { scroll-margin-top:calc(var(--header-h) + 12px); }

/* ============================================================
   THE HERO - porcelain register, full-bleed (fills the viewport
   below the sticky header). The optic fills the hero; the copy is
   overlaid on the clear left and the honest caption sits lower-left,
   matching the locked look spike. #prism-stage is the full-bleed
   layer: it paints the static CSS optical portrait (the fallback),
   and Phase 2 mounts the WebGL refractive-glass scene into it,
   fading the canvas in over the poster. The content sections below
   scroll normally. Ambient only: no scroll-through navigation.
   ============================================================ */
.hero {
  position:relative; overflow:hidden;
  background:var(--porcelain);
  min-height:calc(100vh - var(--header-h));
  min-height:calc(100svh - var(--header-h));
}

/* The pin wraps everything painted in the hero (poster + WebGL canvas +
   projected labels + copy). It is the positioning context for the absolute
   layers. In the fallback it is a plain relative box that fills the hero,
   so the static poster + copy read exactly as Phase 1/2. */
.hero-pin { position:relative; }

/* ---- Phase 3: the immersive scroll-flight region. Activated ONLY by the
   scene module (class added on successful mount, past all gates, motion
   allowed). Without it the hero is a normal one-viewport section and the
   page is an ordinary scroll document; the 3D navigation is purely additive.
   The region grows to ~3.6 viewport-heights and the pin sticks under the
   header, so scroll progress flies the camera from the prism to the
   constellation and parks. overflow:visible on the region is required for
   the sticky pin; the pin clips the scene to the viewport. ---- */
.hero.is-immersive {
  overflow:visible;
  min-height:360vh;
  min-height:360svh;
}
.hero.is-immersive .hero-pin {
  position:sticky; top:var(--header-h);
  height:calc(100vh - var(--header-h));
  height:calc(100svh - var(--header-h));
  overflow:hidden;
}
.hero.is-immersive .hero-inner { will-change:opacity, transform; }

/* Projected label layer for the five constellation nodes. Fixed within the
   pin, non-interactive, aria-hidden; the real content is the in-DOM five
   component register in The System. Labels are positioned per frame by the
   scene module (world -> screen) and revealed on approach. */
.constel-layer {
  position:absolute; inset:0; z-index:2; pointer-events:none; overflow:hidden;
}
.constel-label {
  position:absolute; top:0; left:0; z-index:0; width:216px; margin:0;
  opacity:0; transform:translate(-9999px,-9999px);
  will-change:transform, opacity;
  padding:11px 14px 12px;
}
/* Soft radial porcelain glow behind the label: a gentle legibility halo
   with NO hard box edge (replaces the Phase 3 rectangular scrim), so the
   name/role read as floating text with their own light. Sits behind the
   text (z-index:-1 within the label's stacking context). */
.constel-label::before {
  content:""; position:absolute; z-index:-1; pointer-events:none;
  left:50%; top:48%; transform:translate(-50%,-50%);
  width:152%; height:184%;
  background:radial-gradient(50% 50% at 50% 50%,
    rgba(247,246,243,0.96) 0%, rgba(247,246,243,0.84) 38%,
    rgba(247,246,243,0.46) 64%, rgba(247,246,243,0) 100%);
}
/* A thin teal tick linking each label up to its node (the label is
   projected ~16px below the node's screen point), so the pairing is
   unambiguous. Teal as a single hairline, never a fill. */
.constel-label::after {
  content:""; position:absolute; z-index:-1; pointer-events:none;
  left:50%; top:-15px; width:1px; height:15px; transform:translateX(-50%);
  background:linear-gradient(to top, var(--teal-hair), rgba(0,168,143,0));
}
.constel-label .cl-head { display:flex; align-items:baseline; gap:9px; }
.constel-label .cl-letter {
  font-family:var(--mono); font-weight:500; font-size:14px; color:var(--teal);
}
.constel-label .cl-name {
  font-family:var(--display); font-weight:600; font-size:15px;
  letter-spacing:.01em; color:var(--ink);
}
.constel-label .cl-role {
  display:block; margin-top:5px;
  font-size:12.5px; line-height:1.42; color:var(--ink-soft);
  opacity:0;
}

/* ---- Mobile (narrow / portrait) constellation labels ----
   On a ~400px phone the five projected labels crowd into one spot, overlap,
   and clip off the right edge. Under 760px the scene module switches to a
   one-at-a-time guided reveal (only the active node's label is lit, driven
   by scroll progress). Here we pin that single label to a centered band and
   clamp its width to the viewport, so it can NEVER overlap another label or
   run off screen. The per-frame projected transform the module sets for the
   desktop path is overridden by the !important centered transform, so a
   desktop->mobile resize leaves no stale off-screen position. Desktop
   (>760px) keeps the projected labels unchanged. ---- */
@media (max-width:760px) {
  .constel-label {
    left:50%; right:auto; top:auto; bottom:16%;
    width:min(320px, calc(100vw - 40px));
    transform:translateX(-50%) !important;
    padding:14px 18px 16px; text-align:center;
  }
  .constel-label .cl-head { justify-content:center; gap:10px; }
  .constel-label .cl-letter { font-size:16px; }
  .constel-label .cl-name { font-size:19px; letter-spacing:.005em; }
  .constel-label .cl-role { font-size:13.5px; line-height:1.5; margin-top:8px; opacity:1; }
  /* Keep the soft porcelain legibility glow; drop the teal tick (the mobile
     label is centered, not projected down onto a node). */
  .constel-label::before { width:150%; height:210%; }
  .constel-label::after { display:none; }
}

/* Full-bleed optic layer: the static CSS portrait AND the WebGL mount.
   This same treatment is the no-JS / reduced-motion / no-WebGL / init-fail
   fallback. An optical bench: faint ruled porcelain fading off the left, a
   soft key highlight, a glass prism silhouette, one teal ray, a teal wash
   pooling below. aria-hidden; it carries no information. */
.prism-stage {
  position:absolute; inset:0; z-index:0;
  background-color:var(--porcelain);
  background-image:
    radial-gradient(40% 42% at 70% 62%, var(--teal-wash), rgba(0,168,143,0) 70%),
    radial-gradient(40% 46% at 32% 26%, rgba(255,255,255,0.9), rgba(255,255,255,0) 72%),
    linear-gradient(90deg, var(--porcelain) 0%, rgba(247,246,243,0) 44%),
    repeating-linear-gradient(90deg, rgba(58,58,63,0.05) 0 1px, transparent 1px 30px);
  overflow:hidden;
}
/* The glass prism, seated right of center. A soft porcelain-to-surface
   gradient with a bright facet edge; static, faceted, jewel-like. */
.prism-stage::before {
  content:""; position:absolute; right:20%; top:50%;
  width:min(24vw,320px); aspect-ratio:1 / 0.98; transform:translateY(-50%);
  clip-path:polygon(50% 7%, 93% 88%, 7% 88%);
  background:
    linear-gradient(120deg,
      rgba(255,255,255,0.95) 0%,
      rgba(233,231,225,0.55) 46%,
      rgba(255,255,255,0.85) 62%,
      rgba(233,231,225,0.65) 100%);
  box-shadow:0 18px 44px rgba(17,17,20,0.10);
}
/* One teal ray leaving the prism to the lower right. Hairline, never a fill. */
.prism-stage::after {
  content:""; position:absolute; right:8%; top:60%;
  width:34%; height:1px; transform:rotate(-8deg); transform-origin:left center;
  background:linear-gradient(90deg, var(--teal-hair), rgba(0,168,143,0));
}

/* The WebGL canvas, mounted into #prism-stage and faded in over the poster
   so there is never a flash of empty canvas. It never captures pointer
   events, so the overlaid copy and links stay live. */
.prism-canvas {
  position:absolute; inset:0; z-index:1;
  width:100%; height:100%; display:block;
  opacity:0; pointer-events:none;
}
.prism-canvas.is-visible { opacity:1; }
@media (prefers-reduced-motion: no-preference) {
  .prism-canvas { transition:opacity .6s ease; }
}

/* Overlaid copy: clear left, vertically centered in the full-bleed hero. */
.hero-inner {
  position:relative; z-index:2;
  max-width:var(--maxw); width:100%; margin:0 auto;
  padding:0 var(--gutter);
  min-height:calc(100vh - var(--header-h));
  min-height:calc(100svh - var(--header-h));
  display:flex; flex-direction:column; justify-content:center;
}
.hero-content { max-width:min(46ch,52vw); }
.hero h1 {
  font-family:var(--display); font-weight:500;
  font-size:clamp(2rem, 3.4vw + 1.1rem, 3.1rem); line-height:1.04;
  letter-spacing:-0.015em; color:var(--ink);
}
/* One teal hairline under the headline: teal as a single ray, never a fill. */
.hero-rule { width:64px; height:2px; background:var(--teal); border:0; margin:24px 0 22px; }
.hero .lead { font-size:17px; line-height:1.62; color:var(--graphite); max-width:52ch; margin:0; }

/* Honest instrument caption, mono + dim, lower-left of the hero. */
.hero-caption {
  position:absolute; left:var(--gutter); right:var(--gutter);
  bottom:clamp(22px,4vh,40px); margin:0;
  font-family:var(--mono); font-size:11.5px; letter-spacing:.05em; color:var(--graphite);
}
.hero-caption .dot { color:var(--teal); }

@media (max-width:760px) {
  .hero-content { max-width:min(60ch,86vw); }
  .prism-stage::before { right:8%; width:44vw; }
}

/* ---- Section rhythm. Tone alternates on the section itself,
        boxed within --maxw, never full-bleed. ---- */
.section { padding:64px var(--gutter); max-width:var(--maxw); margin:0 auto; }
.section.tone-deep {
  background:var(--surface);
  border-top:1px solid var(--hairline);
  border-bottom:1px solid var(--hairline);
}
.section-copy { max-width:640px; }
.section h1, .section h2 {
  font-family:var(--display); font-weight:600;
  font-size:clamp(1.55rem, 1.6vw + 1.1rem, 2.05rem); line-height:1.12;
  letter-spacing:-0.01em; margin:0 0 16px;
}
.section p { font-size:15.5px; line-height:1.64; color:var(--ink-soft); }
.section p:last-child { margin-bottom:0; }
@media (max-width:600px) { .section { padding:48px var(--gutter); } }

/* ---- 01 / The System: the five-layer register rows ---- */
.reg { border-top:1px solid var(--hairline-strong); margin-top:32px; }
.reg-row {
  display:grid; grid-template-columns:200px 1fr; gap:20px 24px; padding:22px 0;
  border-bottom:1px solid var(--hairline); align-items:baseline;
}
.reg-term { font-family:var(--display); font-weight:600; font-size:14.5px; letter-spacing:.01em; }
.reg-def { font-size:14.5px; line-height:1.64; color:var(--ink-soft); margin:0; }
@media (max-width:640px) { .reg-row { grid-template-columns:1fr; gap:8px; } }

/* ---- 01 / The System: the five named components, entered as an
        accessible definition register (the estate the chart charted).
        Bayer designation + name + one-line role. ---- */
.components-label {
  display:block; font-family:var(--mono); font-weight:400; font-size:11px;
  letter-spacing:.22em; text-transform:uppercase; color:var(--graphite);
  margin:44px 0 0;
}
.components { border-top:1px solid var(--ink); margin-top:16px; }
.components > div {
  display:grid; grid-template-columns:34px 200px 1fr; gap:6px 22px;
  padding:18px 0; border-bottom:1px solid var(--hairline); align-items:baseline;
}
.comp-bayer { font-family:var(--mono); font-weight:500; font-size:15px; color:var(--teal); }
.comp-name { font-family:var(--display); font-weight:600; font-size:15px; letter-spacing:.01em; color:var(--ink); }
.comp-role { font-size:14px; line-height:1.6; color:var(--ink-soft); margin:0; }
@media (max-width:640px) {
  .components > div { grid-template-columns:28px 1fr; }
  .comp-role { grid-column:1 / -1; }
}

/* ---- 01 / The System: the component mini-nodes. A single shared,
        lightweight WebGL canvas (assets/js/component-nodes.*.js) mounts
        over the list's left gutter and draws a small rotating 3D crystal
        in each row's Bayer slot, a mini echo of that component's
        constellation node. The canvas is a child of the <dl>, so it
        SCROLLS WITH the list; it is absolutely positioned and never
        captures pointer events, so the list stays fully interactive.
        Progressive enhancement: with no JS / no WebGL / reduced-motion*
        / Save-Data / low memory the canvas never mounts and the Greek
        letters render exactly as they do now. Once it mounts, .has-nodes
        visually hides the glyph (box preserved, still in the DOM, still
        aria-hidden decoration) and the crystal takes its place.
        HTML-validity: the canvas is NOT a child of the <dl> (a <canvas> is
        not valid <dl> content). It mounts into .components-stage, a plain
        position:relative wrapper around just the <dl>, so the absolutely-
        positioned canvas overlays the list exactly and scrolls with it. The
        wrapper adds no box of its own (the <dl>'s own margins collapse
        through the un-padded, non-BFC wrapper), so layout is pixel-identical
        to before and slot alignment is measured in the stage's own space. ---- */
.components-stage { position:relative; }
.component-nodes-canvas {
  position:absolute; inset:0; z-index:1;
  width:100%; height:100%; display:block;
  opacity:0; pointer-events:none;
}
.component-nodes-canvas.is-visible { opacity:1; }
@media (prefers-reduced-motion: no-preference) {
  .component-nodes-canvas { transition:opacity .5s ease; }
}
/* Glyph hidden but its box kept, so its slot stays measurable and the
   row rhythm is unchanged; the letter remains in the DOM for the no-3D
   fallback. */
.components.has-nodes .comp-bayer { color:transparent; }

/* ---- 02 / The Method ---- */
.method-body { max-width:680px; }

/* ---- 03 / Born Here + 04 / The Lab + Who: prose + outbound links ---- */
.prose { max-width:680px; }
.prose p:last-child { margin-bottom:0; }
.outbound { font-weight:500; color:var(--ink); border-bottom:1.5px solid var(--teal); }
@media (prefers-reduced-motion: no-preference) { .outbound { transition:border-color .15s ease; } }
.outbound:hover { border-bottom-color:var(--ink); }

/* ---- Footer ---- */
.site-footer { border-top:1px solid var(--hairline); background:var(--porcelain); }
.footer-top { max-width:var(--maxw); margin:0 auto; padding:40px var(--gutter) 28px; }
.footer-brand .lockup { gap:8px; }
.footer-brand .mark { flex:none; width:auto; height:44px; display:block; }
.footer-brand .name { font-family:var(--display); font-weight:600; font-size:16px; letter-spacing:0.03em; text-transform:uppercase; }
.footer-brand p { font-size:13.5px; color:var(--ink-soft); max-width:440px; line-height:1.6; margin:10px 0 0; }
.footer-rule { border-top:1px solid var(--hairline); }
.footer-rule .wrap { padding:18px var(--gutter); display:flex; justify-content:space-between; flex-wrap:wrap; gap:8px; font-family:var(--mono); font-size:11.5px; letter-spacing:.04em; color:var(--graphite); }

/* ---- 404 page + the shared button ---- */
.notfound-copy { max-width:520px; color:var(--ink-soft); font-size:16px; line-height:1.6; margin:18px 0 30px; }
.btn {
  display:inline-flex; align-items:center; justify-content:center; gap:9px;
  font-family:var(--display); font-weight:500; font-size:15px; line-height:1;
  border:1.5px solid var(--ink); border-radius:3px; cursor:pointer;
  padding:14px 24px; min-height:48px;
  color:var(--porcelain); background:var(--ink);
}
@media (prefers-reduced-motion: no-preference) {
  .btn { transition:background-color .15s ease, color .15s ease, border-color .15s ease; }
}
.btn:hover { background:var(--graphite); border-color:var(--graphite); }
