/*
 * Gyrum Canon — root styles (post-eat-your-dog-food refactor).
 *
 * The page consumes:
 *   - @gyrum-labs/tokens for every var(--…) (vendored at ./vendor/tokens/)
 *   - @gyrum-labs/components for chrome primitives — <gy-hero>,
 *     <gy-list-row>, <gy-empty-state>, <gy-badge>, etc (vendored at
 *     ./vendor/components/, registered by ./js/canon-components.js)
 *
 * Bespoke .canon-* classes here are limited to layout structure that
 * isn't (yet) a published primitive — the page container, the footer
 * shell, the backlink. Everything content-bearing is a gy-* element.
 *
 * Mode: home (default). data-mode="ship" | operate | infra | experiment
 * on <html> rebinds the colour family across both vendored chrome AND
 * page CSS in one go (Phase B token-page mode-switcher exercises this).
 */

@import url("./vendor/tokens/tokens.css");

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--accent-soft);
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
}

/* ── Page container ──────────────────────────────────────────────── */

.canon-main {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--space-6) var(--page-gutter);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.canon-backlink {
  margin: 0;
  font-size: var(--font-size-sm);
}

/* ── Section list (landing) ─────────────────────────────────────── */

.canon-sections {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  /* gy-list-row already brings its own bottom-divider rhythm.
     The card wrapping turns the rows into a single grouped surface. */
  overflow: hidden;
}

.canon-sections gy-list-row {
  padding: 0 var(--space-4);
}

/* ── Row title-column floor + meta shrinkability (warp 2656) ──
 * The vendored gy-list-row's shadow DOM is `.name {flex:1 1 auto;
 * min-width:0}` + `.meta {flex:0 0 auto}` — under width pressure
 * .meta refuses to shrink and .name has no floor, so a wide meta
 * slot starves the title column to ~min-content and the title
 * wraps character-by-character (operator-reported on /decisions
 * for ADR-176 + ADR-175, 2026-05-14).
 *
 * Sister ticket warp 2664 (PR#45) capped meta content at 130 chars
 * in render-corpus-index.js — that handles the OBSERVED data set,
 * but the STRUCTURAL rigidity remains: any future row content (a
 * badge + dot + chip + chip) can re-trigger the same collapse.
 *
 * Fix the structural bug at the CSS layer via shadow-parts the
 * vendor already exposes (part="name", part="meta"):
 *   - .name gets a min-width floor — 200px per ticket guidance, so
 *     short meta rows still see the title at full width, AND under
 *     width pressure the title stays readable instead of collapsing
 *   - .meta becomes shrinkable (flex: 0 1 auto) so it yields first
 *     when the row is space-constrained, and gets a max-width cap so
 *     even a regression in the JS truncation can't expand it past a
 *     reasonable share of the row
 *
 * Belt + braces with the JS cap: JS keeps typical-case content tidy,
 * CSS guarantees the structural floor for the title column. */
.canon-sections gy-list-row::part(name) {
  min-width: 200px;
}
.canon-sections gy-list-row::part(meta) {
  flex: 0 1 auto;
  max-width: 60%;
}

/* ── Per-mode preview rail + tint (warp 2602 → amped in warp 2640) ──
 * Each section card opts into the mode of the page it links to via
 * data-mode-preview="X". 6px rail + soft per-mode background tint so
 * the palette is unmistakable at a glance; the `<strong>` heading in
 * each card carries the mode accent color. Without this combination
 * the rails proved too subtle to register against a uniformly slate
 * page (operator review 2026-05-14).
 *
 * Tokens come from @gyrum-labs/tokens (vendor/tokens/tokens.css);
 * no hex literals — the "no literal hex" gate fails the build if
 * one slips in. */
.canon-sections gy-list-row[data-mode-preview="home"] {
  border-left: 6px solid var(--home-accent);
  background: var(--home-accent-soft);
}
.canon-sections gy-list-row[data-mode-preview="home"] strong {
  color: var(--home-accent);
}
.canon-sections gy-list-row[data-mode-preview="ship"] {
  border-left: 6px solid var(--ship-accent);
  background: var(--ship-accent-soft);
}
.canon-sections gy-list-row[data-mode-preview="ship"] strong {
  color: var(--ship-accent);
}
.canon-sections gy-list-row[data-mode-preview="operate"] {
  border-left: 6px solid var(--operate-accent);
  background: var(--operate-accent-soft);
}
.canon-sections gy-list-row[data-mode-preview="operate"] strong {
  color: var(--operate-accent);
}
.canon-sections gy-list-row[data-mode-preview="infra"] {
  border-left: 6px solid var(--infra-accent);
  background: var(--infra-accent-soft);
}
.canon-sections gy-list-row[data-mode-preview="infra"] strong {
  color: var(--infra-accent);
}
.canon-sections gy-list-row[data-mode-preview="experiment"] {
  border-left: 6px solid var(--experiment-accent);
  background: var(--experiment-accent-soft);
}
.canon-sections gy-list-row[data-mode-preview="experiment"] strong {
  color: var(--experiment-accent);
}

/* ── Index stats strip + recent releases band (warp 2636) ──────────
 * Two narrow strips between the hero and the section grid on the
 * landing page. The stats strip ("99 tokens · 24 components · ...")
 * answers "is this catalog real?" before scroll; the recent-releases
 * band ties the index back to /changelog.html. Both render from data
 * files at runtime — no hardcoded counts. Empty states are silent so
 * the page remains structural if any fetch fails. */

.canon-stats-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-5) 0 var(--space-3) 0;
}

.canon-stats-strip:empty {
  display: none;
}

/* Stat chips: each chip carries data-mode="X" matching the kind it
 * counts. Background uses the mode's --accent-soft, count uses the
 * mode's accent. Default (no data-mode) falls back to surface/accent
 * for home-mode rendering. (warp 2640) */
.canon-stat-chip {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
  font-size: var(--font-size-sm);
}

.canon-stat-chip[data-mode="home"]       { background: var(--home-accent-soft);       border-color: var(--home-accent); }
.canon-stat-chip[data-mode="ship"]       { background: var(--ship-accent-soft);       border-color: var(--ship-accent); }
.canon-stat-chip[data-mode="operate"]    { background: var(--operate-accent-soft);    border-color: var(--operate-accent); }
.canon-stat-chip[data-mode="infra"]      { background: var(--infra-accent-soft);      border-color: var(--infra-accent); }
.canon-stat-chip[data-mode="experiment"] { background: var(--experiment-accent-soft); border-color: var(--experiment-accent); }

.canon-stat-chip[data-mode="home"]       .canon-stat-chip__count { color: var(--home-accent); }
.canon-stat-chip[data-mode="ship"]       .canon-stat-chip__count { color: var(--ship-accent); }
.canon-stat-chip[data-mode="operate"]    .canon-stat-chip__count { color: var(--operate-accent); }
.canon-stat-chip[data-mode="infra"]      .canon-stat-chip__count { color: var(--infra-accent); }
.canon-stat-chip[data-mode="experiment"] .canon-stat-chip__count { color: var(--experiment-accent); }

.canon-stat-chip:hover {
  filter: brightness(0.97);
  text-decoration: none;
}

.canon-stat-chip__count {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  color: var(--accent);
}

.canon-stat-chip__label {
  color: var(--text-muted);
}

.canon-recent-band {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-5) 0;
  font-size: var(--font-size-sm);
}

.canon-recent-band:empty {
  display: none;
}

/* Recent-release chips: they're all shipped successful releases, so
 * use the --ok status family (green) — the chip itself communicates
 * "active, healthy, recent" without reading the label. (warp 2640) */
.canon-recent-chip {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  background: var(--ok-soft);
  border-radius: 99em;
  color: var(--ok);
  text-decoration: none;
  font-weight: var(--font-weight-semibold);
}

.canon-recent-chip strong {
  color: var(--ok);
}

.canon-recent-chip:hover {
  filter: brightness(0.97);
  text-decoration: none;
}

.canon-recent-chip--all {
  background: transparent;
  color: var(--accent);
  font-weight: var(--font-weight-semibold);
}

/* ── Mode indicator pill (warp 2632) ───────────────────────────────
 * Small pill on mode-mapped pages telling the visitor which mode the
 * chrome reflects, deep-linking into /pages/style-apply.html#modes
 * (the canonical rule book from warp 2597). Home-mode pages don't
 * render the pill — neutral chrome is the default; nothing to label.
 *
 * The pill picks up the active mode's --accent-soft for its background
 * because the page's <html data-mode="X"> has already rebound that
 * token via vendor/tokens/tokens.css. No per-mode overrides needed
 * here. */
.mode-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 99em;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  text-decoration: none;
  margin-left: var(--space-3);
  vertical-align: middle;
  white-space: nowrap;
}

.mode-pill:hover {
  text-decoration: underline;
}

/* ── Stub-page host (components/decisions/principles/search) ────── */

.canon-stub-host {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  text-align: center;
}

.canon-stub-phase {
  margin: 0;
}

/* ── Footer ──────────────────────────────────────────────────────── */

.canon-footer {
  border-top: 1px solid var(--border);
  padding: var(--space-5) var(--page-gutter);
  margin-top: var(--space-6);
  background: var(--surface);
}

.canon-footer__inner {
  max-width: var(--page-max);
  margin: 0 auto;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.canon-footer__inner p {
  margin: 0 0 var(--space-1) 0;
}

.canon-footer__build {
  font-family: var(--font-mono);
}

/* ── /tokens page (Phase B) ─────────────────────────────────────── */

.tokens-page {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.tokens-family {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--space-5);
}

.tokens-family__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-2);
}

.tokens-family__title {
  font-size: var(--font-size-h2);
  font-weight: var(--font-weight-semibold);
  margin: 0;
}

.tokens-family__source {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.tokens-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-3);
}

.token-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
}

.token-card__swatch {
  height: 56px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
}

.token-card__name {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text);
  word-break: break-all;
}

.token-card__value {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

/* Mode-switcher for the colour-modes family — clicking a chip rebinds
   the entire page's :root[data-mode] so visitors can see the same
   tokens consumed across all five modes. */
.tokens-mode-switcher {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.tokens-mode-chip {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text);
  cursor: pointer;
}

.tokens-mode-chip[aria-pressed="true"] {
  background: var(--accent);
  color: var(--text-on-accent);
  border-color: var(--accent);
}

.typography-specimen {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border);
}

.typography-specimen:last-child {
  border-bottom: none;
}

.typography-specimen__sample {
  color: var(--text);
  flex-grow: 1;
}

.typography-specimen__meta {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  white-space: nowrap;
}

.space-specimen {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}

.space-specimen:last-child {
  border-bottom: none;
}

.space-specimen__bar {
  background: var(--accent);
  height: 12px;
  border-radius: var(--radius-sm);
}

.space-specimen__name {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text);
  min-width: 80px;
}

.space-specimen__value {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.radius-specimen {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
}

.radius-specimen__box {
  width: 48px;
  height: 48px;
  background: var(--accent);
  border: 1px solid var(--border);
}

.shadow-specimen {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* ── /components catalog + detail (Phase D) ─────────────────────── */

.canon-tier-h {
  font-size: var(--font-size-h1);
  font-weight: var(--font-weight-bold);
  margin: var(--space-6) 0 var(--space-1) 0;
  letter-spacing: -0.01em;
}

/* Meta-tier heading (canon ticket 3388) — demoted from H2 reference
   weight to H3 meta weight for "Where this lives", the section that
   carries the import + diagnostic rather than primary documentation.
   Sized at --font-size-h2 so the typographic gap reads as one step
   below the H2 reference tier; muted so the visual rhythm reads
   Live > Markup ≈ Prop space > Where this lives. */
.canon-tier-h--meta {
  font-size: var(--font-size-h2);
  font-weight: var(--font-weight-semibold);
  color: var(--text-muted);
  letter-spacing: 0;
}

.components-tier:first-child .canon-tier-h {
  margin-top: 0;
}

.canon-tier-blurb {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  margin: 0 0 var(--space-4) 0;
  max-width: var(--measure);
}

/* ── Components catalog: inline-specimen rows (warp 2675) ──────────
 * 3-zone row: [name+meta+desc] [specimen] [chevron]. The specimen
 * column carries the entry's example_html rendered live — visitors
 * see the component before reading about it. Templates + pages
 * (too large to render inline) get an empty specimen + chevron only.
 *
 * Per-tier accent rail on the left edge ties each row's tier to the
 * mode vocabulary: atoms → home-slate, molecules → ship-soft,
 * organisms → operate-blue, templates → infra, pages → experiment.
 * (warp 2640 color amp pattern, applied to the catalog.) */

.components-tier {
  margin-bottom: var(--space-6);
}

.component-row {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
  margin-bottom: var(--space-2);
  transition: background 120ms ease, border-color 120ms ease;
}

.component-row:hover {
  background: var(--accent-soft);
  text-decoration: none;
}

.component-row[data-tier="atom"]      { border-left-color: var(--home-accent); }
.component-row[data-tier="molecule"]  { border-left-color: var(--ship-accent); }
.component-row[data-tier="organism"]  { border-left-color: var(--operate-accent); }
.component-row[data-tier="template"]  { border-left-color: var(--infra-accent); }
.component-row[data-tier="page"]      { border-left-color: var(--experiment-accent); }

.component-row__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  grid-column: 1;
  grid-row: 1;
}

.component-row__name {
  font-family: var(--font-mono);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--text);
}

.component-row__meta {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.component-row__desc {
  grid-column: 1;
  grid-row: 2;
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  margin-top: var(--space-1);
  max-width: var(--measure);
}

.component-row__specimen {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  justify-content: flex-end;
  max-height: 96px;
  overflow: hidden;
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--bg);
  /* Empty specimen (template/page tiers) collapses to nothing visible. */
  min-height: 0;
}

.component-row__specimen:empty {
  display: none;
}

.component-row__chevron {
  grid-column: 3;
  grid-row: 1 / span 2;
  font-size: var(--font-size-lg);
  color: var(--text-muted);
  align-self: center;
}

.component-row:hover .component-row__chevron {
  color: var(--accent);
}

/* Narrow viewports: stack into a single column, hide specimen. */
@media (max-width: 720px) {
  .component-row {
    grid-template-columns: 1fr auto;
  }
  .component-row__specimen {
    display: none;
  }
  .component-row__chevron {
    grid-column: 2;
    grid-row: 1;
  }
}

/* ── Corpus catalog: doc-row shape (warp 2678) ──────────────────────
 * Sibling to .component-row for the corpus pages (decisions /
 * principles / guidelines / brand). 4-zone layout:
 *
 *   [id badge] [title + summary stack] [status pill + date + tags] [→]
 *
 * The id badge consumes the page's mode-accent (each corpus page
 * declares data-mode on <html> — warp 2598 / 2630). The status pill
 * carries the warp 2598 .status-* palette mapped per entry.status.
 * Tag chips render small + muted; cap at 4 with "+N more" overflow.
 * The chevron + hover communicate clickability. */

.doc-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
  margin-bottom: var(--space-2);
  transition: background 120ms ease, border-color 120ms ease;
}

.doc-row:hover {
  background: var(--accent-soft);
  text-decoration: none;
}

.doc-row__id {
  font-family: var(--font-mono);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-bold);
  color: var(--accent);
  background: var(--accent-soft);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  min-width: 3.5ch;
  text-align: center;
  align-self: center;
  grid-row: 1 / span 2;
}

.doc-row__stack {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.doc-row__title {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--text);
  line-height: var(--line-height-tight);
}

.doc-row__summary {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  margin: 0;
  line-height: var(--line-height-base);
}

.doc-row__meta {
  grid-column: 3;
  grid-row: 1 / span 2;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
  min-width: 0;
}

.doc-status-pill {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-2);
  border-radius: 99em;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
}

.doc-row__date {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.doc-row__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  justify-content: flex-end;
  max-width: 200px;
}

.doc-tag-chip {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-2);
  border-radius: 99em;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--font-size-xs);
  white-space: nowrap;
}

.doc-tag-chip--more {
  background: transparent;
  border-color: transparent;
  font-style: italic;
}

.doc-row__chevron {
  grid-column: 4;
  grid-row: 1 / span 2;
  font-size: var(--font-size-lg);
  color: var(--text-muted);
  align-self: center;
}

.doc-row:hover .doc-row__chevron {
  color: var(--accent);
}

@media (max-width: 720px) {
  .doc-row {
    grid-template-columns: auto 1fr auto;
  }
  .doc-row__meta {
    grid-column: 1 / -1;
    grid-row: 3;
    flex-direction: row;
    align-items: center;
    align-self: stretch;
    justify-content: flex-start;
    padding-top: var(--space-2);
  }
  .doc-row__chevron {
    grid-column: 3;
    grid-row: 1;
  }
}

/*
 * Live pane — the hero band on the component detail page (warp ticket 3388).
 * Operator UX critique 2026-05-18: prior shape rendered Live / Markup /
 * Prop space / Where this lives all at the same H2 weight, which painted
 * the page as a flat reference document. The Live render IS the deliverable
 * the consumer ships, so it gets the largest heading (Live__h, sized via
 * calc on --font-size-h1 to clear the H2 reference tier), a one-line
 * caption ("What you ship.") that names why it's the hero, and bordered
 * padding emphasis around the frame itself. Markup + Prop space stay at
 * H2 reference weight; Where this lives demotes to H3 meta.
 */
.component-live {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.component-live--hero {
  border-top: 4px solid var(--accent, var(--border));
  padding: var(--space-5) 0 var(--space-5) 0;
  margin-bottom: var(--space-5);
}

.component-live__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.component-live__h {
  font-size: calc(var(--font-size-h1) * 1.5);
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.02em;
  margin: 0;
  line-height: 1.1;
}

.component-live__caption {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  margin: 0;
  font-style: italic;
}

.component-live__frame {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-items: flex-start;
}

/*
 * Variant gallery (warp ticket 3384) — multi-variant components
 * (action-button, badge, button, form, overview) render one card per
 * declared variant instead of a single specimen. Each card is its own
 * bounded frame with an attribute-shaped label (variant=primary,
 * width=default) so the visual rhythm reads clearly and the consumer
 * can tell at-a-glance which value produces which render. The gallery
 * wraps at narrow widths via flex-wrap; each card flexes to a sensible
 * min-width so two cards sit side-by-side at typical reading widths.
 * NB: this comment intentionally avoids the # character — the
 * scripts/test.sh Phase-B no-literal-hex check refuses any /^#[0-9A-Fa-f]{3,8}$/
 * shape (including warp ticket references) in styles.css.
 */
.component-live__gallery {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.component-live__variant-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  flex: 1 1 240px;
  min-width: 0;
}

.component-live__variant-label {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted, var(--text));
  text-transform: none;
}

.component-live__variant-label code {
  background: transparent;
  padding: 0;
}

.component-live__frame--variant {
  padding: var(--space-4);
  min-height: 4rem;
}

.component-source {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* Markup-block header row (canon ticket 3388) — heading on the left,
   [Copy] button anchored to the right. Pattern-matches the
   `.component-where__copy` shape so the two copy affordances read as
   one shared canon idiom rather than two ad-hoc widgets. */
.component-source__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.component-source__head .canon-tier-h {
  margin: 0;
}

.component-source__copy {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: inherit;
  cursor: pointer;
  line-height: 1.4;
}

.component-source__copy:hover {
  background: var(--bg-subtle, var(--surface));
}

.component-source__copy:focus-visible {
  outline: 2px solid var(--focus, currentColor);
  outline-offset: 2px;
}

.component-source__code {
  background: var(--bg-subtle, var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
}

.component-props {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* "Where this lives" — concrete import + secondary diagnostic meta
   (canon ticket 3379). The import-statement row mirrors the
   .component-source code shape but adds a [Copy] button anchored to
   the right; the secondary <dl> renders the owner / build-state /
   blocker as small muted definition rows so the diagnostic stays
   visible but no longer competes with the headline import. */
.component-where {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.component-where__import {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
}

.component-where__code {
  flex: 1 1 auto;
  background: var(--bg-subtle, var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
}

.component-where__copy {
  flex: 0 0 auto;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: inherit;
  cursor: pointer;
  line-height: 1.4;
}

.component-where__copy:hover {
  background: var(--bg-subtle, var(--surface));
}

.component-where__copy:focus-visible {
  outline: 2px solid var(--focus, currentColor);
  outline-offset: 2px;
}

.component-where__meta {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  font-size: var(--font-size-xs);
  color: var(--text-muted, inherit);
}

.component-where__meta-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.component-where__meta-row dt {
  font-weight: var(--font-weight-semibold, 600);
}

.component-where__meta-row dd {
  margin: 0;
}

/* Prop-space table — see warp ticket 3374. Replaces the legacy single-cell
 * "props: foo, bar" join with a per-prop row carrying name / type /
 * default / description. Cell typography mirrors the markup-snippet
 * code-block (var(--font-mono), --font-size-xs) so the table reads
 * as code-adjacent rather than prose. */
.component-props__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.component-props__table thead th {
  text-align: left;
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-subtle, var(--surface));
  border-bottom: 1px solid var(--border);
}

.component-props__table tbody th,
.component-props__table tbody td {
  text-align: left;
  vertical-align: top;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
}

.component-props__table tbody tr:last-child th,
.component-props__table tbody tr:last-child td {
  border-bottom: none;
}

.component-props__table tbody th {
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
}

.component-props__table code {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
}

.component-props__required {
  color: var(--accent, var(--text));
  margin-left: var(--space-1, 4px);
  font-weight: var(--font-weight-semibold);
}

.component-props__note,
.component-props__slots {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle, var(--surface));
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

.component-props__note code,
.component-props__slots code {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
}

/* SR-only utility — used by the prop-space "no default" cell where
 * the visible em-dash needs a screen-reader equivalent. Declared
 * inline here (rather than in a shared utilities sheet) because no
 * sibling rule uses it yet; lift to a utility module the second
 * consumer asks. */
.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;
}

/* ── /decisions + /principles (Phase E) ─────────────────────────── */

.corpus-detail__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
}

.corpus-detail__body {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-6);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
}

.corpus-detail__body h1,
.corpus-detail__body h2,
.corpus-detail__body h3 {
  margin: var(--space-5) 0 var(--space-3) 0;
  font-weight: var(--font-weight-semibold);
}

.corpus-detail__body h1:first-child,
.corpus-detail__body h2:first-child {
  margin-top: 0;
}

.corpus-detail__body h2 {
  font-size: var(--font-size-h2);
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-2);
}

.corpus-detail__body h3 {
  font-size: var(--font-size-lg);
}

.corpus-detail__body p {
  margin: 0 0 var(--space-3) 0;
}

.corpus-detail__body ul,
.corpus-detail__body ol {
  margin: 0 0 var(--space-3) 0;
  padding-left: var(--space-5);
}

.corpus-detail__body li {
  margin-bottom: var(--space-1);
}

.corpus-detail__body code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--accent-soft);
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
}

.corpus-detail__body pre {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  overflow-x: auto;
  margin: 0 0 var(--space-3) 0;
}

.corpus-detail__body pre code {
  background: transparent;
  padding: 0;
}

.corpus-detail__body blockquote {
  border-left: 3px solid var(--accent);
  padding-left: var(--space-3);
  margin: var(--space-3) 0;
  color: var(--text-muted);
}

.corpus-detail__body table {
  border-collapse: collapse;
  margin: 0 0 var(--space-3) 0;
  width: 100%;
}

.corpus-detail__body th,
.corpus-detail__body td {
  border: 1px solid var(--border);
  padding: var(--space-2) var(--space-3);
  text-align: left;
}

.corpus-detail__body th {
  background: var(--accent-soft);
  font-weight: var(--font-weight-semibold);
}

.corpus-detail__fallback {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── /search (Phase F) ──────────────────────────────────────────── */

.search-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.search-input {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--font-size-lg);
}

.search-input:focus {
  outline: none;
  border-color: var(--accent);
}

.search-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.search-stats {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

/* ── /migrations (Phase 1 / structural canon-tier rule) ──────────── */

.migrations-summary {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
}

.migrations-summary .tokens-mode-chip {
  cursor: default;
}

.migrations-tier {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* ── /guides + /application + per-tier blurbs (warp 2568) ────────── */
/*
 * Note: this rule used to set `margin: calc(var(--space-2) * -1) 0 ...`
 * — a NEGATIVE top margin (-8px) that collided with the H2 baseline on
 * any page rendering a heading + blurb pair. The earlier .canon-tier-blurb
 * rule (line ~530, warp 2675) already specifies the correct margins;
 * leaving this block as a no-op anchor for the warp 2568 cite. (warp 2684)
 */

/*
 * warp 2652: TOC was wrapped in card chrome (border + surface + padding)
 * which made bullets read as inset past the card's left edge, and the
 * 2-column layout left the right column unbalanced. Canon pages already
 * carry plenty of bordered cards; the on-this-page index reads cleaner
 * as a flush, chrome-less list anchored to the section's left edge.
 * Heading-and-list layout is preserved; only the surface chrome is
 * dropped. The list keeps the 2-column shape with auto-balanced
 * column-fill so the right column no longer dangles short.
 */
.guides-toc {
  background: transparent;
  border: 0;
  padding: 0;
}

.guides-toc .canon-tier-h {
  margin-top: 0;
  font-size: var(--font-size-lg);
}

.guides-toc__list {
  margin: 0;
  padding-left: var(--space-5);
  columns: 2;
  column-gap: var(--space-6);
  column-fill: balance;
}

.guides-toc__list li {
  margin-bottom: var(--space-1);
}

.guide-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.guide-section p,
.guide-section ol,
.guide-section ul {
  margin: 0;
  max-width: 72ch;
}

.guide-section ol,
.guide-section ul {
  padding-left: var(--space-5);
}

.guide-section ol li,
.guide-section ul li {
  margin-bottom: var(--space-2);
}

.guides-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}

.guide-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.guide-card__h {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
}

.guide-card p {
  margin: 0;
  font-size: var(--font-size-sm);
}

.guide-card pre {
  margin: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-size: var(--font-size-xs);
  overflow-x: auto;
}

.guide-card__meta {
  margin-top: auto;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.composition-table,
.naming-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.composition-table th,
.composition-table td,
.naming-table th,
.naming-table td {
  border-bottom: 1px solid var(--border);
  padding: var(--space-2) var(--space-3);
  text-align: left;
  vertical-align: top;
}

.composition-table tr:last-child td,
.naming-table tr:last-child td {
  border-bottom: none;
}

.composition-table th,
.naming-table th {
  background: var(--accent-soft);
  font-weight: var(--font-weight-semibold);
}

.migration-steps {
  list-style: none;
  padding-left: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  counter-reset: migration-step;
}

.migration-steps > li {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  position: relative;
  counter-increment: migration-step;
}

.migration-steps > li::before {
  content: counter(migration-step);
  position: absolute;
  top: var(--space-3);
  right: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.migration-steps .guide-card__h {
  font-size: var(--font-size-base);
}

.migration-steps p {
  margin: var(--space-2) 0 0 0;
  font-size: var(--font-size-sm);
}

/* ── /patterns (warp 2572) ─────────────────────────────────────── */

.pattern {
  display: flex;
  flex-direction: column;
  /* warp 2652: was --space-3 (half of .canon-main's --space-6 rhythm),
     making the H2 / blurb / example block read cramped. Bumped to
     --space-5 so the sections breathe; --space-2 inside .pattern__head
     keeps the H2-and-blurb tightly paired underneath. */
  gap: var(--space-5);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
}

.pattern:last-of-type {
  border-bottom: none;
}

.pattern__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.pattern__head .canon-tier-h {
  margin: 0;
}

.pattern__blurb {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  max-width: 72ch;
}

.pattern__example {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-5);
  /* Patterns may render larger than the page width on narrow viewports;
     allow horizontal scroll within the example frame rather than break
     the page layout. */
  overflow-x: auto;
}

/*
 * warp 2652: viewer can't tell at-a-glance which block IS the rendered
 * pattern versus surrounding documentation prose. The "Live render"
 * label above each example frames it as the artifact — uppercase,
 * muted, small-tracked so it reads as eyebrow chrome rather than
 * content. Pseudo-element keeps the HTML clean across all five
 * pattern sections.
 */
.pattern__example::before {
  content: "Live render";
  display: block;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: var(--font-weight-semibold);
  margin-bottom: var(--space-3);
}

.pattern__source {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
}

.pattern__source summary {
  cursor: pointer;
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

.pattern__source[open] summary {
  margin-bottom: var(--space-3);
  color: var(--text);
}

.pattern__source pre {
  margin: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  overflow-x: auto;
  white-space: pre;
}

.pattern__tokens {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--text);
  max-width: 80ch;
}

.pattern__tokens code {
  font-size: 0.9em;
}

/* ── /coverage (warp 2574) ─────────────────────────────────────── */

.coverage-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.coverage-counts {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.coverage-totals {
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  font-size: var(--font-size-sm);
  color: var(--text);
  margin-top: var(--space-2);
}

.coverage-totals strong {
  margin-right: var(--space-2);
}

/* ── /sitemap (warp 2581) ──────────────────────────────────────── */

.sitemap-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.sitemap-tree {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.sitemap-group {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0;
  overflow: hidden;
}

.sitemap-group__summary,
.sitemap-folder__summary {
  cursor: pointer;
  padding: var(--space-3) var(--space-4);
  user-select: none;
  font-size: var(--font-size-base);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.sitemap-group__count,
.sitemap-folder__count {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.sitemap-group__children {
  border-top: 1px solid var(--border);
}

.sitemap-folder {
  background: var(--bg-subtle, var(--bg));
  border-top: 1px dashed var(--border);
}

.sitemap-folder:first-child {
  border-top: none;
}

.sitemap-folder__summary {
  padding-left: var(--space-5);
}

.sitemap-folder__children {
  padding-left: var(--space-3);
}

.sitemap-title--dynamic {
  font-style: italic;
}

.sitemap-path {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  margin-left: var(--space-2);
}

.sitemap-lock {
  font-size: var(--font-size-xs);
  margin-left: var(--space-1);
  opacity: 0.7;
}

@media (min-width: 48rem) {
  .sitemap-group__summary {
    pointer-events: none;
  }
}

/* ── /404 (warp 2581) ──────────────────────────────────────────── */

.fourohfour-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.fourohfour .canon-tier-h {
  margin-top: var(--space-4);
}

/* ── /changelog (warp 2585) ────────────────────────────────────── */

.changelog-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.changelog-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.changelog-release {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0;
  overflow: hidden;
}

.changelog-release__summary {
  cursor: pointer;
  padding: var(--space-3) var(--space-4);
  user-select: none;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--font-size-base);
  flex-wrap: wrap;
}

.changelog-release__version {
  font-family: var(--font-mono);
  font-size: var(--font-size-base);
}

.changelog-release__date,
.changelog-release__sha,
.changelog-release__count {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.changelog-release__sha {
  color: var(--accent);
}

.changelog-release[open] .changelog-release__summary {
  border-bottom: 1px solid var(--border);
}

.changelog-release__empty {
  padding: var(--space-3) var(--space-4);
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

.changelog-group {
  padding: var(--space-2) var(--space-4);
  border-top: 1px dashed var(--border);
}

.changelog-group:first-of-type {
  border-top: none;
}

.changelog-group__h {
  margin: var(--space-2) 0 var(--space-2) 0;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.changelog-entries {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.changelog-entry {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-size: var(--font-size-sm);
}

.changelog-entry__summary {
  flex: 1 1 60%;
}

.changelog-entry__ticket,
.changelog-entry__pr {
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  color: var(--accent);
}

/* ── Status utility classes (warp 2598 + warp 2597) ─────────────────
 *
 * Pill-shaped chips backed by the mode-invariant status tokens
 * (--ok / --warn / --bad / --never / --running and their *-soft
 * surfaces) defined in vendor/tokens/tokens.css. Used by:
 *   - coverage page (per-tier completeness badges)
 *   - changelog page (release-state chips)
 *   - the Style Apply Guide page (warp 2597) as the canonical
 *     "how to render a status badge" reference.
 *
 * Mode-invariance is deliberate: a status chip on an operate-mode
 * page (blue chrome) reads the same red as on an infra-mode page
 * (cyan chrome), because "bad" is bad regardless of surface theme.
 */
.status-ok,
.status-warn,
.status-bad,
.status-never,
.status-running {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0 var(--space-2);
  /* Pill-shape via em units — the canon-adoption gate forbids px
   * radius literals (warp 734), and the published token family caps
   * at --radius-xl (10px). em-radius at the font-size scale produces
   * the fully-rounded pill shape badges traditionally use without
   * introducing a new fixed-px hardcode. */
  border-radius: 99em;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  line-height: 1.6;
  white-space: nowrap;
  border: 1px solid transparent;
}

.status-ok {
  background: var(--ok-soft);
  color: var(--ok);
  border-color: var(--ok);
}

.status-warn {
  background: var(--warn-soft);
  color: var(--warn);
  border-color: var(--warn);
}

.status-bad {
  background: var(--bad-soft);
  color: var(--bad);
  border-color: var(--bad);
}

.status-never {
  background: var(--never-soft);
  color: var(--never);
  border-color: var(--never);
}

.status-running {
  background: var(--accent-soft);
  color: var(--running);
  border-color: var(--running);
}

/* ── Canon page typography overrides (warp 2598) ────────────────────
 *
 * Canon is a design-system showcase page that warrants more visual
 * weight than ai-frontend's chrome — the token-level --font-size-h1
 * (1.5rem) is calibrated for in-app surfaces where heading density
 * matters. canon.gyrum.ai's hero is the front door for the system;
 * scoping these overrides to .canon-main keeps the published token
 * sizes intact for every other consumer.
 *
 * Why scoped, not token-bumped: the tokens are the source-of-truth
 * across ai-frontend, warp, and every gyrum surface — bumping them
 * for canon's showcase needs would ripple visually-disruptive size
 * changes through every other product. The .canon-main scope is
 * the right boundary.
 */
.canon-main h1 {
  font-size: 2.25rem;
  line-height: var(--line-height-tight);
  font-weight: var(--font-weight-bold);
  margin: 0;
}

.canon-main h2 {
  font-size: 1.5rem;
  line-height: var(--line-height-tight);
  font-weight: var(--font-weight-semibold);
  margin: 0;
}

.canon-main .canon-lede,
.canon-main gy-hero [slot="lead"] {
  font-size: 1.125rem;
  line-height: var(--line-height-base);
  color: var(--text-muted);
}

/* ── /style-apply page (warp 2597) ─────────────────────────────────
 *
 * Style-apply guide page-specific layout. Status pills on this page
 * consume the shared .status-{ok,warn,bad,never,running} family
 * above (landed by warp 2598). The .style-apply-callout--* family
 * below is page-internal — soft-background callouts that pair each
 * pill with a longer description, anchored by --{status}-soft tokens.
 */

.style-apply-modes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-4);
}

.style-apply-mode {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.style-apply-mode__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.style-apply-mode__name {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
}

.style-apply-mode__rule {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--text);
}

.style-apply-mode__swatches {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-1);
}

.style-apply-swatch {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  min-height: 44px;
  display: flex;
  align-items: center;
}

.style-apply-note {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  max-width: 72ch;
}

.style-apply-status-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}

.style-apply-status {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.style-apply-status__rule {
  margin: 0;
  font-size: var(--font-size-sm);
}

.style-apply-status__callout {
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-size: var(--font-size-sm);
}

.style-apply-callout--ok {
  background: var(--ok-soft);
  color: var(--ok);
}

.style-apply-callout--warn {
  background: var(--warn-soft);
  color: var(--warn);
}

.style-apply-callout--bad {
  background: var(--bad-soft);
  color: var(--bad);
}

.style-apply-callout--never {
  background: var(--never-soft);
  color: var(--text-muted);
}

.style-apply-callout--running {
  background: var(--accent-soft);
  color: var(--running);
}

/* Shared container for the type-ramp and spacing-ramp sections —
 * reuses the .typography-specimen / .space-specimen rows from the
 * Tokens page rather than introducing parallel families. */
.style-apply-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
}

.style-apply-rule {
  font-size: var(--font-size-sm);
  color: var(--text);
}

.style-apply-pairs {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.style-apply-pair {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.style-apply-pair__head {
  padding: var(--space-3) var(--space-4);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  border-bottom: 1px solid var(--border);
}

.style-apply-pair__body {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

@media (max-width: 40rem) {
  .style-apply-pair__body {
    grid-template-columns: 1fr;
  }
}

.style-apply-pair__do,
.style-apply-pair__not {
  padding: var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.style-apply-pair__do {
  background: var(--ok-soft);
}

.style-apply-pair__not {
  background: var(--bad-soft);
  border-left: 1px solid var(--border);
}

@media (max-width: 40rem) {
  .style-apply-pair__not {
    border-left: none;
    border-top: 1px solid var(--border);
  }
}

.style-apply-pair__do h4,
.style-apply-pair__not h4 {
  margin: 0;
  font-size: var(--font-size-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.style-apply-pair__do h4 {
  color: var(--ok);
}

.style-apply-pair__not h4 {
  color: var(--bad);
}

.style-apply-pair__do p,
.style-apply-pair__not p {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--text);
}

/* ── /pages/assets.html — brand-asset catalogue (warp 2653) ──────────
 * Specimen grids for logo marks, station illustrations, the CSS-only
 * spinner, and the inline Pipeline Conveyor v1 SVG animation. Mirrors
 * the shape of /pages/tokens.html's specimen blocks but renders raster
 * + vector assets rather than computed token values. All colour comes
 * from @gyrum-labs/tokens; the literal colour values on
 * /pages/assets.html are inside <svg fill="..."> illustration
 * attributes, which the no-literal-hex gate only checks against
 * styles.css.
 */
.assets-page {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.assets-block {
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.assets-block h2 {
  font-size: var(--font-size-h2);
  font-weight: 700;
  margin: 0 0 var(--space-1);
}

.assets-meta {
  color: var(--fg-muted);
  font-size: var(--font-size-sm);
  margin: 0 0 var(--space-4);
}

.assets-meta code {
  background: var(--bg-subtle);
  padding: 1px 6px;
  border-radius: var(--radius-sm);
}

.assets-meta a {
  color: var(--fg);
  text-decoration: underline;
}

.assets-specimens {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  align-items: flex-end;
}

.assets-specimen {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
}

.assets-specimen img {
  display: block;
  width: 100%;
  height: auto;
}

.assets-specimen span {
  font-size: var(--font-size-xs);
  color: var(--fg-muted);
  font-family: var(--font-mono, monospace);
}

.assets-scene {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

/* ── Spinner specimens (CSS-only port of <gy-spinner>) ────────────────
 * The Lit element ships three :host([stage="…"]) keyframes for the
 * streaming / thinking / done states. The Svelte page mounted the
 * element directly; canon's static page inlines the same keyframes
 * with a wrapping span so the specimens render without the JS dep.
 */
.assets-spinner {
  display: inline-block;
  line-height: 0;
}

.assets-spinner img {
  display: block;
  transform-origin: 50% 50%;
}

.assets-spinner--thinking img {
  animation: assets-spinner-spin 2.4s linear infinite;
}

.assets-spinner--streaming img {
  animation:
    assets-spinner-spin 0.8s linear infinite,
    assets-spinner-pulse 0.4s ease-in-out infinite alternate;
}

.assets-spinner--done img {
  animation: assets-spinner-done 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes assets-spinner-spin {
  to { transform: rotate(360deg); }
}

@keyframes assets-spinner-pulse {
  to { transform: scale(1.08); }
}

@keyframes assets-spinner-done {
  0%   { transform: rotate(0deg) scale(1); }
  50%  { transform: rotate(40deg) scale(1.15); }
  100% { transform: rotate(0deg) scale(1); }
}

/* ── Pipeline conveyor v1 (inlined SVG animation) ─────────────────────
 * Hand-drawn package travels along the belt, gaining a sticker at every
 * station. Pure CSS keyframes, scoped to .assets-conveyor descendants.
 * All numeric timing values match ai-frontend/src/lib/components/
 * PipelineConveyor.svelte verbatim — same 8s master cycle, same
 * station phase offsets.
 */
.assets-conveyor-wrap {
  width: 100%;
  max-width: 760px;
  margin: var(--space-5) auto 0;
}

.assets-conveyor {
  width: 100%;
  height: auto;
  display: block;
}

.assets-stripe {
  animation: assets-belt-scroll 2.67s linear infinite;
}

@keyframes assets-belt-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(30px); }
}

.assets-wheel {
  transform-origin: center;
  transform-box: fill-box;
  animation: assets-wheel-spin 2.67s linear infinite;
}

@keyframes assets-wheel-spin {
  to { transform: rotate(360deg); }
}

.assets-station-head {
  animation: assets-head-pulse 8s ease-in-out infinite;
}

@keyframes assets-head-pulse {
  0%, 12%, 24%, 100% { transform: translateY(0); }
  15%, 20%           { transform: translateY(-3px); }
}

.assets-station-icon {
  transform-origin: center;
  transform-box: fill-box;
  animation: assets-icon-bounce 8s ease-in-out infinite;
}

@keyframes assets-icon-bounce {
  0%, 12%, 24%, 100% { transform: translateY(0) scale(1); }
  15%                { transform: translateY(-6px) scale(1.15); }
}

.assets-package {
  animation: assets-package-travel 8s ease-in-out infinite;
}

@keyframes assets-package-travel {
  0%   { transform: translate(25px, 155px) scale(0.9); opacity: 0; }
  5%   { transform: translate(45px, 155px) scale(1); opacity: 1; }
  95%  { transform: translate(555px, 155px) scale(1.05); opacity: 1; }
  100% { transform: translate(580px, 155px) scale(0.9); opacity: 0; }
}

.assets-layer {
  opacity: 0;
  transform-origin: center;
  transform-box: fill-box;
}

.assets-layer-base {
  opacity: 1;
}

.assets-layer-discovery {
  animation: assets-layer-in-15 8s ease-in-out infinite;
}

@keyframes assets-layer-in-15 {
  0%, 13%    { opacity: 0; transform: scale(0.5); }
  16%        { opacity: 1; transform: scale(1.3); }
  19%, 100%  { opacity: 1; transform: scale(1); }
}

.assets-layer-blueprint {
  animation: assets-layer-in-30 8s ease-in-out infinite;
}

@keyframes assets-layer-in-30 {
  0%, 28%    { opacity: 0; transform: scale(0.5); }
  31%        { opacity: 1; transform: scale(1.3); }
  34%, 100%  { opacity: 1; transform: scale(1); }
}

.assets-layer-build {
  animation: assets-layer-in-45 8s ease-in-out infinite;
}

@keyframes assets-layer-in-45 {
  0%, 43%    { opacity: 0; transform: scale(0.5); }
  46%        { opacity: 1; transform: scale(1.3); }
  49%, 100%  { opacity: 1; transform: scale(1); }
}

.assets-layer-review {
  animation: assets-layer-in-60 8s ease-in-out infinite;
}

@keyframes assets-layer-in-60 {
  0%, 58%    { opacity: 0; transform: scale(0.5); }
  61%        { opacity: 1; transform: scale(1.3); }
  64%, 100%  { opacity: 1; transform: scale(1); }
}

.assets-layer-launch {
  animation: assets-layer-in-75 8s ease-in-out infinite;
}

@keyframes assets-layer-in-75 {
  0%, 73%    { opacity: 0; transform: scale(0.5); }
  76%        { opacity: 1; transform: scale(1.3); }
  79%, 100%  { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .assets-spinner img,
  .assets-stripe,
  .assets-wheel,
  .assets-station-head,
  .assets-station-icon,
  .assets-package,
  .assets-layer {
    animation: none !important;
    transform: none !important;
  }
  .assets-layer { opacity: 1; }
}

/* ── /application — canon-tier organism rows with live preview (warp 2666) ──
 *
 * Gate 2 of the canon-tier promotion contract (parent EPIC warp 2662,
 * ADR-181). Each .app-library-row pairs text describing the organism
 * with a live preview surface — an iframe of ai-frontend's
 * /dev/canon-preview/<name> route during the lift, or a Lit embed
 * (<gy-...>) once the primitive has retired its source-of-truth into
 * gyrum-ui. The build-time gate refuses rows that declare neither.
 *
 * Layout shape: text head on top, meta line, then the preview pane
 * filling the remaining row. The preview is sized via aspect-ratio so
 * it scales with column width without page-jank on load.
 */
.app-library-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.app-library-row {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.app-library-row:hover {
  border-color: var(--border-strong, var(--accent));
}

.app-library-row__head {
  font-size: var(--font-size-md);
  line-height: var(--line-height-snug, 1.4);
}

.app-library-row__name {
  font-weight: var(--font-weight-semibold);
  margin-right: var(--space-1);
}

.app-library-row__meta {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.app-library-row__preview {
  margin: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  width: 100%;
}

/* Promotion-in-progress panel. Until each canon-tier organism's Lit
 * version lands in gyrum-ui via Phase-4 promotion (warp 2278), the row
 * renders this compact status panel pointing at the tracking warp
 * ticket. After promotion lands, data/application-library.json gets a
 * lit_component_tag for the entry and the renderer swaps to the live
 * Lit embed. (warp 2688 — retreat from the warp 2666/2684/2686 iframe
 * arc that didn't work across origins.) */
.app-library-row__promo-pending {
  padding: var(--space-4);
  background: var(--accent-soft);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
  color: var(--text);
}

.app-library-row__promo-heading {
  display: block;
  color: var(--text);
  font-weight: var(--font-weight-semibold);
  margin-bottom: var(--space-1);
}

.app-library-row__promo-note {
  margin: 0;
  color: var(--text-muted);
}

.app-library-row__promo-note a {
  color: var(--accent);
  text-decoration: none;
}

.app-library-row__promo-note a:hover {
  text-decoration: underline;
}

/* Live Lit embed (post-promotion). Each Phase-4 promotion lands a
 * <gy-NAME> custom element vendored under vendor/components/; the row
 * mounts that element directly when data/application-library.json's
 * entry carries a lit_component_tag. */
.app-library-row__preview-lit {
  display: block;
  width: 100%;
  border: 0;
  background: transparent;
}
