/* ============================================================================
   BASE — tokens, reset, typography, layout primitives.
   Loaded on every page. The world itself lives in seal.css; the manuscript
   typesetting lives in paper.css and loads only on the two paper pages.
   ========================================================================= */

:root {
  /* --- The field ------------------------------------------------------
     Cinnabar is 印泥, seal paste. It is a ground, not an accent: it owns
     whole regions. Every value below was chosen by computing the relative
     luminance ratio, not by eye — the three that look right (a lighter red,
     a 0.86 bone, a darker red rule) measure 4.90, 4.47 and 1.19 and fail. */
  --cinnabar: #B22D22;        /* the field.            bone on it = 5.55:1 */
  --cinnabar-lift: #C9412F;   /* hover / raised */
  --cinnabar-press: #8E2118;  /* pressed */
  --indigo: #1B3B5A;          /* second register — the economics half */
  --indigo-press: #12293F;
  --ink: #14151A;             /* type on bone.         on bone   = 15.84:1 */
  --bone: #F1EFE8;            /* the document panel.   on cinnabar = 5.55:1 */
  --bone-dim: #E5E1D7;        /* secondary panel, table stripe */
  --bone-lift: #FFFDF7;       /* hover only. Pure white is a fourth neutral
                                 nobody chose, and reads cold against cinnabar. */

  /* Quiet tones. 0.66 over bone measures 5.6:1; the obvious 0.55 measures
     3.9 and is unreadable for anyone who needs the contrast. On cinnabar
     nothing below 0.90 clears 4.5, so body-size text there is never tinted. */
  --ink-quiet: rgba(20, 21, 26, 0.66);
  --ink-faint: rgba(20, 21, 26, 0.42);   /* rules only, never text */
  --bone-quiet: rgba(241, 239, 232, 0.90);
  --bone-faint: rgba(241, 239, 232, 0.28); /* hairlines on cinnabar */
  --ink-hair: rgba(241, 239, 232, 0.18);    /* hairlines on ink, the close */

  /* --- The other three pastes ----------------------------------------
     Chinese painting and seal paste use a small set of mineral pigments;
     four of them are legible grounds for bone type. Each leaf of the
     register is stamped in one: 朱砂 cinnabar for the cover and the language
     paper, 靛青 indigo for the economics paper, 石绿 malachite for what was
     built, 赭石 ochre for the person. Every number is bone on the tone. */
  --indigo-lift: #26507A;      /* 7.27 */
  --indigo-seal: #0E2033;      /* 14.34 */
  --malachite: #2A5340;        /* 7.57 */
  --malachite-lift: #34614B;   /* 6.17 */
  --malachite-press: #1F4230;  /* 9.71 */
  --malachite-seal: #1B3A2B;   /* 10.82 */
  --ochre: #7A4820;            /* 6.58 */
  --ochre-lift: #8A5220;       /* 5.52 */
  --ochre-press: #5E3715;      /* 8.98 */
  --ochre-seal: #553014;       /* 10.03 */

  /* --- Roles — what a component actually reads ------------------------
     Nothing below the palette references a pigment by name. A leaf switches
     its paste by re-pointing these roles on <body data-theme>, so a new page
     changes nothing until it opts in. Ratios: bone on the tone, or the tone
     on bone, computed. */
  --ground: var(--cinnabar);              /* 5.55 */
  --ground-lift: var(--cinnabar-lift);    /* hover only; 4.26, never rests under text */
  --ground-press: var(--cinnabar-press);  /* pressed, current tab; 7.67 */
  --seal-ink: #7C1A11;                    /* the intaglio fill; 9.07 */
  --seal-frame: rgba(241, 239, 232, 0.72);
  --on-ground: var(--bone);
  --on-ground-quiet: var(--bone-quiet);   /* label-scale mono only; 4.78 over cinnabar */
  --on-ground-faint: var(--bone-faint);   /* hairlines */
  --second: var(--indigo);                /* the band that is not the ground; 10.03 */
  --second-press: var(--indigo-press);
  --accent: var(--cinnabar);              /* on bone: links, marks, section numbers; 5.55 */
  --accent-rule: rgba(178, 45, 34, 0.40);
  --accent-tint: rgba(178, 45, 34, 0.09);

  /* --- Type -----------------------------------------------------------
     Archivo carries a width axis, so display type is cut wide to fill its
     square the way a seal character fills its field. Spectral reads two
     full manuscripts. Martian Mono is measurement, never costume. */
  --cjk: "Songti SC", "Noto Serif SC", "Source Han Serif SC", serif;
  --display: "Archivo", "Helvetica Neue", Arial, "Songti SC", sans-serif;
  --text: "Spectral", Georgia, "Songti SC", serif;
  --mono: "Martian Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* The Ideographic Description Characters (U+2FF0-U+2FFB) are drawn as
     inline SVG by tools/build_pages.py rather than set as type. No font in
     any plausible fallback stack carries them — Apple Symbols and Songti were
     both checked against their cmap tables, and the Noto Sans Symbols 2 build
     in google/fonts turns out to lack the block too — so the previous build
     rendered the notation this site is about as tofu boxes. */

  --t-display: clamp(42px, 7.6vw, 96px);
  --t-h1: clamp(32px, 5vw, 56px);
  --t-h2: clamp(25px, 3.2vw, 38px);
  --t-h3: clamp(19px, 2vw, 25px);
  /* One step between h2 and h3, for the objects that rank below a section
     heading but above a run-in: arm titles, entry titles, the primary contact
     value, a manuscript section head. Five slightly different bespoke clamps
     were doing this job, which flattened the hero — the thesis and the arm
     titles ended up 33px against 30px, when the bridge is supposed to
     outrank both. */
  --t-lead: clamp(21px, 2.4vw, 30px);
  /* The thesis when it sits beside the seal tray: one step under --t-h1,
     because at 56px in a 476px column it ran to eight lines. */
  --t-thesis: clamp(32px, 3.1vw, 44px);
  --t-body: clamp(17px, 0.4vw + 16px, 19px);
  --t-small: 15.5px;
  /* 11px is the floor for anything a visitor has to read, including the
     provenance line under a claim; the earlier 9.5px ramp measured fine on
     paper and failed on every phone the overlay was run on. */
  --t-label: 11px;
  --t-meta: 11px;      /* provenance, dates, sources, keys */

  /* Manuscript ramp. A Read surface carries its own smaller scale: a
     twenty-thousand-word paper with tables of numbers cannot be set on the
     display ramp without the tables outgrowing the column. */
  --t-ms: 13.5px;      /* manuscript secondary: contents, tables, references */
  --t-ms-sm: 13px;     /* equations, figure captions, footnotes */
  --t-data: 12px;      /* tabular figures */

  /* --- Space — one rhythm, used everywhere -------------------------- */
  --s1: 6px;  --s2: 12px; --s3: 20px; --s4: 32px;
  --s5: 52px; --s6: 84px; --s7: 128px;

  --shell: 1180px;
  --measure: 68ch;
  --gutter: clamp(20px, 5vw, 56px);

  /* Stamps land: fast down, no bounce. */
  --press: cubic-bezier(0.18, 0.9, 0.28, 1);

  /* A ring that survives both grounds: the bone stop reads on cinnabar,
     the ink stop reads on bone, so one declaration works everywhere. */
  --ring: 0 0 0 2px var(--bone), 0 0 0 5px var(--ink);
}

/* --- Reset ------------------------------------------------------------ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Every in-page target clears the stamped header band. */
  scroll-padding-top: 96px;
}
@media (max-width: 860px) {
  /* The sticky top row plus the folded contents bar. */
  html { scroll-padding-top: 124px; }
}

/* 靛青 — the economics paper. The second register is cinnabar here, so the
   two papers read as each other's counterpart. */
[data-theme="indigo"] {
  --ground: var(--indigo);                /* 10.03 */
  --ground-lift: var(--indigo-lift);      /* 7.27 */
  --ground-press: var(--indigo-press);    /* 12.90 */
  --seal-ink: var(--indigo-seal);         /* 14.34 */
  --on-ground-quiet: rgba(241, 239, 232, 0.90);   /* 8.46 over indigo */
  --second: var(--cinnabar);
  --second-press: var(--cinnabar-press);
  --accent: var(--indigo);                /* 10.03 on bone, 8.84 on bone-dim */
  --accent-rule: rgba(27, 59, 90, 0.40);
  --accent-tint: rgba(27, 59, 90, 0.09);
}

/* 石绿 — what was built. */
[data-theme="malachite"] {
  --ground: var(--malachite);             /* 7.57 */
  --ground-lift: var(--malachite-lift);   /* 6.17 */
  --ground-press: var(--malachite-press); /* 9.71 */
  --seal-ink: var(--malachite-seal);      /* 10.82 */
  --on-ground-quiet: rgba(241, 239, 232, 0.90);   /* 6.48 over malachite */
  --second: var(--ink);
  --second-press: #000000;
  --accent: var(--malachite);             /* 7.57 on bone, 6.67 on bone-dim */
  --accent-rule: rgba(42, 83, 64, 0.40);
  --accent-tint: rgba(42, 83, 64, 0.09);
}

/* 赭石 — the person. */
[data-theme="ochre"] {
  --ground: var(--ochre);                 /* 6.58 */
  --ground-lift: var(--ochre-lift);       /* 5.52 */
  --ground-press: var(--ochre-press);     /* 8.98 */
  --seal-ink: var(--ochre-seal);          /* 10.03 */
  --on-ground-quiet: rgba(241, 239, 232, 0.90);   /* 5.67 over ochre */
  --second: var(--ink);
  --second-press: #000000;
  --accent: var(--ochre);                 /* 6.58 on bone, 5.79 on bone-dim */
  --accent-rule: rgba(122, 72, 32, 0.40);
  --accent-tint: rgba(122, 72, 32, 0.09);
}

/* 墨 — the door. The ink the close is set in on every page, opened into a
   whole leaf for /write/. The seal is pressed in cinnabar on it, the way a
   stamp sits on black paper, and the hairlines are the close's own. */
[data-theme="ink"] {
  --ground: var(--ink);                   /* 15.84 */
  --ground-lift: #24262E;                 /* hover only; 12.9 */
  --ground-press: #000000;                /* 18.6 */
  --seal-ink: var(--cinnabar);            /* bone on it 5.55 */
  --on-ground-quiet: var(--bone-quiet);   /* 13.9 over ink */
  --on-ground-faint: var(--ink-hair);
  --second: var(--cinnabar);
  --second-press: var(--cinnabar-press);
  --accent: var(--cinnabar);              /* 5.55 on bone */
  --accent-rule: rgba(178, 45, 34, 0.40);
  --accent-tint: rgba(178, 45, 34, 0.09);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--on-ground);
  font-family: var(--text);
  font-size: var(--t-body);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; }

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

/* The hidden attribute wins over any display a component sets. Without this
   a control emitted hidden for scripts-off visitors, then given display:flex
   by its own class, paints as a dead button; the door's copy control did. */
[hidden] { display: none !important; }

/* --- Focus ------------------------------------------------------------ */

:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: 1px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: var(--bone);
  padding: 14px 22px;
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
}
.skip:focus { left: var(--gutter); top: 10px; }

/* --- Type primitives -------------------------------------------------- */

.display,
.h1, .h2, .h3 {
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 112;
  line-height: 0.98;
  letter-spacing: -0.022em;
  text-wrap: balance;
}

.display { font-size: var(--t-display); font-weight: 900; font-variation-settings: "wdth" 118; }
.h1 { font-size: var(--t-h1); }
.h2 { font-size: var(--t-h2); line-height: 1.04; }
.h3 {
  font-size: var(--t-h3);
  font-weight: 700;
  font-variation-settings: "wdth" 100;
  line-height: 1.16;
  letter-spacing: -0.01em;
}

/* The label is measurement: a figure number, a status, a date, a source.
   It is not applied to every section as decoration. */
.label {
  font-family: var(--mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 1.4;
  font-variation-settings: "wdth" 87;
}

.lede {
  font-size: clamp(19px, 1.5vw + 14px, 25px);
  line-height: 1.46;
  max-width: 34ch;
  text-wrap: pretty;
}

.prose { max-width: var(--measure); text-wrap: pretty; }
.prose + .prose { margin-top: var(--s3); }

.note {
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--ink-quiet);
  max-width: 62ch;
}

em { font-style: italic; }
b, strong { font-weight: 700; }

/* An Ideographic Description Character, drawn rather than set. Sized in em so
   it tracks whatever it sits in, from 10px table cells to 30px prose. */
.idc {
  display: inline-block;
  width: 0.8em;
  height: 0.8em;
  vertical-align: -0.055em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.15;
  stroke-linecap: square;
}

/* --- Links ------------------------------------------------------------ */

a { color: inherit; }

/* The paste is a ground, not an accent. Whole components that happen to be
   links — the entry cards, the 404 register rows — must not inherit link
   colour for their body copy; only genuine inline links do. A link marked
   data-plain sets its own type and carries no underline at rest: the address
   on the door, and the prepared doors under each kind of correspondent. */
.doc a:not(.btn):not(.entry):not(.register-row):not([data-plain]) {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: var(--accent-rule);
}
.doc a:not(.btn):not(.entry):not(.register-row):not([data-plain]):hover { text-decoration-color: currentColor; }

/* --- Layout ----------------------------------------------------------- */

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

.band { padding-block: var(--s6); }
.band--tight { padding-block: var(--s5); }
/* The second register: whichever paste is not the page's own. */
.band--second, .band--indigo { background: var(--second); }
.band--ink { background: var(--ink); }

/* More space above a heading than below it, everywhere. */
.band > .wrap > * + * { margin-top: var(--s3); }
.band h2 + *, .band h3 + * { margin-top: var(--s3); }
.band > .wrap > .h2 { margin-top: var(--s5); }
.band > .wrap > :first-child { margin-top: 0; }

/* --- Motion ----------------------------------------------------------- */

/* Content is visible by default and JS removes nothing; the stamp entrance
   is added only when motion is allowed, so no-JS and reduced-motion both
   get the finished composition rather than a blank page. */
@media (prefers-reduced-motion: no-preference) {
  .js [data-press] {
    opacity: 0;
    transform: scale(1.055) rotate(var(--press-tilt, -1.1deg));
  }
  .js [data-press].is-pressed {
    opacity: 1;
    transform: none;
    transition:
      opacity 420ms var(--press),
      transform 520ms var(--press);
    transition-delay: var(--press-delay, 0ms);
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* Scroll-driven rules are state, not motion: the reading-progress bar and
     the table unfade track the scroll position and must keep doing so. */
  .progress, .table-scroll, .rail { animation-duration: auto !important; }
}
