/* archetypes.css — the composition vocabulary (B-071).
 *
 * WHY
 * Reverse-engineering a real 39-page IR deck found that ~38% of it is one shape
 * (a two-zone split at four different ratios) and that eleven shapes cover
 * everything the deck does with type and geometry. The shared layer had exactly
 * ONE composition — `.slide-content` is a single centred 1200px column
 * (base.css:214-236) — so every multi-zone slide was improvised freehand, which is
 * why decks came out inconsistent even with good tokens.
 *
 * These are art-directed grids, not a layout engine. There is no manifest and no
 * compiler: an author copies a fragment from `_templates/archetypes/` and fills it
 * in. Composition becomes a menu choice instead of a per-slide invention, and
 * `index.html` stays hand-owned (a compiler would make it a build output and
 * re-create the regeneration bug B-055 just fixed one level down).
 *
 * OPT-IN BY CONSTRUCTION
 * Every rule is class-scoped (`.arch-*`), so a deck that does not use an archetype
 * is untouched — no body attribute needed. The `arch-` prefix keeps the vocabulary
 * clear of diagrams.css's component classes (.cards2, .cov-grid, .crux, …).
 *
 * NO px, NO hex, NO font-family
 * Every value below is a token or a ratio. That is also what the fragments are
 * gated on (ci-validate-archetypes.mjs), because a literal in a fragment is how a
 * deck silently escapes the theme system.
 */

:root {
  /* Zone gap and rhythm. Ratios of the 1920-wide stage the reference deck is
     authored against, expressed in vw so they hold at any viewport. */
  --arch-gap: 48px;
  --arch-gap-tight: 24px;
  /* Row height floor for pinned-arity grids: keeps a 3-up and a 5-up row from
     collapsing to different heights when one cell has more text. */
  --arch-row-min: 129.6px;
}

/* ── split — the workhorse (≈38% of the reference deck) ────────────────────
   Two zones at an author-chosen ratio. The ratio is a data attribute rather than
   four class names because it is a continuous design decision, not four separate
   components: p11 is 45/55, p13 40/60, p14 50/50, p21 38/62. */
.arch-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--arch-gap);
  align-items: start;
  width: 100%;
}

.arch-split[data-ratio="45/55"] { grid-template-columns: 45fr 55fr; }
.arch-split[data-ratio="40/60"] { grid-template-columns: 40fr 60fr; }
.arch-split[data-ratio="38/62"] { grid-template-columns: 38fr 62fr; }
.arch-split[data-ratio="60/40"] { grid-template-columns: 60fr 40fr; }
.arch-split[data-ratio="62/38"] { grid-template-columns: 62fr 38fr; }

/* Vertically centre a zone whose partner is much taller (a chart beside three
   bullets reads better centred than top-aligned). */
.arch-split[data-align="center"] { align-items: center; }

/* ── card-grid — N columns of uniform cards ───────────────────────────────
   Author-set column count. `diagrams.css .data-cards` uses auto-fit/minmax, which
   cannot express "exactly four across" — the reference deck's 4x2 bio grid and
   2x3 taxonomy both depend on a fixed arity.
   B-113 (Q3): the DEFAULT arity is a theme dial — `composition.gridCols` emits
   `--grid-cols`, so a sparse art direction can deal two big cards where a dense
   one deals three, on the same markup. An author who spells `data-cols` out keeps
   exactly what they wrote (the attribute rules below win on specificity), which
   is the B-089 contract unchanged. */
.arch-card-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols, 3), 1fr);
  gap: var(--arch-gap);
  width: 100%;
}

/* B-089: `3` is the default written above, so this rule is redundant TODAY — and that
   is exactly why it is here. The shipped card-grid fragment writes data-cols="3", which
   keyed no rule at all: it rendered correctly by accident, and the day the default
   changes every deck that spelled its arity out would silently follow. An attribute an
   author is told to write must mean what it says. Same for data-up="3" below. */
.arch-card-grid[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
.arch-card-grid[data-cols="2"] { grid-template-columns: repeat(2, 1fr); }
.arch-card-grid[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
.arch-card-grid[data-cols="5"] { grid-template-columns: repeat(5, 1fr); }

/* A column sub-head carries the eyebrow at its own recursed scale (motif.css binds
   `.col-label`), which is what makes a grid read as grouped rather than as a wall. */
.arch-card-grid .col-label {
  grid-column: 1 / -1;
  font-size: var(--small-size);
  font-weight: 700;
  color: var(--fg);
}

/* ── stat-row — pinned 3/4/5-up oversized numerals ────────────────────────
   Used both as a whole slide and as a band inside another archetype. Arity is
   pinned so the row keeps its rhythm when one label wraps. */
.arch-stat-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--arch-gap);
  align-items: stretch;
  width: 100%;
}

.arch-stat-row[data-up="3"] { grid-template-columns: repeat(3, 1fr); }
.arch-stat-row[data-up="4"] { grid-template-columns: repeat(4, 1fr); }
.arch-stat-row[data-up="5"] { grid-template-columns: repeat(5, 1fr); }

.arch-stat {
  display: flex;
  flex-direction: column;
  /* Pack the figure and its caption together at the top. `min-height` keeps the row
     rhythm even when one caption wraps, but with the default stretch the extra
     height went BETWEEN the number and its label — measured 91px apart, which reads
     as two unrelated items rather than one stat. The reserved height now sits below
     the pair. */
  justify-content: flex-start;
  gap: var(--arch-gap-tight);
  min-height: var(--arch-row-min);
}

.arch-stat .arch-figure {
  font-family: var(--font-heading);
  /* B-114 (P1-3): the figure gets its own dial. It used to read `--h2-size` directly,
     so making the statistics larger meant making every section heading larger — one
     knob wired to two unrelated roles. `type.figureSize` emits `--figure-size`
     (the `type` group is an open record); a preset that says nothing keeps the
     h2-sized default, byte-identical. */
  font-size: var(--figure-size, var(--h2-size));
  font-weight: 800;
  line-height: 1;
  color: var(--accent-text);
  /* Kill the paragraph margins. `justify-content: flex-start` alone did NOT close the
     gap — the real cause was `<p>`'s own margin: the figure's `margin-bottom` is 1em
     of a 48px font, plus the flex row-gap, plus the label's `margin-top`, measured at
     exactly 80.4px. Adjacent flex siblings do not collapse margins, so the pair read
     as two unrelated items. The gap token is the only spacing here. */
  margin: 0;
}

.arch-stat .arch-label {
  font-size: var(--small-size);
  color: var(--muted);
  margin: 0;
}

/* ── statement — the deck's "so what" layer ───────────────────────────────
   Stacked centred blocks of stepping width, terminating in a solid accent slab.
   The reference deck reserves its second typeface for exactly this. */
.arch-statement {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--arch-gap);
  text-align: center;
  width: 100%;
}

.arch-statement > * { max-width: 80%; }
.arch-statement > * + * { max-width: 66%; }

.arch-statement .arch-verdict {
  max-width: 100%;
  background: var(--accent-field);
  color: var(--on-accent);
  padding: var(--arch-gap-tight) var(--arch-gap);
  border-radius: var(--radius-md);
  font-weight: 700;
}

/* ── chart-panel — a chart in a borderless card with a labelled panel ──────
   The panel label is indented to clear the eyebrow column, the unit note sits
   outside the plot, and the source line lives inside the card. */
.arch-chart-panel {
  display: flex;
  flex-direction: column;
  gap: var(--arch-gap-tight);
  background: var(--surface-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--motif-elev-2, none);
  padding: var(--arch-gap);
  width: 100%;
}

.arch-chart-panel .arch-panel-label {
  font-size: var(--small-size);
  font-weight: 700;
  color: var(--fg);
}

/* ROUND-6: these two were scoped to `.arch-chart-panel` and nothing else, so the same
   class on any other slide got NO rule — a citation under a stat row rendered at
   `--body-size` in full `--fg`, which is larger and higher-contrast than the
   `.arch-label` it annotates. The footnote outweighed the figure's own caption.
   `.arch-unit` and `.arch-source` name CONTENT (a unit note, a source line), not a
   location, and an author reaches for them wherever there is a claim to cite. So the
   base rule is unscoped.

   B-088 two grounds: the unscoped rule takes `--muted` because a citation in slide flow
   sits on the PAGE; the panel override takes `--muted-on-surface` because the panel
   paints `--surface-bg`. `--muted` derived against the page lands 4.11-4.35:1 on a card,
   which is the "right derivation, wrong ground" error one level down.

   B-089 could not see this class of defect: the class IS defined, just not in a rule that
   matches here. "Defined somewhere in the shared CSS" is a weaker property than "has a
   matching rule in this context", and a grep cannot tell them apart. B-094 closes it —
   ci-validate-archetypes.mjs check 7 asks Chromium via CDP which selectors actually
   matched each node. Re-scoping these two lines back under `.arch-chart-panel` is that
   check's negative test: it fails with exactly two findings, `<p class="arch-source">` on
   broadsheet.html and on signal.html, in both themes. The fragment gallery alone stays
   green through it — split.html's copies sit inside a panel — which is why the check has
   to run over decks. */
.arch-unit,
.arch-source {
  font-size: var(--small-size);
  color: var(--muted);
}

/* B-114 (P1-3): a caption and a citation are different roles, and they rendered in the
   same type — `--small-size` + `--muted` both — so on a stat slide the label under the
   number and the source line at the bottom read as the same element. Korean text rules
   out the classic small-caps/italic levers, and size or colour moves would re-open the
   B-088 contrast ledger; WEIGHT is the axis with no side effects. The label (it NAMES
   a figure) carries 600; the citation stays 400. Unscoped for the same reason
   `.arch-unit`/`.arch-source` are — the class names content, not a location. */
.arch-label { font-weight: 600; }

/* B-117 (round 12): the uniform column rhythm erased the PROXIMITY cue — a source
   line sat exactly as far from the table it cites as from everything else, so
   attachment was carried by type alone. Gestalt says a citation belongs to its
   evidence: the block BEFORE a source line halves its rhythm gap. `:has(+ …)` is
   already load-bearing in this file (B-109), so the support floor is unchanged. */
.slide-content > *:has(+ .arch-source) { margin-block-end: calc(var(--element-gap) / 2); }

.arch-chart-panel .arch-unit,
.arch-chart-panel .arch-source {
  color: var(--muted-on-surface);
}

.arch-chart-panel .arch-source { margin-top: auto; }

/* ── timeline-h — horizontal milestone rail ───────────────────────────────
   The shipped `.timeline` (diagrams.css) is VERTICAL, which is the wrong axis for
   a roadmap band. Nodes sit on a connector clipped at each circle edge; labels are
   centred on their node but share a top edge so the row does not go ragged. */
.arch-timeline-h {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: var(--arch-gap-tight);
  position: relative;
  width: 100%;
  padding-top: var(--arch-gap-tight);
}

.arch-timeline-h::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--arch-gap-tight) + 0.5rem);
  height: 2px;
  /* The rail IS the graphic that makes this a timeline, so it has to clear the 3:1
     non-text line (WCAG 1.4.11). The accent tint it used measured 1.03:1 dark /
     1.10:1 light — effectively invisible, leaving only the emphasis dot readable.
     --muted is the token already measured against both page backgrounds. */
  background: var(--muted);
}

.arch-node {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--arch-gap-tight);
  text-align: center;
}

.arch-node::before {
  content: "";
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  /* Same reason as the rail: a node that does not read is not a node. */
  background: var(--muted);
}

.arch-node[data-emphasis="true"]::before {
  background: var(--accent);
  box-shadow: 0 0 0 0.35rem var(--accent-surface);
}

.arch-node .arch-label { font-size: var(--small-size); color: var(--muted); }

/* ── process-flow — source cards → connector → outcome cards ──────────────
   A state change across the gutter is the point: outline on the left, filled on
   the right. */
.arch-process-flow {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: var(--arch-gap);
  align-items: center;
  width: 100%;
}

.arch-process-flow .arch-stack {
  display: flex;
  flex-direction: column;
  gap: var(--arch-gap-tight);
}

.arch-process-flow .arch-arrow {
  font-size: var(--h3-size);
  color: var(--accent-text);
}

/* ── logo-wall — categorised third-party marks ────────────────────────────
   Deliberately separate from card-grid despite similar geometry: it carries a
   different contract (licensed external assets, optically rather than
   mechanically weighted). CSS supplies the grid and the ratio-locked well; the
   files and the optical judgement are a human's. */
.arch-logo-wall {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--arch-gap);
  align-items: center;
  width: 100%;
}

.arch-logo-wall[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
.arch-logo-wall[data-cols="6"] { grid-template-columns: repeat(6, 1fr); }

.arch-logo-wall .arch-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 3 / 2;
}

.arch-logo-wall .arch-mark img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* ── cover — asymmetric two-zone field ───────────────────────────────────
   An accent band plus an off-master lockup inset. No chrome, no folio. */
.arch-cover {
  display: grid;
  grid-template-columns: 38fr 62fr;
  gap: var(--arch-gap);
  align-items: end;
  width: 100%;
  min-height: 60%;
}

.arch-cover .arch-band {
  align-self: stretch;
  background: var(--accent-field);
  color: var(--on-accent);
  border-radius: var(--radius-md);
  padding: var(--arch-gap);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--arch-gap-tight);
}

/* NO opacity here. `opacity: 0.85` on this label measured 4.14:1 (dark) and 4.41:1
   (light) against the accent band — below AA — and it slipped through every gate
   because the contrast scanners composite ancestor BACKGROUNDS but ignore element
   opacity. An independent review caught it by sampling DPR-3 screenshot pixels.
   If a label needs to recede, use a token that was measured at full strength. */
.arch-cover .arch-band .arch-label { color: var(--on-accent); }

/* The band's datum: the one number the cover is about. Sized off the display step so
   it reads as the page's peak, and coloured with the measured on-accent rather than a
   hand-picked white. Without a slot like this the band is a large empty accent slab —
   the deck's biggest surface spent on nothing. */
.arch-cover .arch-band .arch-figure {
  font-family: var(--font-heading);
  font-size: var(--figure-size, var(--h2-size));
  font-weight: 800;
  line-height: 1;
  color: var(--on-accent);
  margin: 0;
  margin-bottom: auto;
}

/* ── bespoke-canvas — the honest escape hatch ─────────────────────────────
   Roughly 18% of the reference deck is hand-placed vector work where the
   PLACEMENT carries the meaning: a disease matrix, an MoA flow with labels parked
   in negative space, a funnel with an intentional text collision. A generator that
   emits a real grid there destroys the diagram. This archetype's contract is to
   make NO layout decisions: it is a positioned stage for hand-authored SVG or
   absolutely-positioned markup, sized to the slide and otherwise inert.
   Naming it is what stops a generator from reaching for card-grid instead. */
.arch-bespoke-canvas {
  position: relative;
  width: 100%;
  flex: 1;
  min-height: 0;
}

.arch-bespoke-canvas > svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ── vertical rhythm belongs to the archetypes ─────────────────────────────
   Every container here spaces its children with `gap`, and then the browser's own
   `p`/heading margins add on top: a review measured intended 18-36px seams rendering
   at 56.4, 61.2, 73.2, 74.4 and 91.2px — a date floating away from its milestone, a
   headline floating away from its subtitle. Round three fixed this for `.arch-stat`
   alone by pinning; that treated the instance, not the cause. Inside a gap-based
   container the gap is the ONLY vertical spacing. */
.arch-zone > *,
.arch-node > *,
.arch-stack > *,
.arch-band > *,
.arch-statement > *,
.arch-chart-panel > * {
  margin-block: 0;
}

/* ── shared bits ─────────────────────────────────────────────────────────── */

/* A zone inside split/process-flow that should stack its own children. */
.arch-zone {
  display: flex;
  flex-direction: column;
  gap: var(--arch-gap-tight);
  min-width: 0; /* let grid children shrink instead of forcing overflow */
}

/* Every archetype lives inside `.slide-content`, which is `overflow: hidden`
   (base.css:220) — so grid children must be allowed to shrink or they push their
   content out of the clip with no scrollbar to reveal it. */
.arch-split > *,
.arch-card-grid > *,
.arch-stat-row > *,
.arch-process-flow > *,
.arch-logo-wall > *,
.arch-timeline-h > * {
  min-width: 0;
}

/* SHORT stages, keyed on height. Keying the tightening on width alone missed the
   cases that actually lose content: at 1280x600 the width rule never applies, and the
   deck was still dropping 94px off a chart slide. Height is the axis under pressure in
   a windowed browser or a 4:3 projector, so it gets its own condition. Complements the
   runtime fit pass rather than replacing it — CSS reclaims the room cheaply, and the
   fit pass then has a smaller deficit to close (or none). */

/* NARROW **and** SHORT — the worst case, and the one where collapsing to a single
   column is the wrong trade. At 900x600 the stacked process-flow put 95px off the
   stage even after the rhythm tightened: four cards plus a connector simply do not
   fit vertically. Keep the two zones side by side and let the connector become a
   horizontal rule between them, because when height is the scarce axis, width is what
   there is to spend. Declared after the width-only block so it wins. */

/* B-112: a viewport `@media` block used to live here — the narrow/short-stage
   folds. They are gone because they cannot be right on a fixed canvas: the canvas
   is always 1920x1080, so a rule keyed on the WINDOW would apply a small-screen
   fold to a full-size slide the moment someone shrank the browser. The problem
   they solved (content not fitting a small stage) is solved upstream now — the
   whole canvas scales, so the stage is never small in canvas pixels. Recovered
   from git history if a responsive canvas ever comes back. */
