/* ============================================================================
   arx52.css — the shared design system.

   Tokens below are lifted VERBATIM from pitches/arx52-deck.html so the deck and
   the site cannot drift. If a token changes in one place it must change in both;
   better still, have the deck consume this file too.

   Design decisions this encodes (briefing §10, D5):
     - NON-DARK, deliberately. Warm paper ground, never a dark theme. There is no
       prefers-color-scheme block here and that is intentional, not an omission.
     - ONE accent (brass). Never a second.
     - System fonts only. No webfonts, no network requests.
     - Spacing ladder 8/16/24/40/64/96 and nothing else.
   ========================================================================== */

:root{
  /* ground ─ L*96.6, channel spread 7 */
  --ground:#F7F5F0; --raised:#FFFFFF; --well:#EFEDE7;
  /* ink */
  --ink:#16181C; --ink-2:#55524B; --muted:#6E685E; --decor:#9A958B;
  /* rules */
  --hair:#DCD8D0; --hair-strong:#C9C5BC;
  /* single accent */
  --brass:#8A6B2E; --brass-rule:#A9853F;

  --sans-d:"Segoe UI Variable Display",ui-sans-serif,system-ui,-apple-system,"Helvetica Neue",Helvetica,Arial,sans-serif;
  --sans-t:"Segoe UI Variable Text",ui-sans-serif,system-ui,-apple-system,"Helvetica Neue",Helvetica,Arial,sans-serif;
  --sans-s:"Segoe UI Variable Small",ui-sans-serif,system-ui,-apple-system,"Helvetica Neue",Helvetica,Arial,sans-serif;
  --mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,"Cascadia Mono","Segoe UI Mono",Consolas,"Liberation Mono",monospace;

  /* type scale — base 17px, ratio 1.25. two-anchor fluid ramps. */
  --t-wordmark:  clamp(3.25rem,  2.05rem + 4.85vw, 5.9rem);
  --t-display-xl:clamp(2.5rem,   1.55rem + 3.90vw, 5.0625rem);
  --t-display:   clamp(2.125rem, 1.40rem + 2.95vw, 4.0625rem);
  --t-h1:        clamp(1.75rem,  1.44rem + 1.29vw, 2.594rem);
  --t-h2:        clamp(1.4375rem,1.20rem + 0.95vw, 2.0625rem);
  --t-h3:        clamp(1.25rem,  1.10rem + 0.62vw, 1.656rem);
  --t-lede:      clamp(1.15rem,  1.10rem + 0.24vw, 1.3125rem);
  --t-body:1.0625rem; --t-small:.9375rem; --t-label:.845rem; --t-micro:.6875rem;

  /* spacing ladder — 8/16/24/40/64/96, nothing else */
  --s1:8px; --s2:16px; --s3:24px; --s4:40px; --s5:64px; --s6:96px;

  --gut:clamp(1.5rem,5vw,4rem);
  --rail:clamp(3rem,7vw,7.5rem);
  --measure:68ch;
}

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

/* ── scroll behaviour ──────────────────────────────────────────────────────
   Page Down traverses section by section. That is done in JS (see index.html),
   NOT with CSS scroll-snap.

   ⚠️ DO NOT REINTRODUCE scroll-snap-type HERE. It was tried and it FROZE THE
   PAGE: with `scroll-snap-type: y proximity` and sections of mixed height, five
   consecutive 568px scrolls moved the page a total of 0px — scrollY pinned at 57
   while snap dragged every attempt back to the first snap point. Verified by
   disabling snap live, after which the identical scrolls worked perfectly
   (0 → 568 → 1136 → 1704 → 2272). Mandatory snapping is worse still: it traps
   the reader inside any section taller than the viewport.

   The JS handler is deterministic, leaves wheel and trackpad scrolling
   completely native so long sections stay readable, and cannot wedge the page. */
html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  scroll-padding-top:4px;
}
body{
  margin:0;background:var(--ground);color:var(--ink);
  font-family:var(--sans-t);font-size:var(--t-body);line-height:1.65;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
::selection{background:rgba(138,107,46,.16)}
a{color:inherit}
img,svg{max-width:100%;height:auto}

/* ── layout ───────────────────────────────────────────────────────────────── */
.wrap{max-width:1180px;margin:0 auto;padding:0 var(--gut)}
.rail{padding-left:var(--rail)}
@media(max-width:760px){.rail{padding-left:0}}

/* ── the sheet rail (adapted from the deck's drawing-sheet furniture) ──────
   The deck stamps SHEET 01/12 · ISSUE B · STATUS Private. Those are
   document-control labels and are meaningless on a public page, so the DEVICE
   survives and the labels change: a fixed masthead and a hairline tick scale. */
.masthead{
  position:sticky;top:0;z-index:50;background:rgba(247,245,240,.92);
  backdrop-filter:saturate(140%) blur(8px);
  border-bottom:1px solid var(--hair);
}
.masthead .bar{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);min-height:60px}
.wordmark-sm{font-family:var(--sans-d);font-weight:600;font-size:1.0625rem;letter-spacing:-.01em;text-decoration:none}
.coords{
  font-family:var(--mono);font-size:var(--t-micro);letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;justify-content:flex-end;
}
.coords .dot{color:var(--decor)}
@media(max-width:600px){.coords .opt{display:none}}

/* ── sections ──────────────────────────────────────────────────────────────
   Each section is at least a viewport tall with its content vertically centred,
   so Page Down lands on a composed page rather than mid-paragraph.

   svh (small viewport height) rather than vh, so mobile browsers do not jump
   when the URL bar hides — vh there is the LARGE height and leaves content
   clipped on first paint. vh first as the fallback for older engines.

   Top padding must stay >= the masthead height (61px) or a snapped heading
   sits underneath it. --s5 is 64px, which clears it. */
section{
  min-height:100vh;
  min-height:100svh;
  display:flex;
  flex-direction:column;
  padding:var(--s5) 0;
  border-top:1px solid var(--hair);
}
section:first-of-type{border-top:0}

/* Centring via AUTO MARGINS, not justify-content:center — and the difference
   matters. justify-content:center on a flex column whose content is taller than
   the container overflows it in BOTH directions, putting the top of the content
   above the scroll origin where it can never be reached. Auto margins centre
   when there is spare room and collapse to zero when there is not, so a short
   section is composed and centred while a tall one simply starts at the top and
   stays readable. Handles every viewport with no per-section class. */
section > .wrap{width:100%;margin-block:auto}
.label{
  display:block;font-family:var(--sans-s);font-size:var(--t-label);font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--brass);margin-bottom:var(--s2);
}
h1,h2,h3{font-family:var(--sans-d);font-weight:600;letter-spacing:-.018em;margin:0;text-wrap:balance}
h1{font-size:var(--t-display);line-height:1.04}
h2{font-size:var(--t-h1);line-height:1.1;max-width:26ch}
h3{font-size:var(--t-h3);line-height:1.25;letter-spacing:-.01em}
p{margin:0}
.lede{font-size:var(--t-lede);color:var(--ink-2);max-width:60ch;line-height:1.6}
.prose{color:var(--ink-2);max-width:var(--measure)}
.prose + .prose{margin-top:var(--s3)}
.prose strong{color:var(--ink);font-weight:600}
.stack-2 > * + *{margin-top:var(--s2)}
.stack-3 > * + *{margin-top:var(--s3)}
.stack-4 > * + *{margin-top:var(--s4)}
.stack-5 > * + *{margin-top:var(--s5)}

/* the measured tick scale — the deck's precision device */
/* The deck's two-tier graduation - minor ticks every 8px, major every 40px -
   rather than the single row the site had. Same device, properly drawn. */
.scale{position:relative;height:11px;width:100%;max-width:420px}
.scale::before{
  content:"";position:absolute;inset:auto 0 0 0;height:6px;
  background-image:repeating-linear-gradient(to right,var(--hair-strong) 0 1px,transparent 1px 8px);
}
.scale::after{
  content:"";position:absolute;inset:auto 0 0 0;height:11px;
  background-image:repeating-linear-gradient(to right,var(--hair-strong) 0 1px,transparent 1px 40px);
}
.rule-brass{height:1px;background:var(--brass-rule);width:200px;max-width:40%}

/* ── hero ───────────────────────────────────────────────────────────────────
   Tighter padding than a normal section because on the one-page holder this IS
   the page - the whole thing should land on one screen without scrolling. */
.hero{padding:var(--s4) 0}
.hero .wordmark{
  font-family:var(--sans-d);font-weight:500;font-size:var(--t-wordmark);
  line-height:.92;letter-spacing:-.03em;margin:var(--s3) 0 var(--s4);
}
.hero .tagline{font-family:var(--sans-d);font-size:var(--t-h1);line-height:1.18;font-weight:400;color:var(--ink);max-width:34ch;letter-spacing:-.015em}
.hero .tagline strong{font-weight:700}
.hero .ground{margin-top:var(--s3);color:var(--muted);max-width:52ch;font-size:var(--t-small);line-height:1.6}

/* ── numbered list (capabilities) ─────────────────────────────────────────── */
.rows{margin-top:var(--s4);border-top:1px solid var(--hair)}
.row{display:grid;grid-template-columns:2.5rem minmax(0,15rem) minmax(0,1fr);gap:var(--s3);
  padding:var(--s2) 0;border-bottom:1px solid var(--hair);align-items:start}
.row .n{font-family:var(--mono);font-size:var(--t-micro);color:var(--brass);padding-top:.45rem;letter-spacing:.08em}
.row .t{font-family:var(--sans-d);font-weight:600;font-size:var(--t-body);line-height:1.35;letter-spacing:-.01em}
.row .d{color:var(--ink-2);font-size:var(--t-small);line-height:1.6}
@media(max-width:820px){
  .row{grid-template-columns:2rem minmax(0,1fr)}
  .row .d{grid-column:2}
}

/* ── grid of principles ───────────────────────────────────────────────────── */
.grid-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s4);margin-top:var(--s4)}
.grid-2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s4);margin-top:var(--s4)}
.grid-3 p,.grid-2 p{color:var(--ink-2);font-size:var(--t-small);line-height:1.6;margin-top:var(--s1)}
@media(max-width:860px){.grid-3{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:620px){.grid-3,.grid-2{grid-template-columns:1fr;gap:var(--s4)}}

/* ── proof cards (the live, checkable things) ─────────────────────────────── */
.proof{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s3);margin-top:var(--s4)}
.card{
  background:var(--raised);border:1px solid var(--hair);padding:var(--s3);
  display:flex;flex-direction:column;gap:var(--s1);
}
.card .k{font-family:var(--mono);font-size:var(--t-micro);letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
  display:block;border-bottom:1px solid var(--hair);padding-bottom:var(--s1)}
.card h3{font-size:var(--t-body)}
.card p{color:var(--ink-2);font-size:var(--t-small);line-height:1.58;flex:1}
.card a{
  font-family:var(--sans-s);font-size:var(--t-small);font-weight:600;color:var(--brass);
  text-decoration:none;border-bottom:1px solid var(--hair-strong);align-self:flex-start;padding-bottom:2px;
}
.card a:hover{border-bottom-color:var(--brass)}
@media(max-width:860px){.proof{grid-template-columns:1fr}}

/* ── track record ─────────────────────────────────────────────────────────── */
.record{margin-top:var(--s3);border-top:1px solid var(--hair)}
.record li{list-style:none;padding:var(--s2) 0;border-bottom:1px solid var(--hair);color:var(--ink-2);
  display:flex;gap:var(--s2)}
.record{padding:0;margin-left:0}
.record li::before{content:"—";color:var(--brass);flex:none}

/* ── steps ────────────────────────────────────────────────────────────────── */
.steps{margin-top:var(--s4);border-top:1px solid var(--hair)}
.step{display:grid;grid-template-columns:2.5rem minmax(0,10rem) minmax(0,1fr);gap:var(--s3);
  padding:var(--s3) 0;border-bottom:1px solid var(--hair);align-items:baseline}
.step .n{font-family:var(--mono);font-size:var(--t-micro);color:var(--brass);letter-spacing:.08em}
.step h3{font-family:var(--sans-d);font-size:var(--t-h3);font-weight:600}
.step p{color:var(--ink-2);font-size:var(--t-small);line-height:1.6}
@media(max-width:820px){.step{grid-template-columns:2rem minmax(0,1fr)}.step p{grid-column:2}}

/* ── contact ──────────────────────────────────────────────────────────────── */
.reply{
  margin-top:var(--s4);display:inline-flex;align-items:center;gap:var(--s2);
  font-family:var(--sans-d);font-size:var(--t-h3);font-weight:600;letter-spacing:-.01em;
  color:var(--ink);text-decoration:none;border-bottom:1.5px solid var(--brass-rule);padding-bottom:var(--s1);
}
.reply:hover{color:var(--brass)}

/* ── footer ───────────────────────────────────────────────────────────────── */
footer{border-top:1px solid var(--hair);background:var(--well);padding:var(--s5) 0}
footer .fine{color:var(--muted);font-size:var(--t-small);line-height:1.7;max-width:70ch}
footer .fw{font-family:var(--sans-d);font-weight:600;color:var(--ink-2);margin-bottom:var(--s1)}

/* ── motion — heavily restrained (briefing §10) ────────────────────────────
   The reveal is PROGRESSIVE ENHANCEMENT, deliberately inverted from the usual
   pattern: content is visible by default, and is only hidden once an inline
   script in <head> has confirmed the browser can reveal it again (.js-reveal).

   Why it is built this way: if the script fails, is blocked, or the browser
   never delivers IntersectionObserver callbacks, the page still reads perfectly
   — it simply does not animate. The copy is the product; it must never depend on
   a script succeeding. A fade-in that silently fails leaves a blank page, which
   for a site being sent to a board is the worst possible failure. */
.rise{opacity:1;transform:none;transition:opacity .7s ease,transform .7s ease}
.js-reveal .rise{opacity:0;transform:translateY(10px)}
.js-reveal .rise.in{opacity:1;transform:none}

@media(prefers-reduced-motion:reduce){
  .rise,.js-reveal .rise,.js-reveal .rise.in{opacity:1;transform:none;transition:none}
  html{scroll-behavior:auto}
}

/* ============================================================================
   ANIMATED DIAGRAMS — ported from pitches/arx52-deck.html (David, fa10ccf).
   Driven by arx52-anim.js. Values are the deck's verbatim, so the two match.
   A COPY, not a shared file: the deck must stay self-contained because it gets
   emailed. Change one, change the other.
   ========================================================================== */

/* the method lattice */
.schema-wrap{overflow-x:auto}
.schema{width:100%;height:auto;display:block;margin-top:var(--s3);overflow:visible}
.schema .bx{fill:var(--raised);stroke:var(--hair-strong);stroke-width:1}
.schema .bx-a{fill:var(--raised);stroke:var(--brass-rule);stroke-width:1}
.schema .well{fill:var(--well);stroke:var(--hair-strong);stroke-width:1}
.schema .ln{stroke:var(--hair-strong);stroke-width:1;fill:none}
.schema .ln-a{stroke:var(--brass-rule);stroke-width:1;fill:none}
.schema .dsh{stroke-dasharray:3 4}
.schema .nm{font-family:var(--sans-s);font-size:14px;font-weight:700;letter-spacing:.09em;fill:var(--ink)}
.schema .nm-a{fill:var(--brass)}
.schema .sb{font-family:var(--mono);font-size:11.5px;fill:var(--muted)}
.schema .an{font-family:var(--mono);font-size:11px;letter-spacing:.06em;fill:var(--muted)}
.schema .an-a{fill:var(--brass)}
.schema .cell{font-family:var(--mono);font-size:10.5px;fill:var(--muted)}

/* the particles. mpath carries the geometry only and is never painted —
   the JS reads getTotalLength() off it and moves particles along it. */
.schema .mpath{fill:none;stroke:none}
.schema .trk{stroke:var(--brass);fill:none;stroke-linecap:round}
.schema .trk-a{stroke:var(--brass-rule);fill:none;stroke-linecap:round}
.schema .flow{fill:var(--brass);opacity:.9}
.schema .flow-a{fill:var(--brass-rule);opacity:.75}

/* the orrery */
.orrery-wrap{position:relative;width:100%;max-width:34rem;aspect-ratio:1;justify-self:center;margin:0 auto}
.orrery-wrap canvas{display:block;width:100%;height:100%}
.orrery-cap{font-family:var(--mono);font-size:var(--t-micro);letter-spacing:.09em;
  color:var(--muted);text-align:center;margin-top:var(--s2)}
@media(max-width:760px){.orrery-wrap{max-width:20rem}}

/* Nothing in flight when printing — a frozen particle mid-path is a smudge,
   and the diagram has to read as a drawing on paper. */
@media print{
  .schema .flow,.schema .flow-a,.schema .trk,.schema .trk-a{display:none}
  .schema{max-height:96mm}
}

/* ── two-column split (text beside a figure) ───────────────────────────────
   Used by the Orrery section. Exists to solve two problems at once: it halves
   a section that was the tallest on the page, and it occupies the right-hand
   side, which is otherwise bare everywhere because all copy sits in the left
   rail. The deck fills that space with full-bleed photography; the site has no
   imagery, so a live figure is the only furniture available.
   align-items:center so the figure optically centres against the text block. */
.split{
  display:grid;
  grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);
  gap:var(--s5);
  align-items:center;
}
.split-figure{min-width:0}
.points{margin-top:var(--s4);display:grid;gap:var(--s3)}
.points p{color:var(--ink-2);font-size:var(--t-small);line-height:1.6;margin-top:var(--s1)}

/* Stack before the columns get too narrow to read — the orrery needs real
   width to be legible, and a 40ch measure is the floor for the copy. */
@media(max-width:940px){
  .split{grid-template-columns:1fr;gap:var(--s4)}
  .split-figure{order:2}
  .orrery-wrap{max-width:26rem}
}


/* Six items in a screen rather than three. Sam, 2026-07-31: "you need all six so
   make the font of the 6 problems smaller." Dropping the heading a step and
   tightening the gaps is what buys the second row - the alternative was splitting
   the diagnosis across two sections, which would have separated the six symptoms
   from the "these are not six problems" payoff that depends on them. */
.grid-3.six{gap:var(--s3) var(--s4);margin-top:var(--s3)}
.grid-3.six h3{font-size:var(--t-body);line-height:1.3}
.grid-3.six p{font-size:var(--t-small);line-height:1.5;margin-top:6px}
@media(max-width:860px){.grid-3.six{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:620px){.grid-3.six{grid-template-columns:1fr}}

/* ── section rail ───────────────────────────────────────────────────────────
   Ported from the deck. A fixed column of hairline ticks on the right, one per
   section, the active one brass and wider. On the deck it reads as a measured
   scale down the edge of the sheet, which is the same metrology device as the
   tick rule — so it belongs here too.
   Hidden below 900px: it needs the margin, and on a phone the thumb does the
   navigating. Hidden in print, where a fixed nav is meaningless.

   ⚠️ CALLED .section-rail, NOT .rail, and that matters. `.rail` was already the
   site's left-indent utility, applied to every section's copy block. Naming this
   nav `.rail` gave all of those `position:fixed; right:…; top:50%`, which tore
   every section's copy out of flow and stacked it in the middle of the viewport.
   It looked like a rendering glitch and I dismissed it as one, because I checked
   that SECTION positions were still sequential - they were; it was their contents
   that had collapsed. Check the contents, not just the containers. */
.section-rail{
  position:fixed;z-index:60;
  right:clamp(1rem,2.2vw,1.9rem);
  top:50%;transform:translateY(-50%);
  display:flex;flex-direction:column;gap:9px;align-items:flex-end;
}
.section-rail button{
  appearance:none;-webkit-appearance:none;background:none;border:0;padding:0;
  cursor:pointer;width:22px;height:10px;position:relative;
}
.section-rail button::after{
  content:"";position:absolute;right:0;top:4px;height:1px;width:11px;
  background:var(--hair-strong);
  transition:width .32s cubic-bezier(.2,.7,.2,1),background .32s cubic-bezier(.2,.7,.2,1),height .32s cubic-bezier(.2,.7,.2,1);
}
.section-rail button:hover::after{background:var(--brass-rule);width:17px}
.section-rail button[aria-current="true"]::after{background:var(--brass);width:22px;height:2px;top:3px}
.section-rail button:focus-visible{outline:1px solid var(--brass);outline-offset:3px}
@media(max-width:900px){.section-rail{display:none}}
@media print{.section-rail{display:none!important}}
@media(prefers-reduced-motion:reduce){.section-rail button::after{transition:none}}

/* ── cover: copy left, plate right ──────────────────────────────────────────
   Ported from the deck's first slide. A SEPARATE class from .split on purpose -
   .split is the Orrery section's and has different proportions; sharing one class
   would have made a change to either silently move the other. */
.hero-split{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.02fr);
  gap:var(--s4);
  align-items:center;
}
@media(max-width:900px){
  .hero-split{grid-template-columns:1fr;gap:var(--s4)}
  .hero-split .plate{order:2}
}

/* the coordinate block - a measured span between two figures, which is the same
   drawing-sheet idea as the tick rule */
.dim{display:flex;align-items:center;gap:10px;max-width:520px;color:var(--muted)}
.dim i{flex:1;height:1px;background:var(--hair-strong);position:relative;display:block}
.dim i::before,.dim i::after{content:"";position:absolute;top:-4px;width:1px;height:9px;background:var(--hair-strong)}
.dim i::before{left:0}
.dim i::after{right:0}
.dim span{font-family:var(--mono);font-size:var(--t-micro);letter-spacing:.09em;white-space:nowrap}

/* the plate */
.plate{position:relative;background:var(--well);overflow:hidden;aspect-ratio:4/3;margin:0}
.plate img{width:100%;height:100%;object-fit:cover;display:block;filter:contrast(1.07) brightness(1.02)}
.plate figcaption{
  position:absolute;left:0;bottom:0;
  background:rgba(247,245,240,.94);
  border-top:1px solid var(--hair-strong);border-right:1px solid var(--hair-strong);
  font-family:var(--mono);font-size:var(--t-micro);letter-spacing:.09em;
  color:var(--muted);padding:7px 12px;
}

/* ── holder reveal: CSS only, no JS gate ────────────────────────────────────
   The one-page holder animates in with pure CSS. This exists because the JS-gated
   version (.js-reveal, used by full-site.html) shipped a BLANK PAGE: the head
   script that hides .rise elements was present while the body script that reveals
   them had been deleted with the traversal handler.
   An animation cannot strand content the way a two-part hide/reveal can - the base
   state is visible, and `both` leaves it visible when the animation ends. If
   animations are unsupported or the reader prefers reduced motion, the copy is
   simply there with no fade. */
@keyframes arx-rise{
  from{opacity:0;transform:translateY(10px)}
  to{opacity:1;transform:none}
}
@media(prefers-reduced-motion:no-preference){
  .holder .rise{animation:arx-rise .72s cubic-bezier(.2,.7,.2,1) both}
  .holder .label.rise{animation-delay:.02s}
  .holder .wordmark.rise{animation-delay:.10s}
  .holder .tagline.rise{animation-delay:.20s}
  .holder .ground.rise{animation-delay:.30s}
  .holder .scale.rise{animation-delay:.40s}
  .holder .dim.rise{animation-delay:.46s}
  .holder .plate.rise{animation-delay:.16s}
}
