/* app.css — the RateRoutine interface system.
   Theme 5 "Current Line" (#120), built out to a full system in #171.
   Spec: docs/designs/2026-08-28-interface-design-system.md

   Layering. Type, space, radius and measure run primitive -> semantic ->
   component. COLOUR RUNS TWO LAYERS, deliberately: the ratified role is
   declared at its literal hex and component tokens alias it. It is not
   `--ink: var(--green-black-900)`, because tests/unit/palette-contrast.test.js
   asserts `declared.get('--ink') === '#172A2B'` and that guard is worth more
   than the diagram — it is the thing that caught `button:hover { opacity: 0.9 }`
   silently dropping a 4.71:1 label to 3.95:1. A primitive layer over values
   that are ratified and must not change buys nothing anyway.

   The palette is ratified and the contrast rules are binding
   (docs/repo-constraints.md). Four of those rules constrain THIS FILE's shape
   rather than its colours, and breaking any of them fails the suite:
     - one :root block only (the raw-hex scanner strips the first, then reads
       everything left as "outside");
     - every :root statement is a custom property — a `color-scheme:` line in
       there fails the declaration parse;
     - no `opacity` outside :root, ever;
     - no raw hex outside :root except #fff. */

:root {
  /* ── Ratified roles — Theme 5 (#120) ─────────────────────────────────
     Literal hex, not aliases. See the layering note above. */

  /* No `color-scheme: light dark`. It was declared here with no dark palette
     behind it, so a dark-mode browser painted dark chrome behind colours
     defined for light surfaces and every ratified contrast ratio was measured
     against a surface the visitor never saw. #120 did not ratify a dark
     palette and inventing one is a design decision, not a cleanup. */

  /* Text and structure */
  --ink: #172A2B;      /* headings, body, nav, wordmark — 14.97 on white */
  --muted: #617072;    /* secondary copy — 5.16 on white, 4.70 on paper */
  --divider: #D5DEDB;  /* rules, table lines, quiet boundaries */

  /* Surfaces */
  --paper: #F2F5F3;    /* page background */
  --surface: #FFFFFF;  /* work surfaces and reports */
  --soft: #DDF0EC;     /* verified and informational backgrounds */

  /* Brand colour, as FILLS. The brand teal (#168F8A) and coral (#ED5347) are
     the identity but not what a control is painted with: they are 3.94 and
     3.54 against white, so neither can carry a white label and — per rule 1 —
     neither is ever body text. These are the darkened fills that clear AA,
     and their -hover pairs darken further so a state change never spends
     contrast. */
  --primary: #0F6E6A;        /* white label at 6.07 */
  --primary-hover: #0C5B58;  /* white label at 7.92 */
  --urgent: #C9473C;         /* white label at 4.72 */
  --urgent-hover: #A93A31;   /* white label at 6.29 */

  /* Secondary data and links. Links are body text, so rule 1 puts them here
     rather than on the brand teal. */
  --deep: #244F62;     /* 8.86 on white */

  /* Signal yellow is a fill only, never a foreground: 1.72 on white. Ink on
     it is 8.68, which is the one sanctioned pairing (rule 3). */
  --signal: #F5BC4B;

  /* The foreground for any darkened brand fill — exactly the pairing rule 2
     measures. Named so it stops being a bare #fff sitting in a rule. */
  --on-fill: #FFFFFF;

  /* ── Primitive: type ──────────────────────────────────────────────────
     16px base, 1.200 minor third. A conservative ratio on purpose: this is
     finance and accounting work and §6 asks for calm. */
  --size-100: 0.833rem;  /* 13.3px */
  --size-200: 1rem;      /* 16px   */
  --size-300: 1.2rem;    /* 19.2px */
  --size-400: 1.44rem;   /* 23px   */
  --size-500: 1.728rem;  /* 27.6px */

  /* ── Primitive: space ─────────────────────────────────────────────────
     4px base. Absorbs the nine ad-hoc values this file used to carry
     (0.3 0.4 0.5 0.6 0.75 0.9 1.25 1.5 3rem), which had nothing relating
     them to each other. */
  --space-0: 0rem;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;

  /* ── Primitive: line and edge ─────────────────────────────────────── */
  --radius-1: 4px;   /* controls */
  --radius-2: 8px;   /* cards, heroes */
  --border-1: 1px;   /* rules and control boundaries */
  --border-3: 3px;   /* status markers */
  --border-4: 4px;   /* hero state bars */

  /* ── Semantic: type ─────────────────────────────────────────────────── */
  --text-h1: var(--size-500);
  --text-h2: var(--size-400);
  --text-h3: var(--size-300);
  --text-h4: var(--size-200);
  --text-body: var(--size-200);
  --text-small: var(--size-100);
  --leading-body: 1.5;
  --leading-heading: 1.25;

  /* ── Semantic: measure ────────────────────────────────────────────────
     Two, because this app has two kinds of screen. v1 §E calls the Locations
     screen "a dense table — this is an app, not a card gallery" and calls the
     product desktop-primary; one 32rem column served neither. */
  --measure-prose: 34rem;  /* auth, onboarding, forms, running text */
  --measure-app: 72rem;    /* screens whose primary object is a table */

  /* ── Component ────────────────────────────────────────────────────────
     Aliases, so a control can be restyled without touching the palette. */
  --btn-bg: var(--primary);
  --btn-bg-hover: var(--primary-hover);
  --btn-fg: var(--on-fill);
  --btn-bg-danger: var(--urgent);
  --btn-bg-danger-hover: var(--urgent-hover);
  --btn-bg-disabled: var(--divider);
  --btn-fg-disabled: var(--muted);

  --field-bg: var(--surface);
  --field-fg: var(--ink);
  --field-border: var(--muted);
  --field-border-focus: var(--primary);

  --card-bg: var(--surface);
  --card-border: var(--divider);

  --table-rule: var(--divider);
  --table-head-fg: var(--ink);

  --status-attention: var(--signal);  /* stale, unverified, unresolved */
  --status-ok: var(--primary);        /* achieved, verified */
  --status-alarm: var(--urgent);      /* blind, retracted, exception */
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: var(--text-body);
  color: var(--ink);
  background: var(--paper);
  line-height: var(--leading-body);
}

/* ── Type scale ───────────────────────────────────────────────────────────
   h1 was 1.4rem (22.4px) with no h2 rule at all, so h2 took the UA default
   24px and every page rendered its h1 SMALLER than the h2s beneath it — 38 h2
   sites across five page modules. The scale is declared end to end so the
   hierarchy cannot invert again. */

h1, h2, h3, h4 {
  line-height: var(--leading-heading);
  margin: var(--space-5) 0 var(--space-3);
}

h1 {
  font-size: var(--text-h1);
  margin-top: var(--space-0);
}

h2 {
  font-size: var(--text-h2);
}

h3 {
  font-size: var(--text-h3);
}

h4 {
  font-size: var(--text-h4);
}

/* ── Layout ─────────────────────────────────────────────────────────────── */

main {
  max-width: var(--measure-app);
  margin: 0 auto;
  padding: var(--space-5) var(--space-4) var(--space-7);
}

/* Running text does not stretch to the app measure just because a table on
   the same screen needs it. */
main > p,
main > ul,
main > ol,
main > form {
  max-width: var(--measure-prose);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--border-1) solid var(--divider);
  background: var(--surface);
}

.nav-brand {
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}

.nav-slot {
  color: var(--muted);
  font-size: var(--text-small);
}

/* ── Primary navigation (#194) ────────────────────────────────────────────
   The nav carried a wordmark and an email and nothing else, so /settings and
   /coverage — real, working routes — were reachable only by typing the URL.
   The list sits beside the wordmark and `margin-right: auto` pushes the
   identity block right, which keeps the signed-out two-child nav laying out
   exactly as it did rather than needing a second container.

   Which links appear is decided in html.js against the same role rank the
   routes use, not here: CSS can only hide a link, and a hidden link is still
   in the document for anything reading it. */

.nav-links {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin: 0 auto 0 var(--space-2);
  padding: 0;
  font-size: var(--text-small);
}

/* The one control in the nav, and it is destructive-adjacent: quiet by
   default so it does not compete with the page's primary action, and darker
   on interaction. --deep is 8.86 on --surface and --ink is 14.97, so the
   state change RAISES contrast — the rule the palette test enforces by
   refusing `opacity` anywhere in this file. */
.nav-logout button {
  padding: var(--space-1) var(--space-2);
  background: transparent;
  border-color: var(--divider);
  color: var(--deep);
  font-size: var(--text-small);
}

.nav-logout button:hover {
  background: transparent;
  border-color: var(--muted);
  color: var(--ink);
}

/* #196 part 3. The flash is a one-shot: html.js consumes the cookie on read
   and it carries a 30-second life, so a message missed once cannot be re-shown
   — there is no history and no re-request that brings it back. It renders once
   between the nav and <main>, and the staff queue's eleven in-table forms all
   redirect back to the same long page, so the confirmation for the row you
   just acted on could land above a fold you are not looking at. Sticky, so it
   stays reachable for as long as it exists.

   `top: 0` and not the nav's height: the nav is not sticky, so there is no
   fixed strip to clear — the flash pins to the viewport once the nav has
   scrolled past it. z-index because a later card with its own stacking context
   would otherwise paint over it. */
.flash {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: var(--space-3) var(--space-4);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-1);
  background: var(--signal);
  color: var(--ink);
}

/* ── Forms and controls ─────────────────────────────────────────────────── */

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

label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--text-small);
}

label small {
  color: var(--muted);
  font-weight: normal;
}

/* The column above was written for stacked text-input labels, and a checkbox
   is not one: it put every checkbox on its own row with its text below,
   reading as an orphaned unlabelled box above an unrelated caption. Six sites
   — onboarding.js's groups form, board.js's coverage detail and the two
   settings reminders, billing.js's annual toggle and location picker — not
   only the one the design pass screenshotted.

   This is the SAME failure `form.inline` below already carries a comment
   about: one generic flex rule swallowing a control that needed a row. The
   override was written for forms and never for labels.

   The markup is already correct — onboarding.js:221 wraps input and text in
   one <label>, so screen-reader association held throughout and only the
   visual grouping was broken. Hence a CSS-only fix: `:has()` selects the
   label that contains the box rather than asking five page modules to add a
   class. */
label:has(> input[type="checkbox"]),
label:has(> input[type="radio"]) {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
}

/* `.inline` is on 11 in-table action forms across staff.js and board.js, and
   was never defined — so `form { flex-direction: column }` above applied to all
   of them and every Approve/Reject/Enable control stacked vertically inside its
   table cell. */
form.inline,
form.step-form,
form.verify-form,
form.dismiss-form {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* #196 part 4. The per-group blocks on /onboarding/groups are the one
   card-shaped container in the app still wearing UA styling: a 2px inset ridge
   in a system colour, sitting on a screen whose other cards (.cluster-card,
   .coverage-row) are --card-border at --radius-2 on --card-bg. Same treatment,
   so a group reads as one of the app's cards and not as a browser default that
   nobody got to.

   The flex column replaces `form`'s gap, which stops at the fieldset boundary:
   the two labels inside are block-level flex containers and would otherwise
   stack flush against each other. */
fieldset {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-4);
  border: var(--border-1) solid var(--card-border);
  border-radius: var(--radius-2);
  background: var(--card-bg);
}

/* The group's name, and the same weight a .cluster-card summary carries. */
legend {
  padding: 0 var(--space-2);
  font-size: var(--text-h4);
  font-weight: 600;
  color: var(--ink);
}

input {
  font: inherit;
  padding: var(--space-2);
  /* --muted (5.16:1 on white), not --divider (1.37:1). WCAG 1.4.11 wants 3:1
     for the visual boundary of a user-interface component, and this hairline is
     the only thing that says an input is here: the field's own --surface fill is
     1.05:1 against the page. --divider stays for decorative rules, which are
     exempt. */
  border: var(--border-1) solid var(--field-border);
  border-radius: var(--radius-1);
  background: var(--field-bg);
  color: var(--field-fg);
}

input:focus-visible {
  outline: var(--border-3) solid var(--field-border-focus);
  outline-offset: 1px;
}

/* #196 part 2. /onboarding/import is the single screen where a customer hands
   the product their roster, and the control they click there was whatever the
   operating system draws — grey Chromium chrome sitting inside a stylesheet
   that had opinions about everything else on the page.

   The `input` rule above cannot reach it: the button inside a file input is a
   shadow pseudo-element, so it takes the app's button treatment only if it is
   named. Same tokens as `button` rather than a lookalike, because it IS a
   button and nothing on the page tells the customer this one was drawn by the
   browser. The field's own padding drops to --space-1 so the pseudo-button's
   padding sets the control's height instead of stacking on top of it. */
input[type="file"] {
  padding: var(--space-1);
}

input[type="file"]::file-selector-button {
  font: inherit;
  margin-right: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: var(--border-1) solid var(--btn-bg);
  border-radius: var(--radius-1);
  background: var(--btn-bg);
  color: var(--btn-fg);
  cursor: pointer;
}

/* Darker, never faded — the same state rule every other control here follows
   (§5.3). A control that looks like a button and does not answer a pointer
   reads as disabled. */
input[type="file"]::file-selector-button:hover {
  background: var(--btn-bg-hover);
  border-color: var(--btn-bg-hover);
}

button {
  font: inherit;
  padding: var(--space-2) var(--space-3);
  border: var(--border-1) solid var(--btn-bg);
  border-radius: var(--radius-1);
  background: var(--btn-bg);
  color: var(--btn-fg);
  cursor: pointer;
  width: fit-content;
}

/* Darker on hover, never faded. `opacity: 0.9` used to live here: it
   composites the whole control over the page background and takes a 4.71:1
   label to 3.95:1 — failing AA the moment a pointer touches it, with every
   token still reading as compliant. The palette test refuses any opacity
   declaration in this file for that reason. */
button:hover {
  background: var(--btn-bg-hover);
  border-color: var(--btn-bg-hover);
}

button[disabled] {
  background: var(--btn-bg-disabled);
  border-color: var(--btn-bg-disabled);
  color: var(--btn-fg-disabled);
  cursor: not-allowed;
}

/* A defined variant with no use site yet: board.js's Dismiss is the
   destructive control, but wiring the class onto it is a markup change and
   #171 makes none. Defined ahead of use for the same reason --urgent itself
   was — so the phase that does emit one reaches for a measured value instead
   of picking a red. */
button.danger {
  background: var(--btn-bg-danger);
  border-color: var(--btn-bg-danger);
}

button.danger:hover {
  background: var(--btn-bg-danger-hover);
  border-color: var(--btn-bg-danger-hover);
}

a {
  color: var(--deep);
}

/* #196 part 1, landed alongside #194's nav because that nav is a list of
   links. `input` already had the deliberate --field-border-focus treatment
   above; button and a fell through to the UA's `outline-style: auto`, so the
   app's primary wayfinding would have shipped with no focus ring a keyboard
   user can rely on. Same token and same width as the input rule — one focus
   idiom, not two — at a wider offset so a filled button's own border does not
   swallow it. */
button:focus-visible,
a:focus-visible {
  outline: var(--border-3) solid var(--field-border-focus);
  outline-offset: 2px;
}

p small,
p {
  color: var(--muted);
}

/* ── Tables ───────────────────────────────────────────────────────────────
   Five page modules render tables and none of them had a rule. --divider's own
   comment claimed it covered "table lines" while having zero table use sites;
   this is where it earns that. */

table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--space-4) 0;
}

th {
  text-align: left;
  color: var(--table-head-fg);
  font-size: var(--text-small);
  border-bottom: var(--border-1) solid var(--table-rule);
}

td {
  border-bottom: var(--border-1) solid var(--table-rule);
}

th,
td {
  padding: var(--space-2) var(--space-3);
  vertical-align: top;
}

/* Below the app measure a wide table scrolls inside itself instead of
   scrolling the page body. The 9-column staff Sources table and the 7-column
   Change events table both used to push the whole document sideways. Above
   this width it stays a real table — v1 §E is desktop-primary and asks for no
   separate mobile design, only that narrow widths stay usable. */
@media (max-width: 72rem) {
  table {
    display: block;
    overflow-x: auto;

    /* #195 part 3. That scroll was real and invisible. At 375px the Locations
       table measures 475px against a 343px viewport, so the per-row "Check
       now" button — the only action on the screen — sits past the right edge,
       and an overlay scrollbar shows nothing until a finger is already
       dragging. The row reads as complete.

       Four gradients in two pairs. The `local` covers are anchored to the
       scrolled CONTENT; the `scroll` shadows are anchored to the ELEMENT. At
       rest a cover sits exactly over its shadow and nothing shows; scroll away
       from an edge and that cover travels with the content, uncovering the
       shadow behind it. So a cue appears only on a side that genuinely has
       more table — where one static fade would claim overflow on both edges of
       a table that fits, which is the same lie as no cue at all.

       --paper is what the table sits on and --divider is the quiet-boundary
       role; no new colour and no new token. */
    background-image:
      linear-gradient(to right, var(--paper) 50%, transparent),
      linear-gradient(to left, var(--paper) 50%, transparent),
      linear-gradient(to right, var(--divider), transparent),
      linear-gradient(to left, var(--divider), transparent);
    background-position: left center, right center, left center, right center;
    background-repeat: no-repeat;
    background-size:
      var(--space-5) 100%, var(--space-5) 100%,
      var(--space-2) 100%, var(--space-2) 100%;
    background-attachment: local, local, scroll, scroll;
  }
}

/* ── Status ───────────────────────────────────────────────────────────────
   `.amber` is the app's only status hook — unresolved locations, stale
   sources, blind sources, unverified cadences, retracted change events — and
   it painted nothing, so all five rendered identically to healthy rows. That
   is worse than no hook, because it reads as handled.

   It is a marker PLUS the text label each site already renders, never a fill
   carrying the meaning on its own: Theme 5 §6 and v1 §E both require that
   colour is never the only status indicator, and rule 3 bars signal yellow as
   a foreground on any light surface. It lands on <tr>, <p> and <li>, so the
   left border needs the border-collapse set above to render on a row. */

.amber {
  border-left: var(--border-3) solid var(--status-attention);
  padding-left: var(--space-2);
}

/* ── Hero (v1 §E board states) ────────────────────────────────────────────
   §E requires the safety empties to be visual opposites: verified-clean is
   affirmative, and key-missing / sweep-off / budget-exhausted are a loud
   warning, never a calm empty. */

.hero {
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-2);
  background: var(--card-bg);
  border-left: var(--border-4) solid var(--card-border);
  margin-bottom: var(--space-5);
}

/* Work to do. */
.hero-open {
  border-left-color: var(--status-ok);
}

/* Affirmative. This is --soft's first use site, which activates two
   repo-constraints rules that had never been checked against a real pair:
   rule 4 (coral on soft is 2.99 — refused at any size) and rule 5 (--muted on
   soft is 4.36 — misses AA). Hence ink, and no coral anywhere in here. */
.hero-clean {
  background: var(--soft);
  border-left-color: var(--status-ok);
  color: var(--ink);
}

/* Blind states preempt everything (§E: "loud banner"). White on --urgent is
   4.72 — the sanctioned rule 2 pairing, AA for normal text. */
.hero-blind {
  background: var(--status-alarm);
  border-left-color: var(--urgent-hover);
  color: var(--on-fill);
}

.hero-blind a {
  color: var(--on-fill);
}

.hero-buckets {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

/* Secondary to the hero; each number clicks through to a pre-filtered
   Locations screen. */
.trust-line {
  font-size: var(--text-small);
  color: var(--muted);
  margin-top: var(--space-2);
}

/* ── Drift clusters (the v1 value gate) ─────────────────────────────────── */

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

.cluster-card {
  background: var(--card-bg);
  border: var(--border-1) solid var(--card-border);
  border-radius: var(--radius-2);
  padding: var(--space-4);
}

.cluster-card > summary {
  cursor: pointer;
  font-weight: 600;
}

.cluster-body {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: var(--border-1) solid var(--card-border);
}

/* Per-location step checkoffs: POS / accounting / spreadsheet-if-required. */
.step {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) 0;
}

.step-not-required {
  color: var(--muted);
  text-decoration: line-through;
}

/* ── Onboarding milestones (v1 §E first-run dashboard) ────────────────────
   Data ready -> Resolved -> Baseline ready -> Monitoring on. The marker
   duplicates the ✓/· glyph the row already renders, so the state survives
   monochrome. */

.milestone-done,
.milestone-pending {
  list-style: none;
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-2);
  border-left: var(--border-3) solid var(--card-border);
  background: var(--card-bg);
}

.milestone-done {
  border-left-color: var(--status-ok);
}

.milestone-pending {
  border-left-color: var(--status-attention);
}

/* ── Filters, coverage, staff ─────────────────────────────────────────── */

/* Bound to the trust-line numbers (§E). */
.filter-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  font-size: var(--text-small);
}

/* #195 part 2. Three pills that looked identical whether or not you were
   standing on one, so the filtered table and the unfiltered one were told
   apart only by the address bar. Weight and an underline, not a colour swap:
   the state has to survive monochrome like every other state here, and the
   link keeps --deep so it still reads as a link.

   Hung off aria-current rather than a class so the announced state and the
   painted state are the same fact. A class would let one be right while the
   other is stale. */
.filter-pills a[aria-current="page"] {
  font-weight: 600;
  text-decoration: underline;
}

/* #251 (FORGE-320). The monitoring cadence, stated where the per-row "Check now"
   button used to be. Quiet by design: it is a standing fact about the whole
   table rather than an action, so it sits between the filters and the table at
   the same weight as the trust line, and never competes with a data row. */
.cadence {
  font-size: var(--text-small);
  color: var(--muted);
  margin-top: var(--space-2);
  margin-bottom: var(--space-4);
}

/* The answer a filter gives when it matches nothing — quieter than a data row,
   because it is not one, and centred so it is not mistaken for a first record.
   An empty findings list is what this product is for; it must not render as a
   table that failed to load. */
.empty-row td {
  padding: var(--space-5) var(--space-3);
  color: var(--muted);
  text-align: center;
}

.coverage-row {
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  border: var(--border-1) solid var(--card-border);
  border-radius: var(--radius-2);
  background: var(--card-bg);
}

.staff-batch {
  padding: var(--space-4);
  margin-bottom: var(--space-5);
  border: var(--border-1) solid var(--card-border);
  border-radius: var(--radius-2);
  background: var(--card-bg);
}

/* ── Early agent alerts (#253 / FORGE-322) ────────────────────────────────
   Two surfaces for one opt-in feature: the /settings fieldset that turns it on,
   and the board section listing what an agent has read but nobody has confirmed.

   The board section is deliberately styled AWAY from `.cluster-card`. A cluster
   card is a finding that passed the review gate against an official file; this is
   an LLM's reading of a press release. #249 (FORGE-319) asked for an indicator
   telling the two apart, and the eng review answered it by separation — different
   objects, different sections, different treatments — so they cannot converge
   however the styling later drifts.

   It carries `.amber`'s reasoning without reusing the class: the meaning is in
   the heading and body copy ("An agent can be wrong"), never in the colour alone
   (Theme 5 §6, v1 §E — colour is never the only status indicator). */

.agent-announcements {
  margin-top: var(--space-5);
  padding: var(--space-4);
  border: var(--border-1) dashed var(--card-border);
  border-radius: var(--radius-2);
  background: var(--card-bg);
}

.agent-announcements > h2 {
  margin-top: 0;
}

.agent-announcements ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.agent-announcements li {
  border-left: var(--border-3) solid var(--status-attention);
  padding-left: var(--space-2);
}

/* #261 (FORGE-327) T2 — the per-row overdue marker.

   It gets NO new colour token and no badge, deliberately. The marker is a statement about the
   state of the CHECK, never the state of the rate (design D7), and a coloured chip is exactly
   the treatment that would read as "this rate is now wrong" — the mode:'active' claim the
   announcements/findings separation exists to prevent. The words carry the meaning, as they do
   for the section itself above; the only visual job here is to separate the marker from the
   claim it qualifies. */

.agent-announcement-overdue {
  margin: var(--space-2) 0 0;
  font-size: 0.9375rem;
}

.agent-alerts {
  margin: var(--space-4) 0;
  padding: var(--space-4);
  border: var(--border-1) solid var(--card-border);
  border-radius: var(--radius-2);
}

.ack-record {
  color: var(--muted);
  font-size: 0.875rem;
}
