/* site/system/components.css — layer 3 of 3, the brand-agnostic component library.
 *
 * VENDORED BY COPY from ../../ux-factory/system/components.css (the section band at lines 46 to 695,
 * as of this commit) and then trimmed to what six static pages actually need. THIS FILE DOES NOT
 * TRACK UX-FACTORY HEAD. It is a fork, deliberately: the baseline-collision rule says reuse in this
 * family is by copy, so ux-factory's open churn cannot reach a build that has already shipped.
 * Do not "sync" it. If ux-factory gains something worth having, copy that thing across by hand and
 * re-run the gate.
 *
 * What was dropped on the way in, and why:
 *   · the 218-line site header (mega-menu, dropdown panels, mobile drawer) — ux-factory site chrome
 *     for a 10-page marketing site. Replaced below by a plain sticky bar.
 *   · the four-column mega footer — replaced by the one this site's footer partial actually uses.
 *   · .amber / .teal / .ocean — colour helpers named after another client's primitives.
 *   · .brand-lockup — an SVG logotype slot. B4 forbids setting any company name as a logotype.
 *   · the font-weight !important override, which targeted ux-factory page classes.
 *   · everything from line 696 on: the areas strip, .lp-* landing pages, the ds-*, vd-* and fw-*
 *     component libraries, the command palette, the appearance dock, the inspect bubble.
 *     (Those three prefixes are separated by commas rather than by slashes on purpose. Written with
 *     a slash between them, the star and the slash form a comment terminator and close this block
 *     twelve lines early, which is how the box-sizing rule below went missing for three tickets.
 *     Do not put a star immediately before a slash anywhere in a CSS comment.)
 *
 * S1, and it is enforced: this file references CONTRACT TOKENS ONLY. No colour literal, no primitive
 * token, and no bare px on padding, margin, gap, border-radius or font-size outside the hairline
 * allow-list. Gate group 6 reddens on all four. Where ux-factory carried a one-off value (14px, 22px,
 * 10px, 12px) it was snapped to the nearest scale step rather than promoted to a new token: a scale
 * with eight steps and fourteen exceptions is not a scale.
 */

/* ---------- Base ---------- */
/* Vendored from ux-factory lines 18 to 44, OUTSIDE the section band the plan named. Without it the
   token layers are declared and never painted: --color-bg and --color-fg are bound correctly by the
   pack, nothing applies them to the document, and the page renders as default white with black text
   while every other check passes. Caught by rendering the page, which is why that step is in the
   validation ladder. */

/* THIS RULE WAS DEAD FROM #3 UNTIL #6, and nothing said so. The header comment above listed three
   vendored prefixes with slashes between them, and one of those slash-star pairs terminated that
   comment twelve lines early. The parser then discarded tokens looking for somewhere to resync, and
   this rule, the very next one in the file, went with them. So every element on the site was
   content-box: .container's `width: 100%` plus its two gutters overflowed its parent by 48px on
   every page, invisibly, because html and body clip overflow-x. Found on #6 by measuring the
   composer's textarea at a 360px viewport, where its right edge was being cut off. */
* { box-sizing: border-box; }

html {
  font-family: var(--font-body);
  font-size: var(--type-body);
  font-weight: var(--font-weight-ui);
  line-height: 1.5;
  color: var(--color-fg);
  background-color: var(--color-bg);
  scroll-behavior: smooth;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  margin: 0;
  padding: 0;
  min-height: 100vh;
}

a { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }

/* `overflow-x: hidden` on body makes it a scroll container, which silently unbinds every
   `position: sticky` descendant from the viewport, so the sticky header above would never stick.
   `clip` clips identically WITHOUT creating a scroll container; the `hidden` line stays as the
   pre-Safari-16 fallback. */
html, body { overflow-x: hidden; overflow-x: clip; }

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

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: var(--font-weight-ui);
  margin: 0;
  letter-spacing: 0.02em;
}

.display {
  font-family: var(--font-display);
  font-size: var(--type-display);
  line-height: 1.04;
  letter-spacing: -0.01em;
  font-weight: var(--font-weight-ui);
}

.h1 {
  font-family: var(--font-display);
  font-size: var(--type-h1);
  line-height: 1.06;
  letter-spacing: 0.01em;
  font-weight: var(--font-weight-ui);
}

.h2 {
  font-family: var(--font-display);
  font-size: var(--type-h2);
  line-height: 1.15;
  letter-spacing: 0.02em;
  font-weight: var(--font-weight-ui);
}

.h3 {
  font-family: var(--font-display);
  font-size: var(--type-h3);
  line-height: 1.25;
  letter-spacing: 0.02em;
  font-weight: var(--font-weight-ui);
}

p, .body {
  font-family: var(--font-body);
  font-size: var(--type-body);
  line-height: 1.6;
  font-weight: var(--font-weight-ui);
  margin: 0;
}

.lead {
  font-family: var(--font-body);
  font-size: var(--type-lead);
  line-height: 1.5;
  font-weight: var(--font-weight-ui);
  color: var(--color-fg);
  letter-spacing: 0.005em;
}

.caption {
  font-family: var(--font-body);
  font-size: var(--type-caption);
  line-height: 1.5;
  font-weight: var(--font-weight-ui);
  letter-spacing: 0.04em;
  color: var(--color-fg-muted);
  text-transform: uppercase;
}

.eyebrow {
  font-family: var(--font-body);
  font-size: var(--type-eyebrow);
  font-weight: var(--font-weight-ui);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
}

.muted  { color: var(--color-fg-muted); }
.accent { color: var(--color-accent); }

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

.container {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.section {
  padding: var(--spacing-4xl) 0;
}
/* padding-BLOCK, not the shorthand. Both pages carry `class="container section-sm"`, and the
   shorthand's `0` reset .container's inline gutter, so content touched the viewport edge at 360px
   and 768px. Gate group 6 now refuses the shorthand on any class co-applied with .container.
   .section keeps its shorthand: it is on no .container today, and the gate reads the markup rather
   than this comment, so the day it is, that rule is red too. */
.section-sm { padding-block: var(--spacing-3xl); }

.divider {
  height: 1px;
  background: var(--color-border);
  width: 100%;
}

.rule-thick {
  height: 2px;
  background: var(--color-border-strong);
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-sm);
  font-family: var(--font-display);
  font-weight: var(--font-weight-ui);
  font-size: var(--type-caption);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  padding: var(--spacing-md) var(--spacing-lg);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  /* press-squish: colours on the base curve, transform releases through the bounce spring.
     :active overrides to the fast curve, so the press is quick and the release rebounds. */
  transition: background-color var(--motion-base) var(--motion-ease), color var(--motion-base) var(--motion-ease), transform var(--motion-bounce) var(--motion-ease-bounce);
  text-decoration: none;
  line-height: 1;
}
.btn:active { transform: scale(0.96); transition-duration: var(--motion-base), var(--motion-base), var(--motion-fast); }

.btn-primary {
  background: var(--color-accent);
  color: var(--color-accent-fg);
}
.btn-primary:hover  { background: var(--color-accent-hover); color: var(--color-accent-fg); }
.btn-primary:active { background: var(--color-accent-active); color: var(--color-accent-fg); }

.btn-secondary {
  background: transparent;
  color: var(--color-fg);
  border: 1px solid var(--color-border-strong);
}
.btn-secondary:hover { background: var(--color-bg-inverse); color: var(--color-fg-on-inverse-strong); }

.btn-ghost {
  background: transparent;
  color: var(--color-fg);
  padding: var(--spacing-sm) 0;
  border-bottom: 1px solid var(--color-border-strong);
  letter-spacing: 0.14em;
}
.btn-ghost:hover { color: var(--color-accent); border-bottom-color: var(--color-accent); }

/* On the dark ground, keep secondary and ghost CTAs readable. */
.on-dark .btn-secondary {
  color: var(--color-fg-on-inverse-strong);
  border-color: var(--color-on-dark-border);
}
.on-dark .btn-secondary:hover {
  background: var(--color-fg-on-inverse-strong);
  color: var(--color-fg);
  border-color: var(--color-fg-on-inverse-strong);
}
.on-dark .btn-ghost {
  color: var(--color-fg-on-inverse-strong);
  border-bottom-color: var(--color-on-dark-border);
}
.on-dark .btn-ghost:hover {
  color: var(--color-accent-on-inverse);
  border-bottom-color: var(--color-accent-on-inverse);
}

.btn-arrow::after {
  content: "→";
  display: inline-block;
  transform: translateX(0);
  transition: transform var(--motion-base) var(--motion-ease-spring);
}
.btn-arrow:hover::after { transform: translateX(4px); }

/* ---------- Header ---------- */
/* A plain sticky bar, not ux-factory's mega-menu. Six pages linking to each other need a brand, a
   nav and a rule under it; anything more is chrome nobody asked for. */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
}

.site-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-lg);
  padding: var(--spacing-md) 0;
}

.site-brand {
  font-family: var(--font-display);
  font-size: var(--type-h3);
  font-weight: var(--font-weight-ui);
  color: var(--color-fg);
  text-decoration: none;
  letter-spacing: 0.01em;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--spacing-lg);
  flex-wrap: wrap;
}
.site-nav a {
  font-family: var(--font-body);
  font-size: var(--type-caption);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
  text-decoration: none;
  transition: color var(--motion-fast) var(--motion-ease);
}
.site-nav a:hover,
.site-nav a[aria-current="page"] { color: var(--color-fg); }

/* ---------- Footer ---------- */
/* Styles the partial in docs/footer.html. The B3 disclaimer is the one element on this site that is
   never allowed to be quiet, so it takes body size and full-strength inverse foreground, and the AI
   disclosure sits under it a step down. */

.site-footer {
  background: var(--color-bg-inverse);
  color: var(--color-fg-on-inverse);
  padding: var(--spacing-3xl) 0 var(--spacing-xl);
  margin-top: var(--spacing-4xl);
}

.footer-legal {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
  max-width: 65ch;
}
.footer-legal p {
  font-family: var(--font-body);
  font-size: var(--type-caption);
  line-height: 1.6;
  color: var(--color-fg-on-inverse);
}
.footer-disclaimer {
  color: var(--color-fg-on-inverse-strong);
}
.footer-legal a {
  color: var(--color-accent-on-inverse);
  transition: color var(--motion-fast) var(--motion-ease);
}
.footer-legal a:hover { color: var(--color-fg-on-inverse-strong); }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--spacing-md);
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-inverse-line);
  font-family: var(--font-body);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.02em;
  color: var(--color-fg-on-inverse-muted);
}
@media (max-width: 600px) {
  .footer-bottom { flex-direction: column; gap: var(--spacing-sm); align-items: flex-start; }
}

/* ---------- Cards ---------- */

.card {
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--spacing-xl);
  transition: transform var(--motion-base) var(--motion-ease-spring), box-shadow var(--motion-base) var(--motion-ease-spring), border-color var(--motion-base) var(--motion-ease);
}
.card-link { display: block; cursor: pointer; }
.card-link:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: var(--color-border-strong);
}

/* ---------- Decision card (organism) ---------- */
/* The atom of decision-based content: Decision / Because / Rejected / Would measure.
 * Content budget of 40 words across the four values (DESIGN.md C3). Ships as a <dl> so the fields
 * read as label/value pairs to a screen reader and to an agent alike (A2). Promoted per S5: #10's
 * decisions page and every prototype surface use it. */
.decision-card {
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
  padding: var(--spacing-lg);
}
.decision-card-title {
  font-family: var(--font-display);
  font-size: var(--type-h3);
  margin: 0 0 var(--spacing-md);
}
.decision-card-fields { margin: 0; display: grid; gap: var(--spacing-sm); }
.dc-field {
  display: grid;
  grid-template-columns: 8.5rem 1fr;
  gap: var(--spacing-md);
  align-items: baseline;
}
.dc-field dt {
  font-family: var(--font-display);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
}
.dc-field dd { margin: 0; line-height: 1.55; }
@media (max-width: 520px) {
  .dc-field { grid-template-columns: 1fr; gap: var(--spacing-xs); }
}

/* ---------- Page furniture (organisms) ---------- */
/* Promoted from site/system/handover.css on #6, under S5. C1 binds EVERY prototype, so the
 * "Built because" line is not one page's furniture: index.html carries it from this ticket on and
 * #7 to #10 inherit it without copying a rule. The title beside it is the same element on both.
 *
 * The line is set in the mono face and the muted foreground on purpose. It is verbatim material
 * quoted from a source record (C1, C10), so it reads as a citation rather than as the page's own
 * first sentence, and gate group 14 clause 2 binds the quoted sentence to its block in
 * docs/sources/fin-escalation-citations.md.
 *
 * Page CSS still links last, so a page wanting a different measure sets it there. */

.page-built-because {
  font-family: var(--font-mono);
  font-size: var(--type-caption);
  line-height: 1.6;
  color: var(--color-fg-muted);
  border-left: 2px solid var(--color-accent);
  padding-left: var(--spacing-md);
  margin-bottom: var(--spacing-xl);
  max-width: 60ch;
}

.page-title {
  max-width: 22ch;
  margin-bottom: var(--spacing-xl);
}

/* Promoted from site/system/handover.css and site/system/recovery.css on #8, under S5, once the two
   declarations were byte-identical. Every page carrying a replay states the claim its scenario is
   making, so the bet is not one page's furniture either.

   It reads a step up from body and a step below the context. It is not a callout box: a claim in a
   box is a claim asking to be believed. */
.page-bet {
  font-size: var(--type-body);
  line-height: 1.6;
  color: var(--color-fg);
  border-top: 1px solid var(--color-border);
  padding-top: var(--spacing-md);
}

/* The block that holds a page's framing prose, above whatever the page is actually about. Promoted
   on #6 under S5 once index.html declared a second copy: the structure is the same on both pages and
   only the rhythm differs, so the structure lives here and each page CSS sets its own gap and
   measure after it. */
.page-frame {
  display: grid;
  gap: var(--spacing-md);
  max-width: 65ch;
  margin-bottom: var(--spacing-2xl);
}

/* The frame note (d-029). Every page mounting a replay carries one, above the replay section, saying
 * what the scenario was written to show. It is a components-layer rule because two pages spend it,
 * which is S5, and it is glob-wide rather than scoped to the page that introduced it, because a rule
 * with one producer is the A5 defect this build names by name.
 *
 * IT MUST NOT LOOK LIKE A WARNING. This is the frame stating what the page is, not an apology for
 * it, so it takes the caption size and the muted foreground and a quiet TOP rule. The rule is
 * deliberately not the honesty label's left accent bar: two elements on one page saying related
 * things in the same visual language read as one repeated element rather than as two claims. */
.page-designed-note {
  font-size: var(--type-caption);
  line-height: 1.6;
  color: var(--color-fg-muted);
  border-top: 1px solid var(--color-border);
  padding-top: var(--spacing-sm);
  max-width: 62ch;
}

/* ---------- Conversation replay (organism) ---------- */
/* The one net-new component in this build. Lives in the components layer under S5 rather than in
 * page CSS because two pages use it: handover.html today, recovery.html in #7, which inherits this
 * band without changing a rule in it.
 *
 * THE HONESTY LABEL SITS BESIDE THE GLASS, NOT UNDER IT. It is plain HTML on first paint (d-014),
 * so it is styled as a peer of the transcript panel rather than as a caption hanging off one, and
 * nothing here may set it to display:none: gate group 10 rejects a hidden label and the player
 * refuses to run without it.
 *
 * The escalation card is a receipt. The hedge under the reason row is styled like the citation list
 * and never like a warning, because d-012 names the real risk of the whole grounding decision as
 * that sentence reading like hesitancy rather than like attribution.
 *
 * S1, and it is enforced: contract tokens only. Geometry (the glass height, the label column) is a
 * layout decision and takes real values, which is exactly the boundary DESIGN.md's "How S1 is
 * counted" publishes.
 */

/* The measure moved up from both page CSS files on #8 under S5: handover.css and recovery.css set
   the same 78ch, so it is the organism's measure rather than either page's. Page CSS still links
   last, so a page wanting a different one sets it there. */
.replay {
  display: grid;
  gap: var(--spacing-lg);
  max-width: 78ch;
}

/* THE hidden ATTRIBUTE HAS TO WIN, and without this rule it does not. The UA stylesheet sets
   [hidden] { display: none }, and any author rule setting display on the same element beats it on
   specificity: .replay-error below sets display: grid, so the error region rendered in full on a
   page that had no error. Caught by looking at the page, not by reading element.hidden, which was
   true the whole time. Scoped to this band rather than made global, because it is this band's own
   three regions that toggle. */
.replay [hidden] { display: none; }

.replay-label {
  font-family: var(--font-body);
  font-size: var(--type-caption);
  line-height: 1.5;
  letter-spacing: 0.04em;
  color: var(--color-fg);
  border-left: 3px solid var(--color-accent);
  padding: var(--spacing-sm) var(--spacing-md);
  background: var(--color-bg-surface);
}

.replay-transport {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  flex-wrap: wrap;
}
/* A3: a control that is visible and says it is not ready is honest. One that looks operable a
   moment before its script arrives is a ghost. The transport ships disabled in the markup. */
.replay-transport .btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

.replay-glass {
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--spacing-lg);
  max-height: 460px;
  overflow-y: auto;
}

.replay-transcript {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--spacing-md);
}

.replay-turn {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--spacing-md);
  max-width: 52ch;
}
.replay-turn-agent {
  border-left: 3px solid var(--color-accent);
  margin-left: auto;
}
.replay-turn-customer {
  border-left: 3px solid var(--color-border-strong);
}
/* Reserved for #6, which renders the receiving human's own turns after the take-over. */
.replay-turn-human {
  border-left: 3px solid var(--color-accent-secondary);
  margin-left: auto;
}

.replay-speaker {
  font-family: var(--font-body);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
  margin-bottom: var(--spacing-xs);
}
.replay-text {
  font-size: var(--type-body);
  line-height: 1.6;
}

.replay-status {
  font-family: var(--font-body);
  font-size: var(--type-caption);
  color: var(--color-fg-muted);
}

/* ---- The escalation card ---- */

.replay-escalation {
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
  padding: var(--spacing-lg);
}
.replay-escalation-title { margin-bottom: var(--spacing-lg); }

.replay-fields {
  margin: 0 0 var(--spacing-xl);
  display: grid;
  gap: var(--spacing-lg);
}
.replay-field {
  display: grid;
  grid-template-columns: 9.5rem 1fr;
  gap: var(--spacing-md);
  align-items: baseline;
}
.replay-field dt {
  font-family: var(--font-display);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
}
.replay-field dd { margin: 0; }
.replay-value { line-height: 1.55; }
.replay-field-source,
.replay-hedge-source,
.replay-citation-id {
  font-family: var(--font-mono);
  font-size: var(--type-caption);
  color: var(--color-fg-muted);
  margin-top: var(--spacing-xs);
}

/* Attribution, not a caveat. Same register as the citation list below it, which is what it is. */
.replay-hedge {
  margin-top: var(--spacing-md);
  padding-left: var(--spacing-md);
  border-left: 1px solid var(--color-border);
}
.replay-hedge-label {
  font-family: var(--font-display);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
}
.replay-hedge-quote,
.replay-citation-quote {
  margin: var(--spacing-xs) 0 0;
  font-size: var(--type-body);
  line-height: 1.6;
  white-space: pre-line;
}

.replay-sources-title {
  font-family: var(--font-display);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
  margin-bottom: var(--spacing-md);
}
.replay-citations {
  margin: 0;
  padding-left: var(--spacing-lg);
  display: grid;
  gap: var(--spacing-md);
}
.replay-citation-url {
  display: inline-block;
  margin-top: var(--spacing-xs);
  font-family: var(--font-mono);
  font-size: var(--type-caption);
  color: var(--color-accent);
  text-decoration: underline;
  overflow-wrap: anywhere;
}

/* ---- The handover briefing (organism) ---- */
/* What the escalation card is a receipt FOR: the five contract fields, handed over at the beat.
 * Lives in the components layer under S5 because #7's recovery page mounts the same organism from
 * the same module, and inherits this band without changing a rule in it.
 *
 * IT HAS TO READ AS DISTINCT FROM THE ESCALATION CARD ABOVE IT, and not by colour alone. The card is
 * evidence about what Fin did; the briefing is the proposal about what it should hand over. So the
 * briefing takes the page ground rather than the raised surface, and a heavier left rule in the
 * quiet second accent rather than the primary one. Two different grounds and two different rules,
 * with the headings doing the rest.
 *
 * The provenance line under each value is set in the citation register, mono and muted, exactly like
 * .replay-field-source. That is d-015 made visible: a designed field says so on the card, and it
 * says so in the same voice a citation does rather than in a warning's voice, because the honest
 * half of a briefing is not an apology for the other half. */

.briefing {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent-secondary);
  border-radius: var(--radius-md);
  padding: var(--spacing-lg);
}

.briefing-title { margin-bottom: var(--spacing-sm); }

.briefing-intro {
  font-size: var(--type-caption);
  line-height: 1.6;
  color: var(--color-fg-muted);
  margin-bottom: var(--spacing-lg);
  max-width: 60ch;
}

.briefing-fields {
  margin: 0;
  display: grid;
  gap: var(--spacing-lg);
}

.briefing-field {
  display: grid;
  grid-template-columns: 9.5rem 1fr;
  gap: var(--spacing-md);
  align-items: baseline;
}
.briefing-field dt {
  font-family: var(--font-display);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
}
.briefing-field dd { margin: 0; }

.briefing-value { line-height: 1.55; }

.briefing-provenance {
  font-family: var(--font-mono);
  font-size: var(--type-caption);
  color: var(--color-fg-muted);
  margin-top: var(--spacing-xs);
}
/* The designed marker is the same register as a citation line, and it is not dimmer than one. A
   field that says "designed" is telling the truth about itself, which is the point of the card. */
.briefing-provenance-designed { font-family: var(--font-body); }

/* ---- The take-over (organism) ---- */
/* The visitor takes the receiving human's seat. #7 mounts the same three blocks from the same
   module, so this band sits in the components layer under S5 alongside the replay it lives inside.
 *
 * THE PROVENANCE MARKER IS TEXT FIRST (d-020). Its sentence changes, then its data-provenance
 * attribute, and only then does anything here change colour: SC 1.4.1 forbids colour as the only
 * carrier, and a reader who cannot see the copper still reads a different sentence. Everything below
 * keys off the ATTRIBUTE, so the CSS cannot be the thing that says who is speaking. */

.provenance-marker {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  font-family: var(--font-body);
  font-size: var(--type-caption);
  line-height: 1.5;
  letter-spacing: 0.04em;
  color: var(--color-fg-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--spacing-sm) var(--spacing-md);
}
.provenance-marker::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-border);
  flex-shrink: 0;
}
.provenance-marker[data-provenance="human"] {
  color: var(--color-fg);
  border-color: var(--color-accent-secondary);
  border-left-width: 3px;
}
.provenance-marker[data-provenance="human"]::before {
  background: var(--color-accent-secondary);
}

/* The tracks STRETCH and the button opts out, rather than the other way round. `justify-items: start`
   here sized the composer to its own max-content, which at a 360px viewport put the textarea's right
   edge 34px past the column: not a page scrollbar, because html/body clip overflow-x, so the box was
   simply cut off with nothing to show a reader there was more of it. Caught by measuring the element
   at 360px rather than by looking at the page, which is where it reads as a snug fit. */
.takeover {
  display: grid;
  gap: var(--spacing-md);
}
.takeover > .btn { justify-self: start; }

/* minmax(0, 1fr) rather than the implicit `auto` track, and it is load-bearing at small viewports.
   A <textarea> carries an intrinsic width from its default `cols`, which feeds the auto track's
   max-content; `width: 100%` on the textarea then resolves against that widened track rather than
   against the column, so the box sized itself to about 394px inside a 312px column and its right
   edge was clipped by html/body's overflow-x: clip with nothing telling the reader it had been.
   Measured at 360px, where it reads as a snug fit. */
.composer {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--spacing-sm);
  max-width: 52ch;
}

.composer-label {
  font-family: var(--font-body);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
}

.composer-input {
  font-family: var(--font-body);
  font-size: var(--type-body);
  line-height: 1.6;
  color: var(--color-fg);
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  padding: var(--spacing-md);
  width: 100%;
  resize: vertical;
}
/* A4. The UA focus ring is never removed; it is replaced by a heavier one in the accent, so the
   composer is the most obviously focused thing on the page at the moment it is meant to be. No
   `outline: none` appears anywhere in this file. */
.composer-input:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
/* A3, and the same reasoning .replay-transport .btn[disabled] carries: a disabled control has to
   LOOK disabled, or it is a control that silently swallows a reader's typing. */
.composer-input[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  background: var(--color-bg);
}

.composer-actions {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  flex-wrap: wrap;
}

.takeover .btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* The error is the one place on this surface that is allowed to sound like a correction, because it
   is answering a thing the reader just did. Border, not a fill: a red panel for an empty textarea is
   a scale mismatch. */
.composer-error {
  font-size: var(--type-caption);
  line-height: 1.5;
  color: var(--color-fg);
  border-left: 3px solid var(--color-border-strong);
  padding-left: var(--spacing-md);
}

.composer-status {
  font-family: var(--font-body);
  font-size: var(--type-caption);
  line-height: 1.5;
  color: var(--color-fg-muted);
}

@media (max-width: 700px) {
  .briefing-field { grid-template-columns: 1fr; gap: var(--spacing-xs); }
}

/* ---- The error state ---- */
/* It names WHICH of the three failure modes fired, because they have three different fixes and one
   generic "could not load" sends a developer into the wrong file. */
.replay-error {
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  padding: var(--spacing-lg);
  display: grid;
  gap: var(--spacing-sm);
}
.replay-error-title { font-family: var(--font-display); }
.replay-error-list {
  margin: 0;
  padding-left: var(--spacing-lg);
  font-family: var(--font-mono);
  font-size: var(--type-caption);
  color: var(--color-fg-muted);
}

/* Turn entry. Gated on no-preference alone, so at rest and under reduced motion the turn is fully
   visible with nothing animating, which is what the double gate above the keyframes exists for.
   The @supports half of that gate tests animation-timeline, a scroll-driven feature this entry does
   not use, so requiring it here would switch a correct animation off on Firefox for an unrelated
   reason. Recorded as a named deviation in the implementation report. */
@media (prefers-reduced-motion: no-preference) {
  .replay-turn {
    animation: replay-turn-in var(--motion-slow) var(--motion-ease-settle) both;
  }
}
@keyframes replay-turn-in {
  from { opacity: 0; translate: 0 calc(var(--motion-rise) / 2); }
  to   { opacity: 1; translate: 0 0; }
}

@media (max-width: 700px) {
  .replay-field { grid-template-columns: 1fr; gap: var(--spacing-xs); }
  .replay-turn { max-width: none; }
}

/* ---------- Disc motif ---------- */
/* A filled circle in the accent: status dots, live markers, list bullets. */

.disc-motif {
  background: var(--color-accent);
  border-radius: 50%;
  flex-shrink: 0;
}
.disc-motif.quiet { background: var(--color-accent-secondary); }

/* ---------- Utilities ---------- */

.grid   { display: grid; gap: var(--spacing-xl); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }

@media (max-width: 900px) {
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
}

.flex             { display: flex; }
.flex-col         { display: flex; flex-direction: column; }
.items-center     { align-items: center; }
.justify-between  { justify-content: space-between; }
.gap-sm { gap: var(--spacing-sm); }
.gap-md { gap: var(--spacing-md); }
.gap-lg { gap: var(--spacing-lg); }
.gap-xl { gap: var(--spacing-xl); }

.mb-sm  { margin-bottom: var(--spacing-sm); }
.mb-md  { margin-bottom: var(--spacing-md); }
.mb-lg  { margin-bottom: var(--spacing-lg); }
.mb-xl  { margin-bottom: var(--spacing-xl); }
.mb-2xl { margin-bottom: var(--spacing-2xl); }
.mb-3xl { margin-bottom: var(--spacing-3xl); }
.mt-xl  { margin-top: var(--spacing-xl); }
.mt-2xl { margin-top: var(--spacing-2xl); }

.text-center  { text-align: center; }
.text-balance { text-wrap: balance; }
.text-pretty  { text-wrap: pretty; }

.max-prose { max-width: 65ch; }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

::selection { background: var(--color-accent); color: var(--color-accent-fg); }

.section-label {
  display: flex;
  align-items: baseline;
  gap: var(--spacing-md);
  font-family: var(--font-body);
  font-size: var(--type-eyebrow);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-fg-muted);
  margin-bottom: var(--spacing-2xl);
}
.section-label .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--type-caption);
  color: var(--color-fg);
}
.section-label .line {
  flex: 1;
  height: 1px;
  background: var(--color-border);
  margin-left: var(--spacing-md);
}

/* Scroll-driven reveals for section labels and opt-in card grids.
   Double-gated on @supports AND no-preference, so outside those the content is fully visible at
   rest: Firefox and reduced-motion readers see the finished state and nothing animates.
   card-rise animates `translate` (an independent property) rather than `transform`, so a revealed
   card's fill:both does not override .card-link:hover's transform lift. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .section-label .num {
      animation: label-num var(--motion-slow) var(--motion-ease-spring) both;
      animation-timeline: view();
      animation-range: entry 0% cover 18%;
    }
    .section-label .line {
      transform-origin: left center;
      animation: label-line var(--motion-slow) var(--motion-ease-settle) both;
      animation-timeline: view();
      animation-range: entry 0% cover 22%;
    }
    /* Opt-in staggered grid reveal: add class="grid grid-3 stagger". */
    .stagger > * {
      animation: card-rise var(--motion-slow) var(--motion-ease-spring) both;
      animation-timeline: view();
      animation-range: entry 2% cover 26%;
    }
    .stagger > :nth-child(3n+2) { animation-range: entry 6% cover 30%; }
    .stagger > :nth-child(3n+3) { animation-range: entry 10% cover 34%; }
  }
}
@keyframes label-num  { from { opacity: 0; transform: translateY(calc(var(--motion-rise) / 2)); } to { opacity: 1; transform: none; } }
@keyframes label-line { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes card-rise  { from { opacity: 0; translate: 0 var(--motion-rise); } to { opacity: 1; translate: 0 0; } }
