/* Margin site base: the one shared copy of the brand system (S3, 2026-11-12).
 *
 * Tokens, reset, the red margin line, skip-link, selection, focus,
 * text-wrap, the reveal and ink entrance animations, print, the page
 * leaf, hero, eyebrow, lede, section divider and policy-section prose.
 * Every rule here was moved byte-for-byte out of the pages' inline
 * <style> blocks, where it was repeated on 12 to 19 root pages.
 *
 * Link it before the page's own <style> (and before components.css):
 *   <link rel="stylesheet" href="/assets/fonts.css">
 *   <link rel="stylesheet" href="/assets/base.css">
 *   <link rel="stylesheet" href="/assets/components.css">
 *   <style> ...page-only layout... </style>
 * A page must not redeclare :root; it overrides a component in its own
 * <style>, which comes later in the cascade.
 *
 * Brand law (README "Editing rules"): cream background, light only, the
 * red margin line at 88px desktop / 28px mobile, Crimson Pro for text and
 * JetBrains Mono for accents only, red only for the line, accent words,
 * link underlines and the mark. */

/* Tokens — identical to other pages */
:root {
  --cream:    #F5F1E8;
  --cream-2:  #EBE5D6;
  --paper:    #FBF7EC;
  --ink:      #1A1814;
  --ink-soft: #2C2820;
  --red:      #B83A2E;
  --pencil:   #6B6358;
  --highlight:#E8D88A;
  --rule:     rgba(107,99,88,0.18);
  --focus:    var(--ink);   /* focus rings are ink, never red (S3-19; CMDP §IV) */

  /* ---- Rules, graded ----
     One rule weight made structure and decoration read identically. */
  --hair:     rgba(107,99,88,0.10);
  --spine:    rgba(107,99,88,0.28);
  --ink-note: #5C554A;   /* margin-note ink */

  /* ---- Measure scale ---- named by job, not re-guessed per section. */
  --measure:       58ch;   /* the reading column */
  --measure-tight: 46ch;   /* asides, captions, secondary prose */
  --measure-note: 232px;   /* the margin rail */
  --measure-wide: 1040px;
  --gutter:       56px;

  /* ---- Type scale ---- six steps; every size resolves to one.
     --t-display is the 72px the H1 always shipped at (V-07); --t-head is
     every section H2; nothing is set below --t-label (12px). */
  --t-display: clamp(38px, 6.2vw, 72px);
  --t-head:    clamp(29px, 3.4vw, 42px);
  --t-lede:    21px;
  --t-body:    18px;
  --t-note:    15px;
  --t-label:   12px;

  /* ---- Space scale ---- vertical rhythm inherited, not re-guessed. */
  --s1: 8px;  --s2: 14px; --s3: 22px; --s4: 34px;
  --s5: 52px; --s6: 80px; --s7: 116px;

}

/* ================ THE LEAF ================
   A page leaf: one reading column hung off the red rule, with a true
   outer margin beside it carrying the annotations. Small print and
   asides live in the margin — where a reader looks for them —
   instead of padding out the reading column. Below 1080px the margin
   has nowhere to go, so notes fold under the passage they annotate
   and keep their marginal styling. Shared with index.html. */
.leaf {
  display: grid;
  grid-template-columns: minmax(0, var(--measure));
  gap: var(--s4);
  align-items: start;
}
@media (min-width: 1080px) {
  .leaf {
    grid-template-columns: minmax(0, var(--measure)) var(--measure-note);
    gap: var(--gutter);
  }
}
.leaf-note {
  font-family: 'Crimson Pro', serif;
  font-size: var(--t-note);
  line-height: 1.5;
  color: var(--ink-note);
}
.leaf-note::before {
  content: "";
  display: block;
  width: 28px; height: 1px;
  background: var(--red);
  opacity: 0.55;
  margin: 9px 0 12px;
}
.leaf-note p { margin: 0 0 var(--s2); }
.leaf-note p:last-child { margin-bottom: 0; }
.leaf-note strong { font-weight: 600; color: var(--ink); }
.leaf-note .datum {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--t-label);
  letter-spacing: 0.04em;
  color: var(--ink);
}
@media (max-width: 1079px) {
  .leaf-note {
    padding-left: var(--s3);
    border-left: 1px solid var(--hair);
  }
  .leaf-note::before { display: none; }
}


* { box-sizing: border-box; margin: 0; padding: 0; }

/* `clip` (rather than `hidden`) prevents horizontal overflow without
   making the element a scroll container, which would break sticky
   positioning. Mirrors the homepage. */
html, body {
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
}
@supports not (overflow: clip) {
  html, body { overflow-x: hidden; }
}
/* Smooth scrolling only once the reader is on the page (S3-23). A cold
   arrival at /page#section jumps straight there: a smooth scroll across
   tens of thousands of pixels ends at a stale spot if the web fonts swap
   in mid-flight. Clicking an in-page link focuses it, so :focus-within
   turns the smooth scroll on for every jump after the first. */
html { scroll-behavior: auto; }
@media (prefers-reduced-motion: no-preference) {
  html:focus-within { scroll-behavior: smooth; }
}

body {
  background: var(--cream);
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='160'%3E%3Cfilter%20id='p'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.9'%20numOctaves='2'%20stitchTiles='stitch'/%3E%3CfeColorMatrix%20type='matrix'%20values='0%200%200%200%200.1%200%200%200%200%200.09%200%200%200%200%200.07%200%200%200%200.045%200'/%3E%3C/filter%3E%3Crect%20width='160'%20height='160'%20filter='url%28%23p%29'/%3E%3C/svg%3E");
  color: var(--ink);
  font-family: 'Crimson Pro', Georgia, serif;
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
}

/* The persistent margin line */
body::before {
  content: "";
  position: fixed;
  top: 0; bottom: 0;
  left: 88px;
  width: 1.5px;
  background: var(--red);
  opacity: 0.85;
  z-index: 1;
  pointer-events: none;
  animation: draw-margin-line 0.8s cubic-bezier(.2,.7,.2,1) both;
}
@media (max-width: 720px) { body::before { left: 28px; } }
@keyframes draw-margin-line {
  from { -webkit-clip-path: inset(0 0 100% 0); clip-path: inset(0 0 100% 0); }
  to   { -webkit-clip-path: inset(0 0 0 0);    clip-path: inset(0 0 0 0); }
}

/* ================ Selection + focus polish ================ */
::selection {
  background: var(--highlight);
  color: var(--ink);
}
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}

/* The hidden attribute always hides, even on an element a component styles
   with its own display (a .cta-secondary button kept hidden until its script
   runs, say). */
[hidden] { display: none !important; }

/* A11Y utilities */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: absolute;
  top: -100px; left: 16px;
  z-index: 100;
  background: var(--ink);
  color: var(--cream);
  padding: 12px 18px;
  text-decoration: none;
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  transition: top .2s ease;
}
.skip-link:focus {
  top: 16px;
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* ================ PAGE HERO ================ */
.page-hero {
  position: relative;
  z-index: 2;
  padding: 60px 56px 36px 120px;
  max-width: 1320px;
  margin: 0 auto;
}
@media (max-width: 720px) {
  .page-hero { padding: 44px 24px 28px 56px; }
}

.eyebrow {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--pencil);
  margin: 0 0 28px -32px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  max-width: calc(100% + 32px);
  animation: rise 1.1s cubic-bezier(.2,.7,.2,1) .15s both;
}
.eyebrow::before {
  content: "";
  width: 20px;
  height: 1px;
  background: var(--red);
  flex-shrink: 0;
}
/* The dash is a tick off the margin line: wherever the page is narrower than
   its 1320px frame it starts on the line (88px, or 28px on phones) and stops
   12px short of the text, which sets on the H1's left edge. */
@media (max-width: 720px) {
  .eyebrow { margin-left: -28px; max-width: calc(100% + 28px); flex-wrap: nowrap; }
  .eyebrow::before { width: 16px; }
}

h1 {
  font-family: 'Crimson Pro', serif;
  font-weight: 600;
  font-size: var(--t-display);
  line-height: 1.04;
  letter-spacing: -0.025em;
  color: var(--ink);
  margin-bottom: 32px;
}
h1 .accent { color: var(--red); }

.lede {
  font-family: 'Crimson Pro', serif;
  font-size: var(--t-lede);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: var(--measure);
  margin: 0 0 24px 0;
}
.lede em { font-style: italic; color: var(--ink); }

.last-updated {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--pencil);
  margin-top: 24px;
}
/* From 1080px the hero's dated line moves out of the reading column into
   the margin rail (the .leaf-note idiom: a short red rule, then the note),
   level with the lede's first line. The words are the same; below 1080px
   it stays under the lede. Only heroes that carry the line are a grid. */
@media (min-width: 1080px) {
  .page-hero:has(> .last-updated) {
    display: grid;
    grid-template-columns: minmax(0, var(--measure)) minmax(0, 1fr);
    column-gap: var(--gutter);
    align-items: start;
  }
  .page-hero:has(> .last-updated) > * { grid-column: 1; }
  .page-hero:has(> .last-updated) > .eyebrow,
  .page-hero:has(> .last-updated) > h1 { grid-column: 1 / -1; }
  .page-hero:has(> .last-updated) > .last-updated {
    grid-column: 2;
    margin-top: 0;
    line-height: 1.6;
  }
  .page-hero:has(> .last-updated) > .last-updated::before {
    content: "";
    display: block;
    width: 28px;
    height: 1px;
    background: var(--red);
    opacity: 0.55;
    margin: 16px 0 12px;
  }
}

/* ================ POLICY SECTIONS ================ */
.policy-section {
  position: relative;
  z-index: 2;
  padding: var(--s3) 56px var(--s3) 120px;
  max-width: 1320px;
  margin: 0 auto;
}
@media (max-width: 720px) {
  .policy-section { padding: var(--s3) 24px var(--s2) 56px; }
}

/* Section divider — same rhythm as index.html / faq.html / specs.html.
   Replaces the previous .section-label pattern which had the rule
   anchored inside each section. Lifting it out establishes consistent
   between-section breaks across the site. */
.divider {
  position: relative;
  z-index: 2;
  max-width: 1320px;
  /* Section rhythm (V-08): the previous section's bottom padding plus this
     margin is one --s6 (80px) on desktop and one --s5 (52px) on phones,
     and the label sits one --s3 above the section's first line. */
  margin: calc(var(--s6) - var(--s3)) auto 0;
  padding: 0 56px 0 120px;
}
@media (max-width: 720px) {
  .divider { margin-top: calc(var(--s5) - var(--s2)); padding: 0 24px 0 56px; }
}
.divider .rule {
  height: 1px;
  background: var(--rule);
  width: 100%;
  margin-bottom: 16px;
}
.divider .label {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--pencil);
}
.divider .label .num {
  color: var(--red);
  margin-right: 12px;
}
/* The label is a real <h2> for the document outline (screen-reader
   section navigation). This scoped reset neutralises the UA heading
   defaults (margin, bold) so it renders exactly as the span it
   replaced; everything else comes from .divider .label above. */
.divider h2.label {
  margin: 0;
  font-weight: 400;
}
/* Hairline rule draw-in animation. Mirrors index.html. */
.js .draw-rule {
  transform-origin: left center;
  transform: scaleX(0);
}
.js .reveal.visible .draw-rule {
  animation: draw-line 0.8s cubic-bezier(.2,.7,.2,1) .15s both;
}

.policy-section h2 {
  font-family: 'Crimson Pro', serif;
  font-weight: 600;
  font-size: var(--t-head);
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin-bottom: 20px;
  max-width: 32ch;
}

.policy-section p {
  font-family: 'Crimson Pro', serif;
  font-size: var(--t-body);
  line-height: 1.7;
  color: var(--ink-soft);
  max-width: var(--measure);
  margin-bottom: 16px;
}
.policy-section p:last-child { margin-bottom: 0; }
.policy-section em { font-style: italic; color: var(--ink); font-weight: 500; }
.policy-section strong { font-weight: 600; color: var(--ink); }
.policy-section a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--red);
  padding-bottom: 1px;
  transition: background .2s ease;
}
.policy-section a:hover {
  background: rgba(184, 58, 46, 0.08);
}
.policy-section code {
  font-family: 'JetBrains Mono', monospace;
  font-size: 14px;
  background: var(--cream-2);
  padding: 1px 6px;
}

/* Lists in policy — same red-dash marker as FAQ for brand consistency */
.policy-section ul {
  list-style: none;
  padding: 0;
  margin: 8px 0 20px 0;
  max-width: var(--measure);
}
.policy-section ul li {
  position: relative;
  padding-left: 22px;
  margin-bottom: 10px;
  line-height: 1.6;
  font-family: 'Crimson Pro', serif;
  font-size: var(--t-body);
  color: var(--ink-soft);
}
.policy-section ul li:last-child { margin-bottom: 0; }
.policy-section ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 12px;
  height: 2px;   /* a whole pixel count, so every dash rasterizes alike (S3-121) */
  background: var(--red);
}

/* Reveal animation, scoped to .js so JS-off shows everything.
   Timing matches the rest of the site (1s, brand cubic-bezier). */
.js .reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 1s cubic-bezier(.2,.7,.2,1),
              transform 1s cubic-bezier(.2,.7,.2,1);
}
.js .reveal.visible { opacity: 1; transform: translateY(0); }
/* The hero, a call to action and legal text never wait for the observer:
   they slide in but start fully opaque (P3-A11Y-02). Mark legal pages
   with class="legal" on <main> or on the section. */
.js .page-hero .reveal,
.js .page-hero.reveal,
.js .legal .reveal,
.js .reveal.legal { opacity: 1; }
/* Its own rule: a browser without :has() drops only this selector list,
   not the hero and legal ones above. */
.js .reveal:has(.cta, .cta-primary, .cta-small, .legal-note) { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; transition: none; }
}

/* ---------- entrance animations (shared vocabulary) ---------- */
@keyframes fade-up {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* rise: the entrance for anything that must be readable from the first
   frame (the hero, calls to action, legal text). It moves, it never
   starts transparent (P3-A11Y-02, S3-120). */
@keyframes rise {
  from { transform: translateY(14px); }
  to   { transform: translateY(0); }
}
@keyframes fade-down {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Handwritten ink animations — same set as other pages. The hero's
   eyebrow underline draws in and its headline rises; section headlines
   ink-bleed left to right as they scroll in, and their red accent word
   returns slightly late, like someone went back to underline it. */
@keyframes ink-bleed {
  from { clip-path: inset(0 100% 0 0); opacity: 0.5; }
  to   { clip-path: inset(0 0% 0 0); opacity: 1; }
}
@keyframes draw-line {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes accent-return {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* The hero H1 and its red accent are readable from the first frame: the
   headline rises (transform only), never clipped or transparent, and the
   accent moves with it (P3-A11Y-02; was ink-bleed plus a late
   accent-return, which hid the hero text for about a second). The accent
   stays inline-block so its line breaks are unchanged. */
.js .page-hero h1.ink-hero,
.js h1.ink-hero {
  animation: rise 1.1s cubic-bezier(.2,.7,.2,1) .25s both;
}
.js .ink-hero .accent { display: inline-block; }
/* Section headings ink in when their .reveal parent becomes visible —
   same treatment as the homepage and specs section heads. */
.js .ink-headline { clip-path: inset(0 100% 0 0); opacity: 0.5; }
.js .reveal.visible .ink-headline {
  animation: ink-bleed 0.85s cubic-bezier(.2,.7,.2,1) both;
}
.ink-headline .accent { color: var(--red); }
.js .reveal.visible .ink-headline .accent {
  display: inline-block;
  animation: accent-return 0.5s cubic-bezier(.2,.7,.2,1) .55s both;
}
/* Eyebrow's red dash gets drawn in from the left — same effect as
   other pages. The ::before pseudo is already defined as part of
   the base .eyebrow rule; here we just animate it. */
.js .ink-eyebrow::before {
  transform-origin: left center;
  transform: scaleX(0);
  animation: draw-line 0.7s cubic-bezier(.2,.7,.2,1) .35s both;
}
/* Reduced motion: nothing draws, bleeds or rises. The hero selector is
   repeated at its own specificity so the animated rule above cannot win. */
@media (prefers-reduced-motion: reduce) {
  .js .page-hero h1.ink-hero,
  .eyebrow,
  body::before,
  .js .draw-rule,
  .js .reveal.visible .draw-rule,
  .js h1.ink-hero,
  .js .ink-hero .accent,
  .js .ink-headline,
  .js .reveal.visible .ink-headline,
  .js .reveal.visible .ink-headline .accent,
  .js .ink-eyebrow::before {
    animation: none;
    clip-path: none;
    opacity: 1;
    transform: none;
  }
}

/* ================ PRINT STYLES ================
   The privacy policy is the page most likely to be printed —
   by lawyers reviewing it, students filing it, and people who
   want a paper record. Strip decorative chrome, force black ink
   on white paper, and expand link URLs so print readers can see
   where references go. */
@media print {
  /* Drop the persistent red margin line (saves ink, looks weird in print) */
  body::before { display: none; }

  /* Force readable contrast on paper */
  body {
    background: #fff !important;
    color: #000 !important;
    font-size: 11pt;
    line-height: 1.5;
  }

  /* Hide nav, footer, and any reveal-animation residue */
  .skip-link { display: none !important; }

  /* Strip width constraints — let the page width drive */
  main, .policy-section {
    max-width: none !important;
    padding: 0 !important;
    margin: 0 0 1em 0 !important;
  }

  /* Force all reveal-animation elements to their final state */
  .reveal {
    opacity: 1 !important;
    transform: none !important;
  }

  /* Headings stay with their content — no orphaned headers at page bottom */
  h1, h2, h3 {
    page-break-after: avoid;
    break-after: avoid;
    color: #000 !important;
  }

  /* No mid-paragraph page breaks */
  p, li {
    orphans: 3;
    widows: 3;
  }

  /* Show URLs after links so a printed page is readable without browser */
  a[href^="http"]::after,
  a[href^="mailto:"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    color: #555;
  }

  /* Internal anchors don't need URL expansion */
  a[href^="#"]::after,
  a[href^="/"]::after,
  a[href^="."]::after { content: ""; }

  /* Strong text stays bold, but no decorative red */
  strong { color: #000 !important; }

  /* Italic emphasis stays italic but in black */
  em { color: #000 !important; }

  /* Section labels (numbered headers) print as plain text */
  .section-label {
    color: #000 !important;
    font-weight: bold;
    margin-bottom: 0.25em;
  }

  /* Lists keep their structure but no decorative bullets */
  ul li::before {
    background: #000 !important;
  }
}

/* ================ Editorial typographic polish ================
   Modern line-breaking + a deep-link affordance. Pure progressive
   enhancement — unsupported browsers ignore it and fall back to
   today's exact rendering (no layout shift, no regression).
     • balance — evens multi-line headings so the last line is never
       a lone orphan word.
     • pretty  — improves long-form rag, avoids single-word widows.
     • :target — gives a deep-linked block headroom + a brief
       highlighter wash; reduced-motion users get spacing only. */
h1, h2, h3, h4,
.eyebrow,
.divider .label { text-wrap: balance; }

p, li, .lede, blockquote, figcaption, dd { text-wrap: pretty; }

/* Quoted source shows the characters in the file: JetBrains Mono's
   ligatures would draw => as an arrow and >= as a sign (S3-80). */
code, pre, kbd, samp { font-variant-ligatures: none; font-feature-settings: "calt" 0, "liga" 0; }

[id] { scroll-margin-top: 100px; }
@media (prefers-reduced-motion: no-preference) {
  h2:target, h3:target, .qa:target {
    animation: target-glow 2.2s ease-out both;
  }
  @keyframes target-glow {
    0%, 22% { background-color: var(--highlight); }
    100%    { background-color: transparent; }
  }
}

/* ================ Advanced opt-ins (added for review) ================
   Pure progressive enhancement — unsupported browsers ignore both.
   1. Cross-document view transitions: crossfade page-to-page nav
      instead of hard-cutting. Guard below neutralises it to an instant
      cut under reduced-motion (the universal * rule can't reach the
      ::view-transition pseudo-tree). Review the hand-off to each page's
      load-entrance animations in a real browser.
   2. hanging-punctuation: opening quotes/brackets hang into the margin,
      book-style (Safari today; ignored elsewhere). */
@view-transition { navigation: auto; }

body { hanging-punctuation: first; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ================ PAGE ADDITIONS (about, contact, accessibility) ================ */
.policy-section h3 {
  font-family: 'Crimson Pro', serif;
  font-weight: 600;
  font-size: 22px;
  line-height: 1.25;
  color: var(--ink);
  margin: 28px 0 10px;
  max-width: var(--measure);
}
.policy-section h3:first-child { margin-top: 0; }
