/* ─────────────────────────────────────────────────────────────────────────
 * MASA Operational Hub — prototype stylesheet
 * Token contract: Discord design system, bound to a LIGHT theme.
 *
 * The schema token names are unchanged, so the contract still holds and the
 * brand can still be swapped wholesale. The light values follow the
 * direction already set in this file — cool near-white surfaces, deep teal
 * ink, a muted steel-blue accent — with the gaps filled and the measured
 * contrast failures corrected.
 *
 * Every ratio quoted below is measured against the resolved sRGB value on
 * all three surfaces, in the order page / card / rail.
 * No raw color literal outside this :root block.
 * ─────────────────────────────────────────────────────────────────────── */

:root {
  color-scheme: light;

  /* ─── Surface ──────────────────────────────────────────────────────
   * Page and card sit deliberately close, so hairlines carry the structure
   * rather than luminance steps. --surface-warm is the one genuinely
   * recessed layer: the rail, inputs, dwell tracks, document rows and
   * checklist rows all read against the card through it, so it needs a
   * real step. At 96.4% it measured 1.02:1 against the card — invisible. */
  --bg:           oklch(98.818% 0.00423 271.371);        /* page canvas        #f3f6fd */
  --surface:      oklch(100.00% 0.0000 0.00);  /* cards, tables      #f5f5f6 */
  --surface-warm: oklch(100.00% 0.0000 0.00);        /* rail, inputs       #e9e8f0 */

  /* ─── Foreground ───────────────────────────────────────────────────
   * --fg-2 sets headings and has to outweigh --fg; the two were the wrong
   * way round, so they are swapped. --meta was 2.82:1 and set every column
   * head, eyebrow and rail count — all small text — so it is darkened.
   *   --fg-2  15.13 / 14.97 / 13.45     --muted  7.10 / 7.03 / 6.32
   *   --fg    11.11 / 10.99 /  9.88     --meta   5.49 / 5.43 / 4.88  */
  --fg:    oklch(33.2% 0.04 220.63);   /* body text                    #1c3b44 */
  --fg-2:  oklch(24% 0.04 220.63);     /* headings, emphasis           #04232c */
  --muted: oklch(44% 0.04 220.63);     /* secondary text, hints        #395862 */
  --meta:  oklch(50% 0.04 220.63);     /* eyebrows, column heads       #496973 */

  /* ─── Border ───────────────────────────────────────────────────────
   * Ink at low alpha, so one value holds up on all three surfaces. The
   * dark theme's white-at-6% divider was invisible on a near-white page,
   * and --border-soft was a solid #3f4147 that would have outlined every
   * card and checklist row in near-black. */
  --border:      color-mix(in oklab, oklch(24% 0.04 220.63) 5%, transparent);
  --border-soft: color-mix(in oklab, oklch(24% 0.04 220.63) 10%, transparent);

  /* ─── Surface modifiers ────────────────────────────────────────────
   * DESIGN.md §2 background-modifier-hover / -selected and the mention
   * highlight. These were inlined as rgba() literals tuned for a dark
   * surface; as tokens they now invert with the theme. */
  --hover:       color-mix(in oklab, oklch(24% 0.04 220.63) 7%, transparent);
  --selected:    color-mix(in oklab, oklch(24% 0.04 220.63) 12%, transparent);
  --accent-wash: color-mix(in oklab, oklch(52.96% 0.09 247.21) 12%, transparent);

  /* ─── Accent ───────────────────────────────────────────────────────
   * Hue kept as set. Hover was identical to the base, so the primary
   * button had no hover state at all; it now steps 8 points down the L
   * channel, taking the white label from 5.25:1 to 7.40:1. The accent
   * never sets small text — 4.87 / 4.82 / 4.33 clears the 3:1 non-text
   * floor everywhere it appears. */
  --accent:        oklch(52.96% 0.09 247.21);   /* #3d709d */
  --accent-on:     #ffffff;
  --accent-hover:  oklch(45% 0.09 247.21);      /* #265984 */
  --accent-active: color-mix(in oklab, var(--accent), black 14%);

  /* ─── Semantic ─────────────────────────────────────────────────────
   * Saturated status colors that work on a dark surface do not survive on
   * near-white: amber measured 1.73:1 and was effectively invisible. Each
   * is mixed toward the ink until it clears 3:1 as a dot, bar or border —
   * success 4.47/4.43/3.98, warn 3.61/3.58/3.21, danger 4.39/4.34/3.90.
   * Status chips pair these with an ink label instead of colored text. */
  --success: color-mix(in oklab, #23a55a 75%, oklch(24% 0.04 220.63));  /* #198250 */
  --warn:    color-mix(in oklab, #f0b232 65%, oklch(24% 0.04 220.63));  /* #997e3f */
  --danger:  color-mix(in oklab, #f23f43 85%, oklch(24% 0.04 220.63));  /* #cd4142 */

  /* ─── Typography ───────────────────────────────────────────────────
   * Roboto, the face on MASA's own site. One family across display and
   * body is the right call for a data-dense operational tool — hierarchy
   * comes from weight, 400 through 800, not from a second typeface.
   *
   * The faces are loaded from Google Fonts in each document head. Without
   * that link Roboto is not a system font on macOS or Windows and the
   * stack silently fell through to Helvetica Neue.
   *
   * Mono pairs as Roboto Mono: it carries record numbers, gate codes,
   * stage numbers and document versions, and the previous stack resolved
   * to whatever monospace the machine happened to have. */
  --font-display: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-body:    "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono:    "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --text-xs:   12px;
  --text-sm:   14px;
  --text-base: 16px;
  --text-lg:   18px;
  --text-xl:   20px;
  --text-2xl:  24px;
  --text-3xl:  32px;
  --text-4xl:  56px;

  --leading-body:     1.375;
  --leading-tight:    1.10;
  --tracking-display: -0.02em;

  /* ─── Spacing ──────────────────────────────────────────────────── */
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-8:  32px;
  --space-12: 40px;

  --section-y-desktop: 80px;
  --section-y-tablet:  48px;
  --section-y-phone:   32px;

  /* ─── Radius ───────────────────────────────────────────────────── */
  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-lg:   16px;
  --radius-pill: 9999px;

  /* ─── Elevation ────────────────────────────────────────────────── */
  --elev-flat:   none;
  --elev-ring:   0 0 0 1px var(--border);
  /* A soft ink shadow. 40% black would read as a bruise on near-white. */
  --elev-raised: 0 2px 6px color-mix(in oklab, oklch(24% 0.04 220.63) 14%, transparent),
                 0 0 0 1px var(--border);

  /* ─── Focus ────────────────────────────────────────────────────── */
  /* Solid. The 30% wash blended to roughly 1.4:1 and failed the 3:1
     indicator floor; solid accent measures 4.87 / 4.82 / 4.33 and stays a
     single shadow so the `inset` variant on table rows still works. */
  --focus-ring: 0 0 0 3px var(--accent);

  /* ─── Motion ───────────────────────────────────────────────────── */
  --motion-fast:   80ms;
  --motion-base:   200ms;
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);

  /* ─── Layout ───────────────────────────────────────────────────── */
  --container-max:            1200px;
  --container-gutter-desktop: 24px;
  --container-gutter-tablet:  16px;
  --container-gutter-phone:   16px;
}

/* ── Reset ─────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { margin: 0; color: var(--fg-2); line-height: var(--leading-tight); }
p { margin: 0; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; }
:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

/* ── App frame ─────────────────────────────────────────────────────── */
.app {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  min-height: 100vh;
}

.rail {
  background: var(--surface-warm);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-3) var(--space-4);
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

/* MASA's own lockup sits above the product name rather than beside it —
   the mark is a 4.5:1 horizontal and has nowhere to go on a 248px rail if
   something shares its line. 176px keeps the four coloured diamonds small
   enough that they read as a signature rather than the loudest thing on a
   near-neutral screen. */
.rail__brand {
  display: grid;
  justify-items: start;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-2) var(--space-5);
  text-decoration: none;
}
.rail__logo {
  display: block;
  /* 335px native, so 167px is the largest size that stays crisp on a 2x
     display. A vector or @2x file would lift this ceiling. */
  width: 164px;
  height: auto;
}
.rail__sub {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--meta);
}
.rail__brand:hover .rail__sub { color: var(--fg-2); }

.rail__label {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--meta);
  padding: var(--space-4) var(--space-2) var(--space-1);
}

.navlink {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--muted);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 500;
  min-height: 36px;
  border: 0;
  background: none;
  width: 100%;
  text-align: left;
  cursor: pointer;
  transition: background var(--motion-fast) var(--ease-standard),
              color var(--motion-fast) var(--ease-standard);
}
.navlink:hover { background: var(--hover); color: var(--fg-2); }
.navlink[aria-current="page"],
.navlink.is-active { background: var(--selected); color: var(--fg-2); }
.navlink__count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--meta);
}
.navlink:hover .navlink__count,
.navlink.is-active .navlink__count { color: var(--fg); }

.rail__foot { margin-top: auto; padding-top: var(--space-4); }

/* ── Account block ─────────────────────────────────────────────────── */
.account {
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
  display: grid;
  gap: var(--space-2);
}
.account__label {
  font-size: var(--text-xs);
  color: var(--meta);
  padding-left: var(--space-2);
}
.account__select {
  width: 100%;
  /* The rail is --surface-warm, so the control has to lift off it rather
     than sit on the same value the way it did on dark. */
  background: var(--surface);
  border: 1px solid var(--border-soft);
  color: var(--fg-2);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-size: var(--text-sm);
  cursor: pointer;
}
.account__select:hover { background: var(--selected); }
.account__note { font-size: var(--text-xs); color: var(--meta); padding-left: var(--space-2); }

/* ── Main + topbar ─────────────────────────────────────────────────── */
.main { min-width: 0; display: flex; flex-direction: column; }

.topbar {
  position: sticky; top: 0; z-index: 5;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  padding: var(--space-4) var(--space-6);
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.topbar__title { font-size: var(--text-xl); font-weight: 700; }
.topbar__crumb {
  font-size: var(--text-sm);
  color: var(--muted);
  text-decoration: none;
  display: inline-flex; align-items: center; gap: var(--space-2);
}
.topbar__crumb:hover { color: var(--fg-2); text-decoration: underline; }
.topbar__spacer { margin-left: auto; }

.content { padding: var(--space-6); display: grid; gap: var(--space-5); }

/* ── Chips + badges ────────────────────────────────────────────────── */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid var(--border-soft);
  color: var(--fg);
  background: var(--surface);
}
.chip--proto { color: var(--muted); }
/* The hue is carried by the border and the dot; the label stays ink,
   because no status color clears 4.5:1 as 12px text on these surfaces. */
.chip--priority { border-color: var(--warn);    color: var(--fg-2); background: color-mix(in oklab, var(--warn) 16%, transparent); }
.chip--overdue  { border-color: var(--danger);  color: var(--fg-2); background: color-mix(in oklab, var(--danger) 14%, transparent); }
.chip--blocked  { border-color: var(--danger);  color: var(--fg-2); background: color-mix(in oklab, var(--danger) 14%, transparent); }
.chip--ok       { border-color: var(--success); color: var(--fg-2); background: color-mix(in oklab, var(--success) 14%, transparent); }
.chip--accent   { border-color: var(--accent); color: var(--fg-2); background: var(--accent-wash); }

.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--meta); }
.dot--ok { background: var(--success); }
.dot--warn { background: var(--warn); }
.dot--bad { background: var(--danger); }
.dot--accent { background: var(--accent); }

/* A stage name runs to five words, so it cannot live in a pill. The number
   carries the scan and the name wraps beneath it. */
.stagename {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  gap: var(--space-2);
  align-items: start;
}
.stagename__n {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--meta);
  text-align: right;
  padding-top: 1px;
}
.stagename__t { font-weight: 600; color: var(--fg-2); line-height: 1.3; }

.gate-code {
  display: block;
  margin-top: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted);
  letter-spacing: 0.02em;
}

/* ── Buttons ───────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 44px;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--motion-fast) var(--ease-standard);
}
.btn--primary { background: var(--accent); color: var(--accent-on); }
.btn--primary:hover { background: var(--accent-hover); color: var(--accent-on); }
.btn--secondary { background: var(--surface); color: var(--fg-2); border-color: var(--border-soft); }
.btn--secondary:hover { background: var(--surface-warm); color: var(--fg-2); }
.btn--ghost { background: transparent; color: var(--fg); }
.btn--ghost:hover { background: var(--hover); color: var(--fg-2); }
.btn--sm { min-height: 44px; padding: 8px 14px; font-size: var(--text-xs); }
.btn[disabled], .btn[aria-disabled="true"] {
  background: var(--surface);
  color: var(--meta);
  border-color: var(--border-soft);
  cursor: not-allowed;
}

/* ── Cards ─────────────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-6);
}
.card--flush { padding: 0; overflow: hidden; }
.card__head {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.card__title { font-size: var(--text-base); font-weight: 700; }
.card__body { padding: var(--space-5); display: grid; gap: var(--space-4); }
.card__spacer { margin-left: auto; }

.eyebrow {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--meta);
}

/* ── Toolbar / filters ─────────────────────────────────────────────── */
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.input {
  /* The border has to be its own value. On dark, --surface-warm was darker
     than the card and the field read as a well; on light it matches the
     card closely enough that a same-color border left the control with no
     visible boundary at all. */
  background: var(--surface-warm);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  color: var(--fg);
  min-height: 44px;
}
.input::placeholder { color: var(--meta); }
.input:focus { border-color: var(--accent); outline: none; box-shadow: var(--focus-ring); }
.input--search { min-width: 280px; flex: 1 1 280px; }
select.input { cursor: pointer; }

.toggle {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-sm); color: var(--muted); cursor: pointer;
  padding: 8px 12px; border-radius: var(--radius-sm);
  min-height: 44px;
}
.toggle:hover { background: var(--hover); color: var(--fg-2); }
.toggle input { accent-color: var(--accent); width: 16px; height: 16px; }

/* ── Table ─────────────────────────────────────────────────────────── */
.tablewrap { overflow-x: auto; }
table.grid { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
table.grid th {
  text-align: left;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--meta);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-soft);
  white-space: nowrap;
  background: var(--surface);
}
table.grid td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
table.grid tbody tr { cursor: pointer; transition: background var(--motion-fast) var(--ease-standard); }
table.grid tbody tr:hover { background: var(--hover); }
table.grid tbody tr:hover .cell-title { color: var(--fg-2); }
table.grid tbody tr:focus-visible { background: var(--selected); box-shadow: inset var(--focus-ring); }
/* Section heads inside the one board — "here now" / "coming next". */
table.grid tbody tr.grouprow,
table.grid tbody tr.grouprow:hover { cursor: default; background: none; }
table.grid tbody tr.grouprow td {
  padding-top: var(--space-6);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-soft);
}
table.grid tbody tr.grouprow:first-child td { padding-top: var(--space-3); }
.groupname {
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-2);
}
.groupnote { font-size: var(--text-xs); color: var(--muted); margin-left: var(--space-3); }

.cell-id { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--muted); white-space: nowrap; }
.cell-title { font-weight: 600; color: var(--fg); }
.cell-sub { font-size: var(--text-xs); color: var(--muted); margin-top: 2px; }
.cell-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.cell-flags { display: flex; gap: 6px; flex-wrap: wrap; }

.dwell { display: flex; align-items: center; gap: var(--space-3); min-width: 120px; }
.dwell__bar { width: 56px; height: 4px; border-radius: var(--radius-pill); background: var(--surface-warm); overflow: hidden; flex: none; }
.dwell__fill { display: block; height: 100%; background: var(--success); }
.dwell__fill--warn { background: var(--warn); }
.dwell__fill--bad { background: var(--danger); }
.dwell__val { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── Stat strip ────────────────────────────────────────────────────── */
/* auto-fit rather than a fixed count: the row carries four cards for most
   roles and five for anyone who sees value, and it has to survive a narrow
   window. Setting the count from JS wrote an inline style that outranked
   the breakpoint, so five cards stayed five columns down to 121px each. */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
}
.stat {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
}
.stat__val { font-size: var(--text-2xl); font-weight: 700; color: var(--fg-2); font-variant-numeric: tabular-nums; }
.stat__label { font-size: var(--text-xs); color: var(--muted); margin-top: 2px; }

/* ── Stage rail (12 stages) ────────────────────────────────────────── */
.stagerail { display: flex; gap: 2px; overflow-x: auto; padding-bottom: var(--space-2); }
.stagestep {
  flex: 1 1 0;
  /* Wide enough for the longest name to wrap to three lines rather than
     be abbreviated; the rail scrolls when twelve of these outrun it. */
  min-width: 104px;
  padding: var(--space-3) var(--space-2);
  border-top: 3px solid var(--border-soft);
  background: transparent;
  display: grid;
  gap: 2px;
}
.stagestep__n { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--meta); }
.stagestep__name { font-size: var(--text-xs); font-weight: 600; color: var(--muted); line-height: 1.3; hyphens: auto; }
.stagestep--done { border-top-color: var(--success); }
.stagestep--done .stagestep__name { color: var(--fg); }
.stagestep--current { border-top-color: var(--accent); background: var(--accent-wash); }
.stagestep--current .stagestep__name { color: var(--fg-2); font-weight: 700; }
.stagestep--current .stagestep__n { color: var(--fg-2); }

/* ── Definition rows ───────────────────────────────────────────────── */
.deflist { display: grid; gap: var(--space-3); }
.defrow {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: var(--space-4);
  align-items: baseline;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border);
}
.defrow:last-child { border-bottom: 0; padding-bottom: 0; }
.defrow__k { font-size: var(--text-sm); color: var(--muted); }
.defrow__v { font-size: var(--text-sm); color: var(--fg-2); }
.defrow__v--empty { color: var(--meta); font-style: italic; }

/* ── Documents ─────────────────────────────────────────────────────── */
.doclist { display: grid; gap: var(--space-2); }
.doc {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  background: var(--surface-warm);
}
.doc__name { font-size: var(--text-sm); font-weight: 600; color: var(--fg-2); }
.doc__meta { font-size: var(--text-xs); color: var(--muted); margin-top: 2px; }
.doc__ver {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-soft);
  color: var(--fg);
  flex: none;
}
.doc__spacer { margin-left: auto; }

/* ── Checklist ─────────────────────────────────────────────────────── */
.checklist { display: grid; gap: var(--space-3); }
.check {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-4);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  background: var(--surface-warm);
  transition: border-color var(--motion-fast) var(--ease-standard);
}
.check--done { border-color: var(--success); }
/* Match the box to the row it sits in — green border plus a blue tick was
   two colours saying the same thing, and it spent the accent budget on a
   state that is already unambiguous. */
.check--done .check__box { accent-color: var(--success); }
.check--locked { border-color: var(--danger); }
/* Arriving from a "what to fix" link on the record — clear the sticky
   topbar and mark which row was asked for. */
.check { scroll-margin-top: 96px; }
.check:target { border-color: var(--accent); box-shadow: var(--elev-ring); }
.check__box {
  width: 20px; height: 20px; margin-top: 1px;
  accent-color: var(--accent);
  cursor: pointer;
}
.check__box:disabled { cursor: not-allowed; }
.check__label { font-size: var(--text-sm); font-weight: 600; color: var(--fg-2); cursor: pointer; }
.check__meta { font-size: var(--text-xs); color: var(--muted); margin-top: var(--space-1); }
.check__meta strong { color: var(--fg); font-weight: 600; }
.check__actions { display: flex; gap: var(--space-2); align-items: center; }
.check__kind {
  font-size: var(--text-xs);
  font-family: var(--font-mono);
  color: var(--meta);
}

.banner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-soft);
  background: var(--surface-warm);
  font-size: var(--text-sm);
}
.banner--block { border-color: var(--danger); }
.banner--ok { border-color: var(--success); }

/* Everything that is not the status dot is the text block: title, body and
   the what-to-fix links. Stacking it lets the dot centre against the block
   as a whole, and the gap replaces the old per-child margins — as inline
   spans these ran together on one line. */
.banner > :not(.dot) {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}
.banner__title { font-weight: 700; color: var(--fg-2); }
.banner__body  { color: var(--muted); }
.banner__fix { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.fixlink {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-soft);
  background: var(--bg);
  color: var(--fg);
  font-size: var(--text-xs);
  font-weight: 600;
  text-decoration: none;
  min-height: 32px;
}
.fixlink:hover { background: var(--selected); color: var(--fg-2); }
.fixlink--money { border-color: var(--danger); }

/* ── Timeline ──────────────────────────────────────────────────────── */
.timeline { display: grid; gap: 0; }
.tl {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  gap: var(--space-4);
  padding-bottom: var(--space-5);
  position: relative;
}
.tl:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 7px; top: 16px; bottom: 0;
  width: 2px;
  background: var(--border-soft);
}
.tl__dot {
  width: 16px; height: 16px; border-radius: 50%;
  border: 3px solid var(--surface);
  background: var(--meta);
  margin-top: 2px;
  z-index: 1;
}
.tl__dot--done { background: var(--success); }
.tl__dot--current { background: var(--accent); }
.tl__dot--override { background: var(--warn); }
.tl__title { font-size: var(--text-sm); font-weight: 600; color: var(--fg-2); }
.tl__meta { font-size: var(--text-xs); color: var(--muted); margin-top: 2px; }
.tl__note {
  font-size: var(--text-sm);
  color: var(--fg);
  margin-top: var(--space-2);
  padding: var(--space-3);
  border-left: 2px solid var(--border-soft);
  background: var(--surface-warm);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

/* ── Two-column detail layout ──────────────────────────────────────── */
/* align-items:start is what lets the aside stick — a stretched grid item
   fills the row and has nowhere to travel. */
.split { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: var(--space-5); align-items: start; }

/* History, payment gates and linked records are reference material: they
   stay in view while the working column scrolls past them. The panel also
   scrolls internally, because a late-stage record carries eleven history
   entries and would otherwise outrun the viewport and stop sticking.
   Offset = 56px sticky topbar + the content column's own top padding. */
.stack--aside {
  position: sticky;
  top: calc(56px + var(--space-6));
  max-height: calc(100vh - 56px - var(--space-6) * 2);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.stack { display: grid; gap: var(--space-5); }

/* ── Misc ──────────────────────────────────────────────────────────── */
.textarea {
  width: 100%;
  min-height: 96px;
  resize: vertical;
  background: var(--surface-warm);
  border: 1px solid var(--surface-warm);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  color: var(--fg);
}
.textarea::placeholder { color: var(--meta); }
.textarea:focus { border-color: var(--accent); outline: none; box-shadow: var(--focus-ring); }

.hint { font-size: var(--text-xs); color: var(--muted); }
.money { font-variant-numeric: tabular-nums; }
.money--hidden { color: var(--meta); font-style: italic; }
.empty { padding: var(--space-12) var(--space-6); text-align: center; color: var(--muted); font-size: var(--text-sm); }
.actionbar {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.actionbar__spacer { margin-left: auto; }
.linkrow { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.linkchip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-soft);
  background: var(--surface-warm);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--fg);
  text-decoration: none;
  min-height: 32px;
}
.linkchip:hover { background: var(--selected); color: var(--fg-2); }

.toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  background: var(--surface-warm);
  color: var(--fg-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-5);
  font-size: var(--text-sm);
  box-shadow: var(--elev-raised);
  z-index: 40;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-base) var(--ease-standard),
              transform var(--motion-base) var(--ease-standard);
}
.toast.is-on { opacity: 1; transform: translateX(-50%) translateY(-4px); }

@media (max-width: 1100px) {
  .split { grid-template-columns: minmax(0, 1fr); }
  .stack--aside { position: static; max-height: none; overflow: visible; }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 860px) {
  .app { grid-template-columns: minmax(0, 1fr); }
  .rail { position: static; height: auto; flex-direction: column; }
  .content { padding: var(--space-4); }
  .topbar { padding: var(--space-4); flex-wrap: wrap; }
  .defrow { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
  .check { grid-template-columns: 24px minmax(0, 1fr); }
  .check__actions { grid-column: 2; }
}

/* ─────────────────────────────────────────────────────────────────────────
 * View transitions
 *
 * The three screens are one record moving through one shell, so the shell
 * holds still and only the working area changes. Four named regions:
 *
 *   rail / topbar   persistent chrome — morphs in place, no movement
 *   content         the working area — rises 10px as it arrives
 *   project-hero    the project name — morphs from the queue row into the
 *                   detail heading and on into the gate checklist
 *
 * Cross-document transitions need a same-origin navigation. Where the
 * browser does not support them the navigation is simply instant.
 * ─────────────────────────────────────────────────────────────────── */

@view-transition { navigation: auto; }

.rail    { view-transition-name: rail; }
.topbar  { view-transition-name: topbar; }
.content { view-transition-name: content; }

/* Exactly one element per document may carry this. */
.vt-hero { view-transition-name: project-hero; }

/* Durations are deliberately longer than the hover/toast --motion-base.
   A transition has to be legible across a whole screen of content, and at
   200ms it read as a repaint rather than a movement. */
::view-transition-group(*) {
  animation-duration: 320ms;
  animation-timing-function: var(--ease-standard);
}
::view-transition-old(*),
::view-transition-new(*) {
  animation-duration: 320ms;
  animation-timing-function: var(--ease-standard);
}

/* The working area is the only thing that travels. The outgoing copy fades
   in place rather than sliding up, so it never crosses the topbar. */
::view-transition-old(content) {
  animation: vt-fade-out 320ms var(--ease-standard) both;
}
::view-transition-new(content) {
  animation: vt-rise 320ms var(--ease-standard) both;
}

@keyframes vt-rise {
  from { opacity: 0; transform: translateY(48px); }
  to   { opacity: 1; transform: none; }
}
@keyframes vt-settle {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}
@keyframes vt-fade-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Same-document updates — a filter change, a checklist tick, a role swap.
   These settle rather than travel, but a pure crossfade between two nearly
   identical tables is invisible, so the arriving copy still moves. */
html[data-vt="update"]::view-transition-group(*),
html[data-vt="update"]::view-transition-old(*),
html[data-vt="update"]::view-transition-new(*) {
  animation-duration: 260ms;
}
html[data-vt="update"]::view-transition-old(content) {
  animation: vt-fade-out 160ms var(--ease-standard) both;
}
html[data-vt="update"]::view-transition-new(content) {
  animation: vt-settle 260ms var(--ease-standard) both;
}

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}
