/*
 * Components. Every value comes from tokens.css — tests/Design/TokenUsageTest.php
 * fails on any hex literal here.
 */

.icon {
  width: 1em;
  height: 1em;
  flex: none;
  vertical-align: -0.125em;
}

/* Badges — three families, three visual treatments (§16.4) */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.125rem var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1.5;
  white-space: nowrap;
}

/* Provenance: neutral ramp + glyph. No state color, ever. */
.badge--prov-1 { background: var(--tint-neutral); color: var(--text); }
.badge--prov-2 { background: var(--tint-neutral); color: var(--text-secondary); }
.badge--prov-3 { background: transparent; border-color: var(--border-strong); color: var(--text-muted); }

/* Pricing basis: violet for MSRP, neutral for street, outline for unpriced. */
.badge--msrp { background: var(--tint-violet); color: var(--accent-violet-text); border-color: var(--border-violet); }
.badge--street { background: var(--tint-neutral); color: var(--text-secondary); }
.badge--unpriced { background: transparent; border-color: var(--border-strong); color: var(--text-muted); }

/* Operational state: the only green/amber/red in the system. */
.badge--success { background: var(--tint-success); color: var(--state-success); }
.badge--warn { background: var(--tint-warn); color: var(--state-warn); }
.badge--error { background: var(--tint-error); color: var(--state-error-text); }
.badge--neutral { background: var(--tint-neutral); color: var(--text-secondary); }

/* Buttons (§16.5) */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.5rem var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: 1.4;
  text-decoration: none;
  cursor: pointer;
  transition: all var(--duration) var(--ease);
}

.btn--sm { padding: 0.3125rem var(--space-3); font-size: var(--text-xs); }

.btn--primary {
  background: var(--accent-violet-strong);
  color: var(--on-accent);
  box-shadow: var(--shadow-panel);
}
.btn--primary:hover { transform: translateY(-1px); box-shadow: var(--shadow-lift); }

.btn--secondary { background: var(--surface-raised); border-color: var(--border-strong); }
.btn--secondary:hover { border-color: var(--border-violet); background: var(--surface); }

.btn--destructive { background: var(--state-error); color: var(--on-accent); }
.btn--destructive:hover { transform: translateY(-1px); box-shadow: var(--shadow-lift); }

.btn--ghost { color: var(--text-secondary); }
.btn--ghost:hover { background: var(--surface-raised); color: var(--text); }

.btn:focus-visible {
  outline: 2px solid var(--accent-violet-text);
  outline-offset: 2px;
  box-shadow: var(--focus-ring);
}

.btn:disabled,
.btn.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* Form controls (§16.5) */
.field { display: flex; flex-direction: column; gap: var(--space-2); }

.field__label {
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.field__required { color: var(--state-error-text); margin-left: var(--space-1); }

.control {
  width: 100%;
  padding: 0.5rem var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-inset);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  transition: all var(--duration) var(--ease);
}

.control--mono { font-family: var(--font-mono); }
.control:hover { border-color: var(--border-violet); }

.control:focus {
  outline: 2px solid var(--accent-violet-text);
  outline-offset: 0;
  border-color: var(--border-violet);
  box-shadow: var(--focus-ring);
}

.control:disabled { opacity: 0.5; cursor: not-allowed; }

.control__wrap { position: relative; display: flex; align-items: center; }
.control__icon { position: absolute; left: var(--space-3); color: var(--text-muted); }
.control--search { padding-left: var(--space-8); }
.control__suffix {
  position: absolute;
  right: var(--space-3);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.field__help { margin: 0; color: var(--text-secondary); font-size: var(--text-xs); max-width: 75ch; }
.field__error {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--state-error-text);
  font-size: var(--text-xs);
}
.field--error .control { border-color: var(--state-error); }

.field__checkbox { display: flex; align-items: center; gap: var(--space-2); color: var(--text); font-size: var(--text-base); }

/* Panels (§16.4) */
.panel {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-panel);
  transition: all var(--duration) var(--ease);
}

@supports (backdrop-filter: blur(6px)) {
  .panel { backdrop-filter: blur(6px); }
}

.panel__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--panel-padding) var(--panel-padding) 0;
}

.panel__title {
  margin: 0;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: var(--leading-tight);
}

.panel__description { margin: var(--space-1) 0 0; color: var(--text-secondary); font-size: var(--text-sm); }
.panel__actions { display: flex; gap: var(--space-2); flex: none; }
.panel__body { padding: var(--space-4) var(--panel-padding) var(--panel-padding); }

.panel--interactive:hover {
  border-color: var(--border-violet);
  box-shadow: var(--glow-violet);
  transform: translateY(-2px);
}

/* Reserved by §16.4: comparison summary, lifecycle crossover, engine selection summary. */
.panel--edge { position: relative; overflow: hidden; }
.panel--edge::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, var(--accent-violet), var(--accent-cyan));
}

.panel--grid {
  background-image:
    linear-gradient(var(--border) 1px, transparent 1px),
    linear-gradient(90deg, var(--border) 1px, transparent 1px);
  background-size: 32px 32px;
  -webkit-mask-image: radial-gradient(120% 90% at 50% 0%, var(--mask-fade), transparent 70%);
  mask-image: radial-gradient(120% 90% at 50% 0%, var(--mask-fade), transparent 70%);
}

.panel__toggle {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.panel__toggle:focus-visible { outline: 2px solid var(--accent-violet-text); outline-offset: 4px; }
.panel__chevron { color: var(--text-muted); transition: transform var(--duration) var(--ease); }
.panel.is-collapsed .panel__chevron { transform: rotate(-90deg); }
.panel.is-collapsed .panel__body { display: none; }

/* KPI cards (§16.3) — the comparison summary, not a dashboard */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-4);
}

.kpi {
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  /* Makes 100cqi below the width of this card's CONTENT box, so the figure
     is sized against the space it actually has rather than the viewport. */
  container-type: inline-size;
}

.kpi__label {
  margin: 0 0 var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.kpi__value {
  margin: 0;
  color: var(--text);
  font-family: var(--font-mono);
  /* Shrink to fit, never clip. --kpi-chars is the character count the card
     publishes (see Capex\View\Kpi::card); 0.62em is a safe upper bound on the
     advance width of a monospace digit, so chars x 0.62em is the width the
     figure needs at a given size, and inverting it gives the size that fits
     100cqi. --text-2xl stays the ceiling: this shrinks a long figure, it does
     not shrink every figure. The fallback of 12 matches a typical USD total,
     so a card rendered without the property is sized sanely rather than huge. */
  font-size: min(var(--text-2xl), calc(100cqi / (var(--kpi-chars, 12) * 0.62)));
  font-weight: 500;
  line-height: var(--leading-tight);
  /* The belt to that shrink's braces. If a figure is still too wide — a font
     with wider digits than the estimate, a card below the grid floor — it
     breaks onto a second line. Wrapping is ugly; a total silently missing its
     last digit is wrong, and §6's rule is that a figure is either right or
     visibly absent. */
  overflow-wrap: anywhere;
}

.kpi__unit { margin-left: var(--space-2); color: var(--text-muted); font-size: var(--text-sm); }
.kpi__delta { margin: var(--space-2) 0 0; font-size: var(--text-sm); font-weight: 500; }
.kpi__delta--success { color: var(--state-success); }
.kpi__delta--warn { color: var(--state-warn); }
.kpi__delta--error { color: var(--state-error-text); }
.kpi__delta--neutral { color: var(--text-secondary); }
.kpi__note { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: var(--text-xs); }

/* The KPI grid (2026-09-12): one row per engine, one column per figure, so a
   figure sits directly above the next engine's. Built on .table; these rules
   only stack a figure over its note, and a short name over the full model name. */
.kpi-grid .table__td { vertical-align: top; }
.kpi-grid__name { display: block; color: var(--text); font-weight: 600; }
.kpi-grid__model { display: block; margin-top: var(--space-1); color: var(--text-muted); font-size: var(--text-xs); }
.kpi-grid__value { display: block; color: var(--text); font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--text-lg); white-space: nowrap; }
.kpi-grid__note { display: block; margin-top: var(--space-1); color: var(--text-muted); font-size: var(--text-xs); max-width: 22rem; margin-left: auto; }

/* Operator clean-up §5.1: the answer sentence, first in the results and first
   under the report's title. Body colour and a step up in size; tokens only. */
.answer {
  margin: 0 0 var(--space-4);
  max-width: 75ch;
  color: var(--text);
  font-size: var(--text-lg);
  line-height: var(--leading-normal);
}

/* The report's stream grid (2026-09-12): year one over escalated lifetime in
   every cell, eight columns whatever the engine count. Dense and small so the
   eight fit the 7.4in a Letter sheet leaves; each figure on its own line,
   never wrapped mid-number. */
/* Measured live 2026-09-12 on the 708px content box of the screen sheet (8.5in
   less 2 x 0.55in): at 12px/8px padding with nowrap headings the eight columns
   ran 860px; at 11px/4px-6px with wrapping headings they run exactly 708. */
.stream-grid .table__th,
.stream-grid .table__td { padding: 4px 6px; font-size: 11px; }
.stream-grid .table__th { white-space: normal; letter-spacing: 0; }
.stream-grid .kpi-grid__name { font-size: 12px; }
.stream-grid__year-one,
.stream-grid__lifetime { display: block; white-space: nowrap; }
.stream-grid__lifetime { color: var(--text-secondary); }

/* The report's per-engine normalized tables (2026-09-12): the caption is the
   engine's full name and reads as a heading, larger and heavier than the table
   body, so a reader scanning the section sees which engine each table is. */
.normalized-table .table__caption { font-size: var(--text-base); font-weight: 600; color: var(--text); padding: var(--space-3) 0 var(--space-2); }
.normalized-table + .normalized-table { margin-top: var(--space-3); }

/* Empty state */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-12) var(--space-6);
  text-align: center;
}

.empty__icon { width: 1.75rem; height: 1.75rem; color: var(--text-muted); }
.empty__title { margin: 0; color: var(--text); font-size: var(--text-base); font-weight: 600; }
.empty__line { margin: 0; max-width: 46ch; color: var(--text-secondary); font-size: var(--text-sm); }
.empty__action { margin-top: var(--space-2); }

/* Tables (§16.4) */
.table-wrap { overflow-x: auto; }

.table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-ui);
  font-size: var(--text-sm);
}

.table__caption {
  padding-bottom: var(--space-3);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  text-align: left;
}

.table__th {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-strong);
  background: var(--surface-raised);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
}

.table__td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  color: var(--text);
  vertical-align: middle;
}

.table--dense .table__th,
.table--dense .table__td { padding: var(--space-2) var(--space-3); }

.table__th.is-right,
.table__td.is-right { text-align: right; }
.table__td.is-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.table tbody tr { transition: background var(--duration) var(--ease); }
.table tbody tr:hover { background: var(--surface-raised); }
.table tbody tr.is-selected { background: var(--tint-violet); box-shadow: inset 2px 0 0 var(--accent-violet); }

/* Editable grid (the maintenance editor). Dense by design: the backlog is
   forty rows of numbers, and the whole family has to be visible while typing. */
.grid-form { display: flex; flex-direction: column; gap: var(--space-3); }

.control--grid {
  width: 100%;
  padding: 0.3rem var(--space-2);
  font-size: var(--text-sm);
}

.control--grid.is-invalid { border-color: var(--state-error); }

.grid-form__changed {
  margin-right: auto;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-family: var(--font-mono);
}

/* The row tint is a second signal, never the only one: every invalid cell
   also carries its message in words. */
tr.is-invalid > .table__td { background: var(--surface-inset); }

/* Comparison screen (Phase C1). Tokens only — a hex literal here fails
   TokenUsageTest. --font-size-sm and --border-subtle, named in the plan,
   are not defined under those names in tokens.css — --text-sm and --border
   are the tokens that actually exist and carry the same role. */

.scenario-form {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  align-items: start;
}

/* Scenario form notes (spec 2026-10-01): numbered markers after the basic
   fields' labels, and the messages themselves in one block at the bottom.
   The number links a marker to its line; cyan means a note, red a warning. */
.note-marker {
  display: inline-flex;
  align-items: center;
  margin-left: var(--space-2);
  padding: 0 var(--space-2);
  border: 1px solid;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0;
  text-transform: none;
  vertical-align: middle;
  white-space: nowrap;
}
.note-marker--info { color: var(--accent-cyan); background: var(--tint-cyan); border-color: var(--accent-cyan); }
.note-marker--warning { color: var(--state-error-text); background: var(--tint-error); border-color: var(--state-error); }

.field-notes {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}
.field-notes__title {
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}
.field-notes__line {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0;
  max-width: 90ch;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}
.field-notes__line .note-marker { margin-left: 0; flex-shrink: 0; }
.field-notes__line--warning { color: var(--state-error-text); }

.scenario-form__group {
  grid-column: 1 / -1;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin: 0;
}

.scenario-form__presets {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

/* The action row: Compare first, Print report beside it once there is a
   comparison to print. Spans the grid like the presets row above. */
.scenario-form__actions {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

/* Advanced disclosure (Phase C2a Task 7). Spans the grid like
   .scenario-form__group and .scenario-form__presets above, and its own
   fieldsets reuse .scenario-form__group so they read as the same family. */
.scenario-form__advanced {
  grid-column: 1 / -1;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.scenario-form__advanced > summary {
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.scenario-form__advanced > summary:focus-visible {
  outline: 2px solid var(--accent-violet-text);
  outline-offset: 2px;
}

.scenario-form__advanced[open] > summary {
  margin-bottom: var(--space-3);
}

.scenario-form__advanced .scenario-form__group + .scenario-form__group {
  margin-top: var(--space-3);
}

/* The per-stream nested disclosure inside Escalation. Not a grid child, so
   just enough indent/spacing to read as subordinate to its fieldset. */
.scenario-form__nested {
  margin-top: var(--space-3);
  padding-left: var(--space-3);
  border-left: 1px solid var(--border);
}

.scenario-form__nested > summary {
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.scenario-form__nested > summary:focus-visible {
  outline: 2px solid var(--accent-violet-text);
  outline-offset: 2px;
}

/* One row per milestone item inside the Milestones fieldset (Phase C2a
   Task 8). Sets repeated rows off from each other the same way
   .scenario-form__advanced sets one fieldset off from the next. */
.scenario-form__milestone + .scenario-form__milestone {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

.scenario-form__presets-label {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

/* The competitor picker (2026-09-11): one dropdown per competitor, in the
   same auto-fit grid the scenario fields use, so a second or third slot
   flows beside the first instead of stacking a column. The add control and
   the note sit below the slots at full width. */
.picker {
  display: grid;
  gap: var(--space-3);
}

.picker__slots {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}

.picker__actions {
  display: flex;
  justify-content: flex-start;
}

.picker__note {
  color: var(--text-muted);
  font-size: var(--text-sm);
  margin: var(--space-3) 0 var(--space-2);
  max-width: 75ch;
}

.notice {
  color: var(--text-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  margin: var(--space-3) 0 0;
  max-width: 75ch;
}

/* A problem — something the user asked for that did not happen, e.g. a
   requested competitor the duty filter dropped (ComparisonResults::
   problemNotice()) — set apart from an informational .notice, which stays
   as quiet as it already is. Existing tokens only, same as every other rule
   here (test_comparison_css_uses_tokens_only): --state-error-text on
   --tint-error is the same pair already proven at 4.5:1 in
   tests/Design/ContrastTest.php's 'error badge' entry, so this reuses a
   contrast result the suite already guarantees rather than asking it to
   guarantee a new one. The "Problem" label is real text, not a color left
   to carry the meaning alone (§16) — a screen reader announces it as
   ordinary paragraph text, no ARIA required. */
.notice--problem {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--state-error-text);
  background: var(--tint-error);
  border: 1px solid var(--state-error);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin: var(--space-3) 0 0;
}

.notice--problem .notice__label {
  flex-shrink: 0;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: var(--text-xs);
}

/* A part-priced-data marker: a break-even figure computed between two engines
   whose maintenance data is priced to different extents. Not a problem — the
   user asked for it and got it, and the figure is real. It is a caution about
   what the figure covers, so it takes the warn tokens rather than the error
   ones, and it is announced by the word in .notice__label rather than by
   colour alone (§16). --state-warn on --tint-warn is already a declared,
   passing pair in tests/Design/ContrastTest.php. */
.notice--coverage {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--state-warn);
  background: var(--tint-warn);
  border: 1px solid var(--state-warn);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin: var(--space-3) 0 0;
}

.notice--coverage .notice__label {
  flex-shrink: 0;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: var(--text-xs);
}

/* Operator clean-up §4.2: the technical wording beneath a plain note, staff only. */
.notice-detail {
  margin: 0 0 var(--space-2);
  max-width: 75ch;
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.notice-detail > summary { cursor: pointer; color: var(--text-muted); }
.notice-detail > p { margin: var(--space-1) 0 0; }

/*
 * C3 charts (Capex\View\Chart\*). Server-rendered SVG: no JS, and the same
 * markup prints. Colour comes from tokens.css's --chart-screen-N / --chart-print-N
 * custom properties, referenced by the PHP that emits each <line>/<rect>/
 * marker's stroke or fill — nothing here assigns series colour, so this file
 * stays free of the "series identity by colour alone" mistake §7 forbids.
 * Token names below are this file's existing muted-border/muted-text/
 * primary-text/base-surface tokens, not new ones: --border (subtle, already
 * used for panel borders), --text-muted (already exists verbatim), --text
 * (primary content colour throughout this file) and --bg (the page ground
 * the chart figures sit on — they are not inside a Panel, so var(--surface)
 * would be the wrong ground to halo against).
 */
/* The deck is as wide as its widest row: StackChart::WIDTH (420) + the
   row gap (16) + VisitsChart::WIDTH (380) = 816px, which also holds the
   760-unit crossover at its designed size. Without this, on a wide
   workspace each capped chart sat at the left of a cell twice its width. */
.chart-deck { display: flex; flex-direction: column; gap: var(--space-4); margin-block-end: var(--space-5); max-width: 816px; }
.chart-deck__row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: var(--space-4); }
.chart { margin: 0; }
/* max-width is the chart's own designed width, published by Svg::open() as
   --chart-width: at 100% of a wide workspace a 760-unit chart drew at 2× and
   every label with it. Below that width it scales down as before. */
.chart__svg { width: 100%; max-width: var(--chart-width); height: auto; display: block; }
.chart__axis { stroke: var(--border); stroke-width: 1; }
.chart__tick { stroke: var(--border); stroke-width: 1; }
.chart__tick-label,
.chart__bar-label,
.chart__bar-coverage,
.chart__axis-title { font-size: 11px; fill: var(--text-muted); }
.chart__bar-total,
.chart__break-even-label { font-size: 12px; font-weight: 600; fill: var(--text); }
.chart__bar-gap { font-size: 11px; font-style: italic; fill: var(--text-muted); }
.chart__series-label { font-size: 11px; }

/* Decisions 1 and 2 (2026-09-04): a coverage/provenance note drawn as its own
   row beneath a series nameplate. Muted and unbold on purpose — it is a caveat
   about the line, not a second name for it — and deliberately NOT the series
   stroke colour, so a reader scanning for "which line is this" is not pulled
   into the note. CrossoverChart::SERIES_NOTE_ROW_HEIGHT (11.0) is this size;
   change one and the other must follow or a note lands on the next nameplate. */
.chart__series-note,
.chart__bar-note { font-size: 11px; fill: var(--text-muted); }
.chart__break-even { fill: var(--text); stroke: var(--bg); stroke-width: 2; }
.chart__milestone { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 3; }
.chart__milestone-label { font-size: 10px; fill: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.chart__caption { font-size: 12px; color: var(--text-muted); margin-block-start: var(--space-2); }
.chart__caption-text { margin: 0; }

/* The density key (2026-09-07). A key is a lookup table, not prose: a reader
   arrives holding a number and wants exactly one row, so it gets one line per
   engine rather than a run of semicolons. Numbers are tabular and right
   aligned in a fixed column so the names start on one edge and the eye can
   run straight down them. */
.chart__key {
  list-style: none;
  margin: 0 0 var(--space-2);
  padding: 0;
  display: grid;
  gap: 2px;
}
.chart__key-item {
  display: grid;
  grid-template-columns: 1.4em 1fr;
  gap: var(--space-2);
  align-items: baseline;
}
.chart__key-num {
  font-variant-numeric: tabular-nums;
  text-align: right;
  font-weight: 600;
  color: var(--text);
}
.chart__key-name { color: var(--text-muted); }

/*
 * Final review wave B, FIX 4. role="img" on .chart__svg (Svg::open()) makes
 * the SVG a leaf to assistive technology, so its own descendant <text>
 * cannot be reached that way. CrossoverChart's milestone years/kinds and
 * VisitsChart's service-events figures appear nowhere else on the page
 * (ComparisonResults.php has no row for either), so both charts emit the
 * same facts as this sibling list instead — a screen reader reaches it as
 * ordinary page text. Standard visually-hidden-but-accessible clip
 * technique: removed from the visual flow and clipped to nothing, but never
 * display:none / visibility:hidden, which assistive technology skips too.
 */
.chart__sr-table,
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Engines catalog (Phase E1): one card per engine, facts in words. */
.engine-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-4);
}

.engine-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-panel);
  min-width: 0;
}

.engine-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}

.engine-card__title {
  margin: 0;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  font-weight: 600;
  line-height: var(--leading-tight);
  overflow-wrap: anywhere;
}

.engine-card__sub {
  margin: var(--space-1) 0 0;
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

.engine-card__facts {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
}

.engine-card__line { display: block; }

.engine-card__label {
  display: block;
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.engine-card__value {
  display: block;
  margin: var(--space-1) 0 0;
  color: var(--text);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  overflow-wrap: anywhere;
}

.engine-card__value .badge { vertical-align: middle; }

.engine-card__footer {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

.engine-card__chips,
.engine-card__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

.engine-card__chips .engine-card__label { text-transform: none; font-size: var(--text-sm); color: var(--text-secondary); }

/*
 * Phase E2 — the engine editor. A retired-rating notice takes the warn pair
 * already proven in ContrastTest (--state-warn on --tint-warn), the same
 * rule as .notice--coverage; a muted row and a retired card say "retired"
 * in a badge or a select, so the dimming is reinforcement, never the only
 * signal (§16.9). Tokens only, as every block here.
 */
.notice--warn {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--state-warn);
  background: var(--tint-warn);
  border: 1px solid var(--state-warn);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin: 0 0 var(--space-4);
}

.notice--warn .notice__label {
  flex-shrink: 0;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: var(--text-xs);
}

.control--static {
  display: inline-block;
  padding: var(--space-1) 0;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  white-space: nowrap;
}

.table tr.is-muted .control,
.table tr.is-muted .control--static {
  opacity: 0.55;
}

.engine-card.is-retired {
  opacity: 0.7;
  border-style: dashed;
}

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

.engine-editor__form .field--wide { grid-column: 1 / -1; }

.engine-editor__id {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.engine-editor__line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.engine-editor.is-retired .panel { opacity: 0.85; }

/* ---- Fuel editor (E3) ------------------------------------------------ */
.fuel-editor__line { color: var(--text-secondary); margin: 0; }
.fuel-editor__paste .control--textarea { font-family: var(--font-mono); min-height: 16rem; white-space: pre; }
.fuel-editor__help { color: var(--text-muted); font-size: var(--text-sm); margin: .5rem 0; }
.fuel-editor__aliases summary { cursor: pointer; color: var(--text-secondary); font-size: var(--text-sm); }
.fuel-editor__aliases dl { display: grid; grid-template-columns: max-content 1fr; gap: .25rem 1rem; font-size: var(--text-sm); margin: .5rem 0 0; }
.fuel-editor__aliases dt { font-family: var(--font-mono); }
.fuel-editor__aliases dd { margin: 0; color: var(--text-secondary); }
.fuel-editor__errors { color: var(--state-error-text); margin: 0 0 .75rem; padding-left: 1.25rem; }
.fuel-editor__columns { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-secondary); margin: .5rem 0; }
.fuel-editor__preview { border-top: 1px solid var(--border); margin-top: 1rem; padding-top: 1rem; }
.fuel-editor__confirm { background: var(--tint-warn); border-radius: var(--radius-md); padding: var(--panel-padding); margin-top: .75rem; }
.grid-compare { margin: .75rem 0; }

/* ---- Admin export / history (E4) ------------------------------------- */
.export-panel__meta { display: grid; grid-template-columns: max-content 1fr; gap: .25rem 1rem; margin: 1rem 0; }
.export-panel__meta dt { color: var(--text-secondary); }
.export-panel__meta dd { margin: 0; }
.export-panel__line { color: var(--text-secondary); margin: .5rem 0; }
.history-filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: .75rem 1rem; align-items: end; margin-bottom: 1rem; }
.history-filters .row-actions { grid-column: 1 / -1; }
.history-table .table__td { vertical-align: top; }
.history-pagination { display: flex; align-items: center; gap: .75rem; margin: 1rem 0; }
.history-pagination__count { color: var(--text-secondary); font-family: var(--font-mono); }
.history-purge__preview { margin: .75rem 0; }
@media (max-width: 720px) { .history-filters { grid-template-columns: 1fr; } }

/* Phase F — saved scenarios */
.scenario-filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .75rem 1rem; align-items: end; margin-bottom: 1rem; }
.scenario-filters .row-actions { grid-column: 1 / -1; }
.scenario-table .table__td { vertical-align: top; }
.scenario-table__name { display: block; font-weight: 600; }
.scenario-table__quote { display: block; color: var(--text-muted); font-size: var(--text-xs); }
.scenario-table__delete { display: inline; }
.scenario-table__confirm { background: var(--tint-warn); border-radius: var(--radius-md); padding: var(--panel-padding); margin-top: .5rem; }
.scenario-banner__meta { display: grid; grid-template-columns: max-content 1fr; gap: .25rem 1rem; margin: 0 0 .75rem; }
.scenario-banner__meta dt { color: var(--text-muted); font-size: var(--text-xs); }
.scenario-banner__meta dd { margin: 0; }
.scenario-banner__line { margin: .5rem 0 .25rem; }
.scenario-banner__drift { margin: 0 0 .5rem 1.25rem; }
.scenario-banner__note { color: var(--text-muted); font-size: var(--text-xs); margin: 0; }
.scenario-save { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .75rem 1rem; align-items: end; }
.scenario-save .row-actions, .scenario-save__caption { grid-column: 1 / -1; }
.scenario-save__caption { color: var(--text-muted); font-size: var(--text-sm); margin: 0; }

/* Spec 2026-09-18 — exchange rates */
.fx-panel__line { color: var(--text-secondary); margin: 0 0 var(--space-4); }
.fx-panel__subtitle { margin: var(--space-5) 0 var(--space-2); font-size: var(--text-sm); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.form-grid--2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .75rem 1rem; align-items: start; }

/* Operator clean-up §4.7: .sr-only (text for screen readers only — the charts'
   notes about how they were drawn) shares the .chart__sr-table clip rule above. */
.chart__title { margin: 0 0 var(--space-2); color: var(--text); font-size: var(--text-sm); font-weight: 600; }

.chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  margin: 0 0 var(--space-1);
  padding: 0;
  list-style: none;
  color: var(--text-muted);
  font-size: 12px;
}

.chart__swatch { display: inline-block; width: 10px; height: 10px; margin-right: 6px; border-radius: 2px; vertical-align: -1px; }
.chart__swatch--1 { background: var(--chart-stream-1); }
.chart__swatch--2 { background: var(--chart-stream-2); }
.chart__swatch--3 { background: var(--chart-stream-3); }
.chart__swatch--4 { background: var(--chart-stream-4); }
.chart__swatch--5 { background: var(--chart-stream-5); }
.chart__swatch--6 { background: var(--chart-stream-6); }
