/*
 * Escape Machinery — Tier-0C Workbench design tokens + shell scaffold.
 * Proprietary. Internal use only.
 *
 * Baseline: docs/workbench/design-system.md §2 (tokens), §1 (technical, precise,
 * calm, minimal-not-empty). Tokens are grouped BY ROLE so meaning — not raw
 * values — drives components. ClojureScript references token NAMES via the
 * design_system/tokens namespace (added in Stage 4), never literals.
 *
 * SCOPE (Stage 1): token scaffold + the three-region shell frame only. Semantic
 * state colours below are declared but ALWAYS paired with a glyph + text carrier
 * in components (never colour-alone — SPEC-3 §21; accessibility.md). Dark/light
 * aware; reduced-motion honoured.
 */

/* ── Light theme (default) ─────────────────────────────────────────────────── */
:root {
  /* Neutrals — surface & structure */
  --em-surface-0:      #ffffff;   /* app background          */
  --em-surface-1:      #f6f7f9;   /* panels / rails          */
  --em-surface-2:      #eceef1;   /* raised / headers        */
  --em-border-subtle:  #dfe3e8;   /* separators over borders */
  --em-border-strong:  #c3c9d1;

  --em-text-primary:   #1b1f24;
  --em-text-secondary: #4a525c;
  /* Muted/secondary text must still pass WCAG AA (≥4.5:1) on the raised product
     surfaces it sits on — surface-1/surface-2 panels, status chips, the context
     bar — not only on pure white. #6b7480 failed axe color-contrast on those
     panels (~4.0:1); #5a626d clears 4.5:1 on every light product surface while
     staying visibly muted below primary/secondary (F-4 / AX-15). Dark theme is
     already clean and is left untouched. */
  --em-text-muted:     #5a626d;
  --em-text-inverse:   #ffffff;

  --em-accent:         #2b6cb0;   /* interactive / focus     */
  --em-accent-weak:    #e4eef7;
  --em-accent-strong:  #23578c;   /* accent, pressed/hovered */

  /* Control (button) faces. DELIBERATELY darker than every surface a button can sit on
     (surface-0/1/2), so a button never matches the panel behind it. Kept as their own
     tokens rather than reusing a surface: a surface is a BACKGROUND role and a control
     face is a FOREGROUND-object role, and conflating them is exactly how the secondary
     button ended up invisible on a panel. */
  --em-control-face:        #e6e9ee;
  --em-control-face-hover:  #dce0e7;
  --em-control-face-active: #ced4dd;
  --em-control-border:      #b9c0ca;

  /* Semantic state (assertion lifecycle / edge state). ALWAYS accompanied by a
   * glyph + text label in the UI. Confidence is NOT encoded here — it is never
   * rendered as certainty (design-system.md §3, ux.confidence-not-certainty). */
  --em-state-observed:   #8a6d3b;  /* machine-observed, unreviewed  */
  --em-state-validated:  #2b6cb0;  /* human-validated               */
  --em-state-approved:   #2a7350;  /* promoted to accepted knowledge — darkened from
                                      #2f855a so the LIGHT-theme approved/live green
                                      clears WCAG AA 4.5:1 as foreground text on every
                                      light surface (4.93:1 on #eceef1, 5.34:1 on
                                      #f6f7f9, 5.73:1 on #fff). Still recognisably a
                                      state-green. DARK theme keeps #68b58a (below). */
  --em-state-disputed:   #b7791f;  /* contested                     */
  --em-state-superseded: #718096;  /* replaced                      */
  --em-state-rejected:   #9b2c2c;  /* rejected                      */
  --em-state-conflict:   #c05621;  /* contradictory evidence present*/
  --em-state-candidate:  #6b46c1;  /* possible identity, NOT merged */
  --em-state-pending:    #805ad5;  /* optimistic write in flight    */
  --em-edge-withdrawn:   #9aa1ab;  /* retired: zero live support — muted, subordinate */

  /* ── Tone map — the ONE source of truth for a status/label → colour. Every badge
   * and tag primitive (ui/status_badge, ui/tag) references these tones; the ~10
   * hand-rewritten status→colour mappings that used to live per-component (the old
   * `.em-status-*`, `.em-run-status-*`, `.em-badge-*`, `.em-ds-c-*`, `.em-chip-*`
   * families) now collapse to this single indirection. These are pure aliases of the
   * hard-won `--em-state-*` tokens above — NO new colours are introduced, so every
   * contrast fix (approved darkened for AA, the muted/secondary tokens) is inherited,
   * not re-derived. Because they alias `--em-state-*`, the dark-theme values below
   * flow through automatically (no per-tone dark override needed). Colour is ALWAYS
   * reinforcement — the primitives also carry a text label and (optional) glyph. */
  --em-tone-approved:   var(--em-state-approved);
  --em-tone-observed:   var(--em-state-observed);
  --em-tone-validated:  var(--em-state-validated);
  --em-tone-candidate:  var(--em-state-candidate);
  --em-tone-conflict:   var(--em-state-conflict);
  --em-tone-disputed:   var(--em-state-disputed);
  --em-tone-rejected:   var(--em-state-rejected);
  --em-tone-superseded: var(--em-state-superseded);
  --em-tone-pending:    var(--em-state-pending);
  --em-tone-accent:     var(--em-accent);
  --em-tone-neutral:    var(--em-text-muted);

  /* Focus ring — keyboard visibility (SPEC-3 §21) */
  --em-focus-ring: 0 0 0 2px var(--em-surface-0), 0 0 0 4px var(--em-accent);

  /* Typography — one technical sans, one monospace for identifiers/IRIs/digests */
  --em-font-sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --em-font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
  --em-fs-caption: 0.75rem;   /* metadata; never used to cram core content */
  --em-fs-body:    0.875rem;  /* the density floor — no core text below this */
  --em-fs-strong:  1rem;
  --em-fs-title:   1.25rem;   /* strong hierarchy; NO marketing-scale headings */
  --em-lh-body:    1.5;
  --em-fw-regular: 400;
  --em-fw-medium:  500;
  --em-fw-bold:    650;

  /* Spacing — a single scale; density comes from spacing + tables, not tiny text */
  --em-space-1: 4px;
  --em-space-2: 8px;
  --em-space-3: 12px;
  --em-space-4: 16px;
  --em-space-5: 24px;
  --em-space-6: 32px;

  /* Radius / elevation — sparingly; layering signal only, not decoration */
  --em-radius-sm: 3px;
  --em-radius-md: 6px;
  --em-elevation-overlay: 0 6px 24px rgba(16, 22, 30, 0.18); /* palette/sheet only */

  /* Motion — short, purposeful; fully suppressed under reduced-motion */
  --em-motion-fast: 120ms;
  --em-motion-base: 200ms;
  --em-motion-ease: cubic-bezier(0.2, 0, 0, 1);

  /* Layout metrics for the persistent 3-region shell (SPEC-3 §7) */
  --em-context-bar-h: 44px;
  --em-left-rail-w:   260px;
  --em-inspector-w:   420px;
}

/* ── Dark theme ────────────────────────────────────────────────────────────── */
@media (prefers-color-scheme: dark) {
  :root {
    --em-surface-0:      #14171b;
    --em-surface-1:      #1b1f24;
    --em-surface-2:      #232830;
    --em-border-subtle:  #2c333c;
    --em-border-strong:  #3a424d;

    --em-text-primary:   #e7eaee;
    --em-text-secondary: #b3bcc7;
    --em-text-muted:     #8a94a0;
    --em-text-inverse:   #14171b;

    --em-accent:         #63a4d8;
    --em-accent-weak:    #1f2d3a;
    --em-accent-strong:  #7fb6e2;   /* on dark, "stronger" is LIGHTER — pressed accent */

    /* Control faces invert their DIRECTION, not just their value: on dark they are
       LIGHTER than every surface (surface-0/1/2), for the same reason they are darker on
       light — a button must never be the same colour as the panel behind it. `text-inverse`
       is the dark background here, so the primary button's reversed label stays legible. */
    --em-control-face:        #333a45;
    --em-control-face-hover:  #3d4552;
    --em-control-face-active: #4a5361;
    --em-control-border:      #4d5665;

    --em-state-observed:   #c9a25f;
    --em-state-validated:  #63a4d8;
    --em-state-approved:   #68b58a;
    --em-state-disputed:   #d6a43f;
    --em-state-superseded: #97a0ac;
    --em-state-rejected:   #d78585;
    --em-state-conflict:   #e08a52;
    --em-state-candidate:  #a688e0;
    --em-state-pending:    #a688e0;
    --em-edge-withdrawn:   #7c828c;

    --em-elevation-overlay: 0 6px 24px rgba(0, 0, 0, 0.5);
  }
}

/* ── Reset / base ──────────────────────────────────────────────────────────── */
* { box-sizing: border-box; }

html, body, #app {
  height: 100%;
  margin: 0;
  /* Fixed-viewport shell: the PAGE never scrolls. This is the hard backstop for
     the vertical height-containment chain (see the "height-containment chain" note
     below): every level from #app down to `.em-tables-body` is min-height:0
     contained, so a tall table (250 / 1000+ rows) is bounded at every ancestor and
     the ONLY scroller is `.em-tables-body` (overflow:auto). Because containment is
     airtight, this overflow:hidden clips NOTHING reachable — it purely forbids a
     document-level scroll, so the tall table can no longer drag the whole page into
     a white void past the last row. Every row remains reachable inside the body
     scroller. The command palette (position:fixed) is unaffected by this. */
  overflow: hidden;
}

body {
  font-family: var(--em-font-sans);
  font-size: var(--em-fs-body);
  line-height: var(--em-lh-body);
  color: var(--em-text-primary);
  background: var(--em-surface-0);
  -webkit-font-smoothing: antialiased;
}

code, .em-mono { font-family: var(--em-font-mono); }

/* Keyboard focus is always visible and logically ordered (SPEC-3 §21). */
:focus-visible {
  outline: none;
  box-shadow: var(--em-focus-ring);
  border-radius: var(--em-radius-sm);
}

/* ── Shell scaffold (Stage 1: static frame; resizable panes land in Stage 4) ── */
/* The re-com v-box root of the fixed-viewport shell. re-com boxes carry `display:
   flex` + inline flex-flow but NEVER a `min-height`/`min-width` (verified: box-base
   only emits min-* when the prop is passed) — so a flex COLUMN re-com box does not
   let its flex child shrink below content by default. `.em-shell` is the top of the
   vertical height-containment chain; making it min-height:0 (and min-width:0) keeps
   its flex child (the body `.em-split`) bounded to the viewport minus the context
   bar, instead of growing to the table's content height. */
.em-shell            { height: 100%; min-height: 0; min-width: 0; }
.em-context-bar {
  height: var(--em-context-bar-h);
  border-bottom: 1px solid var(--em-border-subtle);
  background: var(--em-surface-2);
}
/* The full-height SURFACE + right border now live on the guaranteed-full-height
 * hosts: `.em-rail-scroll` (expanded) and `.em-rail-collapsed` (collapsed). The base
 * `.em-rail` v-box stays transparent so the host surface shows through to the bottom
 * even when its content is short. */
.em-inspector {
  border-left: 1px solid var(--em-border-subtle);
  background: var(--em-surface-1);
}
.em-workspace       { background: var(--em-surface-0); }
.em-placeholder     { color: var(--em-text-muted); }
.em-prototype-tag {
  display: inline-block;
  align-self: center;      /* do not stretch to the context-bar height */
  margin-left: auto;       /* pin to the right edge of the context bar  */
  flex: 0 0 auto;          /* size to content, never a full-width band  */
  font-size: var(--em-fs-caption);
  font-weight: var(--em-fw-medium);
  letter-spacing: 0.04em;
  line-height: 1.6;
  /* (item 10) DEMOTED from a filled purple pill. The mark is honest and required —
     this is pre-release software and must never read as production-complete — but it
     is not the product, so it is an outline, not a shout. */
  color: var(--em-text-muted);
  background: transparent;
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm);
  padding: 0 var(--em-space-2);
}

/* ── Context bar (Stage 4: server-derived session) ─────────────────────────── */
.em-brand           { font-weight: var(--em-fw-bold); }
/* ── Scope (item 8) ──────────────────────────────────────────────────────────
   ONE scope, named honestly: the tenant. The old `tenant ▸ programme` breadcrumb
   asserted a two-part narrowing that the server does not perform, and its italic
   muted `no programme selected` read as an unconfigured setting. `no programme`
   is a first-class supported state, so it is NOT styled as a warning; the full
   factual statement lives in a keyboard-operable disclosure rather than a
   hover-only title. */
.em-scope { position: relative; }
.em-scope-summary {
  display: inline-flex; align-items: baseline; gap: var(--em-space-2);
  cursor: pointer; list-style: none; border-radius: var(--em-radius-sm);
  padding: 2px var(--em-space-1);
}
.em-scope-summary::-webkit-details-marker { display: none; }
.em-scope-summary:hover { background: var(--em-control-face-hover); }
.em-scope-summary:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-scope-tenant    { font-weight: var(--em-fw-medium); color: var(--em-text-primary); }
.em-scope-qualifier {
  font-size: var(--em-fs-caption); color: var(--em-text-secondary);
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm);
  padding: 0 var(--em-space-1); white-space: nowrap;
}
.em-scope-panel {
  position: absolute; top: calc(100% + var(--em-space-1)); left: 0; z-index: 40;
  width: 46ch; max-width: 90vw; padding: var(--em-space-3);
  background: var(--em-surface-0); border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-md); box-shadow: var(--em-elevation-overlay);
}
.em-scope-panel-lead { margin: 0 0 var(--em-space-2); font-size: var(--em-fs-body); }
.em-scope-panel-note {
  margin: 0 0 var(--em-space-2); font-size: var(--em-fs-caption);
  color: var(--em-text-secondary); line-height: var(--em-lh-body);
}
.em-scope-panel-note:last-child { margin-bottom: 0; }
.em-scope-programme { color: var(--em-text-primary); font-weight: var(--em-fw-medium); }
.em-scope-sep       { color: var(--em-text-muted); }
.em-scope-error     { color: var(--em-state-rejected); font-weight: var(--em-fw-medium); }

.em-cmdk-hint {
  font-size: var(--em-fs-caption);
  color: var(--em-text-secondary);
  background: var(--em-surface-1);
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm);
  padding: 2px var(--em-space-2);
  cursor: pointer;
}

.em-live {
  font-size: var(--em-fs-caption);
  font-variant-numeric: tabular-nums;
}
.em-live-on    { color: var(--em-state-approved); }
.em-live-off   { color: var(--em-text-muted); }
.em-live-recon { color: var(--em-state-pending, #b5860b); }
.em-live-stale { color: var(--em-state-disputed, #b23c17); }

/* Manual retry affordance, shown only in the sticky `unreachable` state. */
.em-live-retry {
  font-size: var(--em-fs-caption);
  color: var(--em-text-inverse);
  background: var(--em-state-disputed, #b23c17);
  border: 1px solid transparent;
  border-radius: var(--em-radius-sm);
  padding: 2px var(--em-space-2);
  cursor: pointer;
}
.em-live-retry:hover { filter: brightness(1.08); }

.em-actor           { line-height: 1.25; }
.em-actor-name      { font-weight: var(--em-fw-medium); font-size: var(--em-fs-caption); }
.em-actor-authority { color: var(--em-text-muted); font-size: var(--em-fs-caption); }

/* ── Experience switcher (tier-0d unified-shell §7 step 3; ────────────────────────
 *    docs/ux/tier-0d-experience-switcher.md; mockup experience-switcher.html).      */
/* SINGLE state — a static, NON-interactive label (the step-3 shipping state). Not a
 * control: no button, no caret, nothing focusable. Reads as the name of the current
 * place in the breadcrumb between the brand and the tenant▸programme scope. */
.xp-static { font-weight: var(--em-fw-medium); color: var(--em-text-primary); white-space: nowrap; }

/* MULTI state — the WAI-ARIA menu button. Built + spec-complete; DORMANT until a
 * second experience registers (step 5). */
.xp-wrap { position: relative; display: inline-flex; }
.xp-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: inherit; font-size: var(--em-fs-body); font-weight: var(--em-fw-medium);
  color: var(--em-text-primary); background: var(--em-surface-1);
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm);
  padding: 3px var(--em-space-2); cursor: pointer; white-space: nowrap;
  max-width: 16rem; overflow: hidden; text-overflow: ellipsis;
}
.xp-trigger .caret { color: var(--em-text-muted); font-size: var(--em-fs-caption); }
/* open trigger reuses the shipped focus ring treatment (--em-focus-ring). */
.xp-trigger.is-open { border-color: var(--em-accent); box-shadow: var(--em-focus-ring); }
.xp-menu {
  position: absolute; top: calc(100% + 4px); left: 0; min-width: 15rem;
  background: var(--em-surface-0); border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-md); box-shadow: var(--em-elevation-overlay);
  padding: var(--em-space-1); z-index: 10;
  /* subtle open fade (design §8); the global reduced-motion rule removes it. */
  animation: em-xp-fade var(--em-motion-fast) var(--em-motion-ease);
}
@keyframes em-xp-fade { from { opacity: 0; } to { opacity: 1; } }
.xp-menu-label { font-size: var(--em-fs-caption); color: var(--em-text-muted);
  padding: var(--em-space-1) var(--em-space-2); }
.xp-item {
  display: flex; align-items: center; gap: var(--em-space-2);
  padding: var(--em-space-2); border-radius: var(--em-radius-sm);
  color: var(--em-text-primary); cursor: pointer; white-space: nowrap;
}
.xp-item .mark { width: 1em; text-align: center; color: var(--em-text-muted); }
/* --em-text-secondary (not -muted): the route text must meet 4.5:1 on the menu
 * surface in BOTH themes (dark -muted fell just under — caught by the S28 axe test). */
.xp-item .route { margin-left: auto; font-family: var(--em-font-mono);
  font-size: var(--em-fs-caption); color: var(--em-text-secondary); padding-left: var(--em-space-4); }
/* current experience: NON-COLOUR markers — glyph + bold weight (+ aria-current in
 * the DOM); colour is reinforcement only (SPEC-3 §21). */
.xp-item.is-current { font-weight: var(--em-fw-bold); }
.xp-item.is-current .mark { color: var(--em-state-validated); }
/* the roving-focus item (tabindex=0) — the shipped focus ring. */
.xp-item.is-focused { box-shadow: var(--em-focus-ring); background: var(--em-accent-weak); }
.xp-item:hover { background: var(--em-accent-weak); }

/* ── Left rail collapse ────────────────────────────────────────────────────── */
.em-rail-collapse, .em-rail-expand {
  background: transparent;
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm);
  color: var(--em-text-secondary);
  cursor: pointer;
  padding: 0 var(--em-space-2);
}
/* collapsed rail is panel-1 directly (no scroll host) → stretched full-height by
 * `.em-split-p1`; it owns its own surface + right border. */
.em-rail-collapsed { background: var(--em-surface-2); border-right: 1px solid var(--em-border-subtle); }

/* ── Graph projection status ───────────────────────────────────────────────── */
.em-projection-status {
  font-size: var(--em-fs-caption);
  color: var(--em-text-secondary);
  font-variant-numeric: tabular-nums;
}

/* ── resizable splitter (custom h-splitter — not re-com h-split) ───────────── */
/* Panel-1 width is the only dynamic value; it is applied via the React style   */
/* prop (CSSOM flex-basis) so the strict CSP needs no unsafe-inline.            */
.em-split {
  flex: 1 1 0;
  display: flex;
  flex-flow: row nowrap;
  min-width: 0;
  min-height: 0;
}
.em-split-p1,
.em-split-p2 {
  display: flex;
  min-width: 0;      /* honour the flex basis instead of content min-width */
  min-height: 0;
  overflow: hidden;
}
.em-split-p2 { flex: 1 1 0; }   /* panel-2 always fills the remainder */

.em-split-gutter {
  flex: 0 0 6px;
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: var(--em-surface-2);
  cursor: col-resize;
  touch-action: none;            /* pointer drag, never a scroll gesture */
}
/* Collapsed left rail: the gutter is removed from view + interaction, but the
   element stays in the tree so panel-2 keeps a STABLE child index (React never
   reconciles panel-2 against the gutter → the graph canvas never remounts). */
.em-split-gutter-hidden { display: none; }
.em-split-gutter:hover,
.em-split-gutter:focus-visible { background: var(--em-accent-weak); }
.em-split-gutter:focus-visible {
  outline: none;
  box-shadow: var(--em-focus-ring);
}
.em-split-handle {
  width: 2px;
  height: 24px;
  border-radius: 1px;
  background: var(--em-border-strong);
}
.em-split-gutter:hover .em-split-handle,
.em-split-gutter:focus-visible .em-split-handle { background: var(--em-accent); }

/* ── Command palette (Stage 4 skeleton) ────────────────────────────────────── */
.em-cmdk-overlay {
  position: fixed;
  inset: 0;
  background: rgba(16, 22, 30, 0.32);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
  z-index: 1000;
}
.em-cmdk-panel {
  width: min(640px, 92vw);
  background: var(--em-surface-0);
  border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-md);
  box-shadow: var(--em-elevation-overlay);
  overflow: hidden;
}
.em-cmdk-input {
  width: 100%;
  border: none;
  border-bottom: 1px solid var(--em-border-subtle);
  background: var(--em-surface-0);
  color: var(--em-text-primary);
  font-family: var(--em-font-sans);
  font-size: var(--em-fs-strong);
  padding: var(--em-space-4);
  outline: none;
}
.em-cmdk-list { list-style: none; margin: 0; padding: var(--em-space-2); max-height: 60vh; overflow-y: auto; }
.em-cmdk-item { margin: 0; }
/* group heading (presentational; the OPTIONS carry the accessible names) */
.em-cmdk-group-label {
  padding: var(--em-space-2) var(--em-space-3) var(--em-space-1);
  color: var(--em-text-secondary);
  font-size: var(--em-fs-caption);
  font-weight: var(--em-fw-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* one executable command option — the <li role=option> IS the row (no nested
   button ⇒ no nested-interactive); the combobox input keeps focus and drives
   selection via aria-activedescendant, exactly like the review-queue listbox. */
.em-cmdk-opt {
  display: flex;
  align-items: baseline;
  gap: var(--em-space-3);
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  border-radius: var(--em-radius-sm);
  padding: var(--em-space-2) var(--em-space-3);
  color: var(--em-text-primary);
  font-size: var(--em-fs-body);
  cursor: pointer;
}
/* highlight is REDUNDANT with aria-selected; the non-colour cue is the left rule. */
.em-cmdk-opt.is-active {
  background: var(--em-surface-1);
  border-color: var(--em-border-strong);
  border-left: 3px solid var(--em-accent);
}
/* a disabled command shows SUBTLY (muted + not-allowed) with a text reason — never
   colour-alone, never focus-trapping; aria-disabled marks it for assistive tech. */
.em-cmdk-opt.is-disabled { color: var(--em-text-muted); cursor: not-allowed; }
.em-cmdk-opt.is-disabled.is-active { border-left-color: var(--em-border-strong); }
.em-cmdk-opt-label { flex: 0 0 auto; }
.em-cmdk-opt-hint  { flex: 1 1 auto; color: var(--em-text-muted); font-size: var(--em-fs-caption); }
.em-cmdk-opt-reason {
  flex: 0 0 auto;
  font-size: var(--em-fs-caption);
  font-style: italic;
  color: var(--em-text-secondary);
}
.em-cmdk-footer {
  border-top: 1px solid var(--em-border-subtle);
  padding: var(--em-space-2) var(--em-space-4);
  color: var(--em-text-muted);
  font-size: var(--em-fs-caption);
}

/* ── ⌘K search results (Stage 7.5: search wired into the palette) ───────────── */
.em-cmdk-results { max-height: 44vh; overflow-y: auto; padding: var(--em-space-2); }
.em-cmdk-state {
  padding: var(--em-space-3) var(--em-space-3);
  color: var(--em-text-secondary);
  font-size: var(--em-fs-body);
}
.em-cmdk-state.is-error { color: var(--em-text-primary); display: flex; align-items: baseline; gap: var(--em-space-2); }
.em-cmdk-retry {
  background: transparent;
  border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-sm);
  color: var(--em-text-primary);
  font-family: var(--em-font-sans);
  font-size: var(--em-fs-caption);
  padding: 0 var(--em-space-2);
  cursor: pointer;
}
.em-cmdk-retry:focus-visible { outline: 2px solid var(--em-accent); outline-offset: 1px; }
.em-cmdk-listbox { list-style: none; margin: 0; padding: 0; }
.em-cmdk-result {
  display: flex;
  align-items: baseline;
  gap: var(--em-space-3);
  width: 100%;
  text-align: left;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--em-radius-sm);
  padding: var(--em-space-2) var(--em-space-3);
  color: var(--em-text-primary);
  font-family: var(--em-font-sans);
  font-size: var(--em-fs-body);
  cursor: pointer;
}
/* highlight is redundant with aria-selected — non-colour cue is the left rule. */
.em-cmdk-result.is-active {
  background: var(--em-surface-1);
  border-color: var(--em-border-strong);
  border-left: 3px solid var(--em-accent);
}
.em-cmdk-result:focus-visible { outline: 2px solid var(--em-accent); outline-offset: 1px; }
.em-cmdk-result-type {
  flex: 0 0 auto;
  min-width: 5.5rem;
  font-size: var(--em-fs-caption);
  color: var(--em-text-secondary);
  background: var(--em-surface-1);
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm);
  padding: 0 var(--em-space-2);
}
.em-cmdk-result-label { flex: 1 1 auto; }
.em-cmdk-result-status {
  flex: 0 0 auto;
  font-size: var(--em-fs-caption);
  color: var(--em-text-secondary);
}

/* ── Graph workspace (Stage 5: renderer behind the adapter) ────────────────── */
/* The re-com `box` that hosts `.em-workspace-stack` in the split pane. Like every
   re-com box it ships `display:flex` + an inline flex value from `:size "1"` but NO
   min-height/min-width — so it must be min-height:0 contained HERE, or its child
   stack (and therefore the table) can grow to content height and the containment
   leaks up the chain. overflow:hidden keeps any residual overflow inside the pane;
   the real scroller is `.em-tables-body`. */
.em-workspace { background: var(--em-surface-0); overflow: hidden; min-height: 0; min-width: 0; }
/* The centre region stacks the (always-mounted) graph host and, when an identity
   review is open, the review host. The graph host is display:none while reviewing
   so it collapses to zero size and the review fills the centre — the graph is
   preserved underneath, never unmounted, so returning needs no relayout/fit (just
   a resize() to re-measure the re-shown container). */
.em-workspace-stack {
  display: flex; flex-flow: column nowrap;
  /* fill WIDTH (via width:100%) as well as height. Use flex-basis 0 (`1 1 0`),
     NOT `auto`: an `auto` basis sizes the stack to its CONTENT, so a tall table
     (250+ rows) over-grows past the viewport and the scroll region detaches from
     the rows (white void). Basis 0 fills the parent and keeps overflow contained
     in .em-tables-body's own scroll. */
  width: 100%; flex: 1 1 0;
  height: 100%; min-height: 0; min-width: 0;
}
.em-graph-host,
.em-review-host {
  display: flex; flex-flow: column nowrap;
  flex: 1 1 0; min-height: 0; min-width: 0;
}
.em-graph-host.em-hidden { display: none; }
.em-graph-workspace {
  display: flex; flex-flow: column nowrap;
  height: 100%; min-height: 0; min-width: 0;
}
.em-graph-empty {
  margin: auto; max-width: 420px; text-align: center; color: var(--em-text-secondary);
}

/* ── Developer tools (item 1) ────────────────────────────────────────────────
   ONE default-closed disclosure holding every piece of test machinery, rendered
   ONLY in the development posture (the host mounted /dev/login). It is deliberately
   QUIET: a single small trigger, no coloured banner, no full-width bar. The two
   marked bars inside it keep their DEV tags — inside the panel that marking is
   honest and useful; outside it, it dominated the product. */
.em-devpanel { border-bottom: 1px solid var(--em-border-subtle); background: var(--em-surface-1); }
.em-devpanel-toggle {
  display: inline-flex; align-items: center; gap: var(--em-space-1);
  font: inherit; font-size: var(--em-fs-caption); color: var(--em-text-secondary);
  background: none; border: none; border-radius: var(--em-radius-sm);
  padding: var(--em-space-1) var(--em-space-3); cursor: pointer;
}
.em-devpanel-toggle:hover { color: var(--em-text-primary); background: var(--em-control-face-hover); }
.em-devpanel-toggle:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-devpanel-dot { color: var(--em-state-pending); font-size: 0.6em; }
.em-devpanel-caret { color: var(--em-text-muted); }
.em-devpanel-region { border-top: 1px solid var(--em-border-subtle); }
.em-devpanel-note {
  margin: 0; padding: var(--em-space-2) var(--em-space-3) 0;
  font-size: var(--em-fs-caption); color: var(--em-text-muted); max-width: 80ch;
}

/* dev fixtures bar — clearly marked; only ever inside the developer panel */
.em-devfix {
  display: flex; flex-flow: row wrap; align-items: center; gap: var(--em-space-2);
  padding: var(--em-space-2) var(--em-space-3);
  background: transparent;
}
.em-devfix-tag {
  font-size: var(--em-fs-caption); font-weight: var(--em-fw-bold);
  letter-spacing: 0.06em; color: var(--em-text-inverse);
  background: var(--em-state-pending); border-radius: var(--em-radius-sm);
  padding: 1px var(--em-space-2);
}
.em-devfix-btn {
  font-size: var(--em-fs-caption); color: var(--em-text-primary);
  background: var(--em-control-face); border: 1px solid var(--em-control-border);
  border-radius: var(--em-radius-sm); padding: 2px var(--em-space-2); cursor: pointer;
}
.em-devfix-btn:hover { background: var(--em-control-face-hover); }
.em-devfix-btn:active { background: var(--em-control-face-active); }
.em-devfix-btn:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-devfix-btn.em-active { border-color: var(--em-accent); background: var(--em-accent-weak); }

/* dev live-event injector (Stage 9 multi-user demo — another reviewer's action) */
.em-devfix-live {
  display: flex; flex-flow: row wrap; align-items: center; gap: var(--em-space-2);
  width: 100%; margin-top: var(--em-space-1); padding-top: var(--em-space-2);
  border-top: 1px dashed var(--em-border-subtle);
}
/* DEV-only tag. LIGHT theme carries white text (--em-text-inverse #fff); the raw
   disputed gold (#b7791f) was 3.64:1, so use a darker gold (#9c6412 → 4.95:1). DARK
   theme carries near-black text on the lighter disputed gold and was already clean,
   so it is restored to the token below. Scoped to this DEV tag — the shared disputed
   status token is untouched. */
.em-devfix-live .em-devfix-tag { background: #9c6412; }
@media (prefers-color-scheme: dark) {
  .em-devfix-live .em-devfix-tag { background: var(--em-state-disputed); }
}
.em-devfix-sep { color: var(--em-text-muted); font-size: var(--em-fs-caption); margin-left: var(--em-space-1); }

/* live activity readout (the honest "someone else changed this" log) */
.em-live-activity { font-size: var(--em-fs-caption); margin-top: var(--em-space-2); }
.em-live-activity-title {
  display: block; font-weight: var(--em-fw-bold); letter-spacing: 0.02em;
  color: var(--em-text-secondary); margin-bottom: var(--em-space-1);
}
.em-live-activity-list { list-style: none; margin: 0; padding: 0; }
.em-live-activity-list li {
  display: flex; align-items: baseline; gap: var(--em-space-1);
  margin-bottom: var(--em-space-1); line-height: 1.3;
}
.em-live-activity-text { flex: 1; }
.em-live-dismiss {
  border: none; background: none; cursor: pointer; color: var(--em-text-muted);
  font-size: var(--em-fs-body); line-height: 1; padding: 0;
}
.em-live-stale {
  color: var(--em-state-disputed, #b23c17); font-style: italic; margin: 0 0 var(--em-space-1);
}

/* Momentary flash toast — a SINGLE self-dismissing confirmation for a local action
   (e.g. clipboard copy). Fixed, bottom-centre, above the workspace but below the
   command palette (z-index 1000). The animation is suppressed under reduced-motion
   by the global rule below. Distinct from the Live-activity feed. */
.em-flash {
  position: fixed; left: 50%; bottom: var(--em-space-5); transform: translateX(-50%);
  z-index: 900; max-width: min(90vw, 480px);
  display: flex; align-items: center; gap: var(--em-space-2);
  padding: var(--em-space-2) var(--em-space-4);
  background: var(--em-surface-2); color: var(--em-text-primary);
  border: 1px solid var(--em-border-strong); border-radius: var(--em-radius-md);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
  font-size: var(--em-fs-body); pointer-events: none;
  animation: em-flash-in 140ms ease-out;
}
.em-flash-text { line-height: 1.3; }
@keyframes em-flash-in {
  from { opacity: 0; transform: translate(-50%, var(--em-space-2)); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* toolbar — every control keyboard + pointer operable */
.em-graph-toolbar {
  display: flex; flex-flow: row wrap; align-items: center; gap: var(--em-space-3);
  padding: var(--em-space-2) var(--em-space-3);
  border-bottom: 1px solid var(--em-border-subtle); background: var(--em-surface-1);
}
.em-graph-toolbar button, .em-graph-toolbar select {
  font-size: var(--em-fs-caption); color: var(--em-text-primary);
  background: var(--em-surface-0); border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm); padding: 2px var(--em-space-2); cursor: pointer;
}
/* a disabled toolbar action (e.g. "Balance here" without a single selection) reads
   SUBTLE + non-interactive — never colour alone: it also loses the pointer cursor. */
.em-graph-toolbar button:disabled {
  color: var(--em-text-muted); cursor: not-allowed; opacity: 0.6;
}
/* These are inline <label>s wrapping "Layout "/"Approved only" text + a taller
 * control (select / checkbox). As plain inline boxes the text sits on the baseline
 * while the taller control centres separately, so the label text rides ~1-2px high.
 * inline-flex + align-items:center vertically centres the text WITH its control, so
 * the whole toolbar row sits on one centred line (`.em-graph-toolbar` is already
 * align-items:center). The gap replaces the literal spaces in the label strings. */
.em-toolbar-field, .em-toolbar-toggle {
  display: inline-flex; align-items: center; gap: var(--em-space-1);
  font-size: var(--em-fs-caption); color: var(--em-text-secondary);
}

/* neighbourhood-depth stepper: [−] depth N [+]. Real buttons (inherit the toolbar
 * button styling incl. the :disabled subtle/not-allowed treatment); the value between
 * uses tabular numerals so the width does not jitter as N changes. */
.em-depth-stepper {
  display: inline-flex; align-items: center; gap: var(--em-space-1);
  font-size: var(--em-fs-caption); color: var(--em-text-secondary);
}
.em-depth-value { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* wayfinding breadcrumb trail — a nav landmark; crumbs are real buttons */
.em-breadcrumbs {
  display: flex; flex-flow: row wrap; align-items: baseline; gap: 0;
  padding: var(--em-space-1) var(--em-space-3);
  border-bottom: 1px solid var(--em-border-subtle); background: var(--em-surface-0);
  font-size: var(--em-fs-caption);
}
.em-crumb-wrap { display: inline-flex; align-items: baseline; }
.em-crumb-sep { color: var(--em-text-muted); margin: 0 var(--em-space-2); }
.em-crumb { font-size: var(--em-fs-caption); }
.em-crumb-current { font-weight: var(--em-fw-medium); color: var(--em-text-primary); }
button.em-crumb { background: none; border: none; padding: 0; cursor: pointer; }
.em-graph-count {
  margin-left: auto; font-size: var(--em-fs-caption); color: var(--em-text-secondary);
  font-variant-numeric: tabular-nums;
}

/* filters */
.em-filters { font-size: var(--em-fs-caption); }
.em-filters > summary { cursor: pointer; color: var(--em-text-secondary); }
.em-filters-body {
  position: absolute; z-index: 20; margin-top: var(--em-space-1);
  display: flex; flex-flow: row wrap; gap: var(--em-space-3);
  max-width: 560px; padding: var(--em-space-3);
  background: var(--em-surface-0); border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-md); box-shadow: var(--em-elevation-overlay);
}
.em-filter-group { border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); padding: var(--em-space-2); margin: 0; }
.em-filter-group > legend { color: var(--em-text-muted); padding: 0 var(--em-space-1); }
.em-filter-opt { display: flex; align-items: center; gap: var(--em-space-1); }
.em-filter-note { flex-basis: 100%; color: var(--em-text-muted); margin: var(--em-space-1) 0 0; }

/* main split: canvas + side rail */
.em-graph-main { display: flex; flex: 1 1 0; min-height: 0; min-width: 0; }
.em-graph-canvas {
  flex: 1 1 0; min-height: 360px; min-width: 0;
  background: var(--em-surface-0); outline: none;
}
/* The canvas is focusable so f/m/Escape work, but a border/box focus ring around
   the whole graph is visual noise — suppress it. Keyboard focus is indicated
   instead by the SELECTED node's ring + the "Selected" list + the polite aria-live
   announcement (the meaningful focus signal for a graph). (Stage-12 axe: confirm
   the in-graph selection indicator satisfies focus-visible for this application
   region; add a minimal container indicator only if it flags.) */
.em-graph-canvas:focus-visible { box-shadow: none; outline: none; }
.em-graph-side {
  flex: 0 0 240px; overflow-y: auto;
  border-left: 1px solid var(--em-border-subtle); background: var(--em-surface-1);
  padding: var(--em-space-3); display: flex; flex-flow: column nowrap; gap: var(--em-space-4);
}

/* legend (contextual) */
.em-legend-title, .em-selection-title, .em-fps-title {
  display: block; font-weight: var(--em-fw-medium); font-size: var(--em-fs-caption);
  color: var(--em-text-secondary); margin-bottom: var(--em-space-1);
}
.em-legend-row { font-size: var(--em-fs-caption); margin-bottom: var(--em-space-1); }
.em-legend-chip {
  display: inline-block; margin: 0 var(--em-space-1) var(--em-space-1) 0;
  padding: 0 var(--em-space-1); border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm); background: var(--em-surface-0);
}
/* The programme scope picker in the context-bar panel (2026-08-31). It is the ONLY
   way to put a programme in scope — a deep link cannot, because the session dies on
   page load. Every value is an existing token. */
.em-scope-picker { margin-top: var(--em-space-2); padding-top: var(--em-space-2);
  border-top: 1px solid var(--em-border-subtle); }
.em-scope-picker-l { display: block; font-size: var(--em-fs-caption);
  color: var(--em-text-secondary); margin-bottom: var(--em-space-1); }
.em-scope-picker-s { width: 100%; max-width: 34ch; font: inherit;
  padding: 2px var(--em-space-1); border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm); background: var(--em-surface-0);
  color: var(--em-text-primary); }
.em-scope-picker-n { margin: var(--em-space-1) 0 0; font-size: var(--em-fs-caption);
  color: var(--em-text-muted); }

/* A cluster member is a CONTROL, not a bullet (2026-09-01) — selecting one swaps the
   inspector to that system's own detail. Styled as a link rather than a button so a
   list of fifteen does not read as fifteen calls to action. */
.em-ds-member-btn {
  font: inherit; background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--em-accent); text-align: left;
}
.em-ds-member-btn:hover { text-decoration: underline; }

.em-legend-frontier { font-weight: var(--em-fw-bold); color: var(--em-accent); }

/* ── THE LEGEND KEY (2026-08-31) ────────────────────────────────────────────
   Each entry shows the FORM the canvas actually draws, the mark (where the state
   has one), then the word. The swatch exists because form is now the always-on
   channel: with the resting-state ✓ dropped, "approved" IS a solid border, and a
   legend that only prints the word teaches nobody what to look for.

   The swatch is DECORATIVE (aria-hidden) — the word is the carrier, so a screen
   reader reads the state name and nothing else. Every value here is an existing
   token; no new visual language. */
.em-legend-key { display: inline-flex; align-items: center; gap: var(--em-space-1); }
.em-legend-word { font-size: var(--em-fs-caption); }
.em-legend-mark { font-size: var(--em-fs-caption); line-height: 1; }

/* NODE swatch — a small box wearing the node's border treatment. */
.em-legend-swatch--node {
  display: inline-block; width: 14px; height: 11px; border-radius: 2px;
  border: 2px solid var(--em-text-secondary); background: transparent;
}
.em-legend-swatch--node.em-legend-swatch--solid     { border-style: solid; }
.em-legend-swatch--node.em-legend-swatch--dashed    { border-style: dashed; }
.em-legend-swatch--node.em-legend-swatch--double    { border-style: double; border-width: 4px; }
/* the heavy long dash the canvas draws for MIXED; `dashed` at a greater width reads
   as the longer rhythm at swatch scale, where a dash-array cannot be expressed. */
.em-legend-swatch--node.em-legend-swatch--long-dash { border-style: dashed; border-width: 3px; }
.em-legend-swatch--node.em-legend-swatch--dotted    { border-style: dotted; border-width: 1.5px; }

/* EDGE swatch — a short rule wearing the edge's line treatment. */
.em-legend-swatch--edge {
  display: inline-block; width: 18px; height: 0; align-self: center;
  border-top: 2px solid var(--em-text-secondary);
}
.em-legend-swatch--edge.em-legend-swatch--solid           { border-top-style: solid; border-top-width: 3px; }
.em-legend-swatch--edge.em-legend-swatch--long-dash       { border-top-style: dashed; border-top-width: 2px; }
.em-legend-swatch--edge.em-legend-swatch--dashed          { border-top-style: dashed; border-top-width: 1.5px; }
.em-legend-swatch--edge.em-legend-swatch--dotted          { border-top-style: dotted; border-top-width: 1.5px; }
.em-legend-swatch--edge.em-legend-swatch--dashed-strike   { border-top-style: dashed; border-top-width: 2px; }
.em-legend-swatch--edge.em-legend-swatch--solid-underlay  { border-top-style: solid; border-top-width: 3px;
                                                            box-shadow: 0 3px 0 -1px var(--em-border-subtle); }
.em-legend-swatch--edge.em-legend-swatch--conflict        { border-top-style: solid; border-top-width: 3px; }
.em-legend-swatch--edge.em-legend-swatch--withdrawn       { border-top-style: dashed; border-top-width: 1px; opacity: 0.6; }
/* NOT-LOADED IS NOT EMPTY (2026-08-31). Shown in the map stage's place when the
   surface has fetched nothing, so the counts line and the fog canvas — which would
   read as findings about the estate — are not drawn at all. */
.em-ds-notloaded {
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  min-height: 220px; padding: var(--em-space-4); text-align: center;
  border: 1px dashed var(--em-border-subtle); border-radius: var(--em-radius-sm);
  background: var(--em-surface-0);
}
.em-ds-notloaded-h { font-weight: var(--em-fw-medium); margin: 0 0 var(--em-space-1); }
.em-ds-notloaded-d { color: var(--em-text-secondary); margin: 0; max-width: 46ch; }

/* review-focus legend note (Increment 1): a bordered call-out that names what the
   columns encode for THIS claim's predicate (never a fixed verb). */
.em-legend-reviewfocus {
  border-left: 2px solid var(--em-state-validated); padding-left: var(--em-space-2);
  color: var(--em-text-secondary);
}
.em-legend-note { color: var(--em-text-muted); font-size: var(--em-fs-caption); font-style: italic; margin: var(--em-space-1) 0 0; }
/* (item 5) The legend keeps its ENCODING KEY visible and demotes its PROSE — the
   chips are consulted repeatedly, the paragraphs are read once. Keyboard-operable
   and self-announcing; nothing necessary is removed. */
.em-legend-more { margin-top: var(--em-space-2); font-size: var(--em-fs-caption); }
.em-legend-more > summary { cursor: pointer; color: var(--em-text-secondary); }
.em-legend-more > summary:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }

/* truncation — a first-class visible state */
.em-truncation {
  padding: var(--em-space-2) var(--em-space-3); font-size: var(--em-fs-caption);
  color: var(--em-text-primary); background: var(--em-accent-weak);
  border-bottom: 1px solid var(--em-border-subtle);
}
.em-truncation-glyph { color: var(--em-state-disputed); font-weight: var(--em-fw-bold); }

/* ── The FOCUS panel (item 4) ────────────────────────────────────────────────
   Three states, LABELLED SEPARATELY: the review subject (semantic), the graph
   focus (wayfinding, camera-only), and the canvas selection (interaction). A
   definition list, so the pairing is structural and announced as such — the panel
   it replaced showed one heading, `Selected`, over a canvas-selection value, and
   said `nothing selected` while the screen visibly represented an open claim. */
.em-focus { font-size: var(--em-fs-caption); }
.em-focus-title {
  display: block; font-weight: var(--em-fw-medium); font-size: var(--em-fs-caption);
  color: var(--em-text-secondary); margin-bottom: var(--em-space-2);
}
.em-focus-list { margin: 0; display: grid; grid-template-columns: 1fr; gap: 0; }
@media (min-width: 1600px) {
  /* Only a wide rail earns a two-column definition list; below that the key sits
     ABOVE its value, or a 200px rail wraps every value to five lines. */
  .em-focus-list { grid-template-columns: auto 1fr; gap: var(--em-space-1) var(--em-space-2); }
  .em-focus-val { margin: 0; }
}
.em-focus-list > .em-focus-val { margin: 0 0 var(--em-space-2); }
.em-focus-key {
  color: var(--em-text-muted); white-space: nowrap;
  text-transform: uppercase; letter-spacing: 0.03em; font-size: 0.7rem;
  padding-top: 1px;
}
.em-focus-val { margin: 0; line-height: 1.45; min-width: 0; overflow-wrap: anywhere; }
.em-focus-claim { font-weight: var(--em-fw-medium); color: var(--em-text-primary); }
.em-focus-status { color: var(--em-text-secondary); }
.em-focus-node { font-weight: var(--em-fw-medium); }
.em-focus-hint { font-style: italic; }
.em-focus-absence { line-height: var(--em-lh-body); }
.em-focus-empty { margin: 0; }

/* selection list (redundant, accessible — not colour-only) */
.em-selection { font-size: var(--em-fs-caption); }
.em-selection-nodes { list-style: none; margin: 0; padding: 0; }
.em-selection-item, .em-selection-nodes li { margin-bottom: var(--em-space-1); }
.em-selection-item { line-height: 1.5; }
/* endpoints read as node LABELS (the exact IRI is on hover), the predicate is
   the readable relation between them — never a wall of raw URL text. */
.em-selection-endpoint { font-weight: 600; }
.em-selection-predicate { color: var(--em-text-muted); }
.em-muted { color: var(--em-text-muted); }

/* fps meter (stress gate) */
.em-fps { font-size: var(--em-fs-caption); border-top: 1px solid var(--em-border-subtle); padding-top: var(--em-space-3); }
.em-fps-row { display: flex; gap: var(--em-space-2); margin-bottom: var(--em-space-1); }
.em-fps-row button {
  font-size: var(--em-fs-caption); background: var(--em-surface-0);
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm);
  padding: 2px var(--em-space-2); cursor: pointer;
}
.em-fps-readout { font-variant-numeric: tabular-nums; }
.em-fps-note { color: var(--em-text-muted); margin: var(--em-space-1) 0 0; }
.em-pass { color: var(--em-state-approved); }
.em-fail { color: var(--em-state-rejected); }

/* ── Inspector (Stage 6: resource + assertion inspection) ──────────────────── */
.em-inspector { overflow: hidden; }               /* Stage-1 border-left/bg kept */
.em-insp {
  display: flex; flex-flow: column nowrap;
  /* fill the pane WIDTH (not just height): without width:100% this flex item
     shrinks to the current tab's content width, so the pane narrows as you
     switch tabs and the graph shows through. Fill so the pane stays fixed. */
  width: 100%; flex: 1 1 auto;
  height: 100%; min-height: 0; overflow: hidden;
  font-size: var(--em-fs-body);
}
.em-insp-titlebar {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  padding: var(--em-space-2) var(--em-space-3);
  border-bottom: 1px solid var(--em-border-subtle); background: var(--em-surface-2);
}
.em-insp-titlebar-label { font-weight: var(--em-fw-medium); color: var(--em-text-secondary); }
.em-linklike {
  background: transparent; border: none; padding: 0; cursor: pointer;
  color: var(--em-accent); font: inherit; text-align: left;
}
.em-linklike:hover { text-decoration: underline; }

.em-insp-nothing { margin: auto; max-width: 300px; text-align: center; padding: var(--em-space-5); }

.em-insp-header {
  flex: 0 0 auto; padding: var(--em-space-3);
  border-bottom: 1px solid var(--em-border-subtle);
}
.em-insp-kind {
  font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--em-text-muted);
}
.em-insp-title { font-weight: var(--em-fw-bold); font-size: var(--em-fs-strong); }
.em-insp-stale {
  margin-top: var(--em-space-2); padding: var(--em-space-1) var(--em-space-2);
  font-size: var(--em-fs-caption); color: var(--em-text-primary);
  background: var(--em-accent-weak); border-radius: var(--em-radius-sm);
}

.em-insp-tabs {
  flex: 0 0 auto; display: flex; gap: var(--em-space-1);
  padding: 0 var(--em-space-2); border-bottom: 1px solid var(--em-border-subtle);
  background: var(--em-surface-1);
}
.em-insp-tab {
  background: transparent; border: none; border-bottom: 2px solid transparent;
  color: var(--em-text-secondary); cursor: pointer;
  padding: var(--em-space-2) var(--em-space-2); font: inherit;
}
.em-insp-tab-active { color: var(--em-text-primary); border-bottom-color: var(--em-accent); font-weight: var(--em-fw-medium); }

.em-insp-tabpanel { flex: 1 1 0; min-height: 0; overflow-y: auto; }
.em-insp-body { padding: var(--em-space-3); }

/* (item 10) Section rhythm: sections sit closer together and their heads carry a
   little more weight, so the eye reads the inspector as a ranked sequence rather
   than a document of equal paragraphs. Tokens only — no new values. */
.em-insp-section { margin-bottom: var(--em-space-3); }
.em-insp-h {
  margin: 0 0 var(--em-space-2); font-size: var(--em-fs-caption);
  font-weight: var(--em-fw-bold);
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--em-text-muted);
  border-bottom: 1px solid var(--em-border-subtle); padding-bottom: 2px;
}
.em-field { display: flex; gap: var(--em-space-2); margin-bottom: var(--em-space-1); }
.em-field-label { flex: 0 0 96px; color: var(--em-text-muted); }
.em-field-value { flex: 1 1 auto; min-width: 0; word-break: break-word; }
.em-insp-note { margin: var(--em-space-1) 0 0; color: var(--em-text-muted); font-size: var(--em-fs-caption); font-style: italic; }
.em-insp-empty { color: var(--em-text-muted); font-style: italic; }

/* progressive disclosure for pedagogy an expert already knows but a newcomer
   needs — native <details>/<summary>, keyboard-operable, state announced by the
   platform. The honesty explanation is demoted in visual weight, never removed. */
.em-insp-learn { margin-top: var(--em-space-1); }
.em-insp-learn > summary {
  cursor: pointer; color: var(--em-text-muted);
  font-size: var(--em-fs-caption); font-style: italic;
}
.em-insp-learn[open] > summary { margin-bottom: 2px; }
.em-insp-learn > .em-insp-note:first-of-type { margin-top: 2px; }

.em-insp-state { padding: var(--em-space-4); color: var(--em-text-secondary); }
.em-insp-error, .em-insp-missing { color: var(--em-state-rejected); }
.em-insp-gated {
  padding: var(--em-space-4); color: var(--em-text-primary);
  background: var(--em-surface-2); border-radius: var(--em-radius-sm); margin: var(--em-space-3);
}

/* ═══════════════════════════════════════════════════════════════════════════════
   CANONICAL TOKEN PRIMITIVES (design-system Slice 1, Cluster A)
   Three primitives OWN every inline badge/tag/chip. They are the single home for
   the token vocabulary that used to be re-implemented ~8 times across components:

     .em-token-row     (P3, ui/token-row) — the flex row that owns the spacing
                        BETWEEN a token and its siblings. Replaces `.em-chips`, the
                        `.em-badge { margin-left }` + its nine-selector zeroing block,
                        and the ~11 hand-rolled row containers.
                        NOT a guarantee against the "jammed badge" defect, though it
                        was once described as one. It governs only tokens that are
                        actually inside it — several call sites use their own gap-
                        bearing flex row (`.em-run-detail-title`, `.em-run-attempt-
                        head`) and never touch it — and it says
                        nothing about a token's INTERNALS, which is where the defect
                        recurred: a near-invisible box with 4px of padding let the
                        tone rule collapse onto the glyph. If you are here because a
                        badge looks jammed, check the token's own padding/border
                        first; the row gap is usually not the culprit.
     .em-status-badge  (P1, ui/status-badge) — scheme-named LIFECYCLE status. A
                        coloured left-rule + label + MANDATORY scheme text (G7) +
                        optional glyph. Replaces the status half of the old `.em-chip`
                        and ALL of `.em-run-status-*`.
     .em-tag           (P2, ui/tag) — a generic labelled token (NOT a status). Box
                        (default) or pill shape; optional glyph/mono/removable ×.
                        Replaces the generic pill, `.em-badge*`, `.em-ds-c-*`,
                        `.em-scope-tag*`, `.em-confirm-chip`, `.em-idn-chip`,
                        `.em-legend-chip`, `.em-idn-edge-state`, `.em-rq-ask-tag`,
                        `.em-cmdk-result-type`.

   Every tone below indirects through the `--em-tone-*` map (one source of truth).
   ═══════════════════════════════════════════════════════════════════════════════ */

/* P3 — token-row: the flex row that OWNS spacing. `--baseline` for rows that align
   a token against running text; default centres. */
.em-token-row { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; vertical-align: middle; }
.em-token-row--baseline { align-items: baseline; }

/* P1 — status-badge: colour is reinforcement; the label + scheme text carry meaning. */
/* The tone rule is the badge's coloured EDGE, so the box must be visible enough for it
   to read as one. With a near-invisible box and 4px of padding the rule sat right on top
   of the glyph — and since both carry the SAME tone colour, they merged into a single
   blob jammed against the neighbouring text (the recurring "jammed badge" report). The
   hairline top/right/bottom closes the box; the wider LEFT padding is what gives the
   glyph air from the rule. Vertical padding is deliberate too: with none, the rule was
   exactly text-height and read as a stray bar rather than an edge. */
.em-status-badge {
  display: inline-flex; align-items: baseline; gap: var(--em-space-1); flex-wrap: wrap;
  padding: 2px var(--em-space-2) 2px var(--em-space-3);
  border-left: 3px solid var(--em-tone-neutral);
  border-top: 1px solid var(--em-border-subtle);
  border-right: 1px solid var(--em-border-subtle);
  border-bottom: 1px solid var(--em-border-subtle);
  background: var(--em-surface-1); border-radius: var(--em-radius-sm);
}
/* the status word must never break mid-word ("Appro/ved"); only the muted
   scheme text may wrap to the next line when the value column is tight. */
.em-status-badge-label { font-weight: var(--em-fw-medium); white-space: nowrap; }
.em-status-badge-scheme { font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-status-badge-g { align-self: center; color: var(--em-tone-neutral); }
/* tone modifiers colour the left-rule + glyph only (never the label text, which
   stays primary so contrast is tone-independent). */
.em-status-badge--approved   { border-left-color: var(--em-tone-approved); }
.em-status-badge--approved   .em-status-badge-g { color: var(--em-tone-approved); }
.em-status-badge--observed   { border-left-color: var(--em-tone-observed); }
.em-status-badge--observed   .em-status-badge-g { color: var(--em-tone-observed); }
.em-status-badge--validated  { border-left-color: var(--em-tone-validated); }
.em-status-badge--validated  .em-status-badge-g { color: var(--em-tone-validated); }
.em-status-badge--disputed   { border-left-color: var(--em-tone-disputed); }
.em-status-badge--disputed   .em-status-badge-g { color: var(--em-tone-disputed); }
.em-status-badge--superseded { border-left-color: var(--em-tone-superseded); }
.em-status-badge--superseded .em-status-badge-g { color: var(--em-tone-superseded); }
.em-status-badge--rejected   { border-left-color: var(--em-tone-rejected); }
.em-status-badge--rejected   .em-status-badge-g { color: var(--em-tone-rejected); }
.em-status-badge--conflict   { border-left-color: var(--em-tone-conflict); }
.em-status-badge--conflict   .em-status-badge-g { color: var(--em-tone-conflict); }
.em-status-badge--pending    { border-left-color: var(--em-tone-pending); }
.em-status-badge--pending    .em-status-badge-g { color: var(--em-tone-pending); }
.em-status-badge--neutral    { border-left-color: var(--em-tone-neutral); }
.em-status-badge--neutral    .em-status-badge-g { color: var(--em-tone-neutral); }

.em-count-list, .em-inline-list, .em-rel-list, .em-evref-list,
.em-cand-list, .em-action-list, .em-conflict-list, .em-related-list,
.em-impact-list, .em-history-list { list-style: none; margin: 0; padding: 0; }
.em-count-list li { display: flex; align-items: center; justify-content: space-between; margin-bottom: 2px; }
.em-count { font-variant-numeric: tabular-nums; color: var(--em-text-secondary); }
.em-inline-list li { color: var(--em-text-secondary); }

/* relationships — direction is load-bearing, always shown */
.em-rel { padding: var(--em-space-1) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-rel-arrow, .em-stmt-arrow { color: var(--em-accent); font-weight: var(--em-fw-bold); }
.em-rel-self { color: var(--em-text-muted); }
.em-rel-meta { display: flex; flex-wrap: wrap; gap: var(--em-space-2); align-items: center; margin-top: 2px; }
.em-support { font-size: var(--em-fs-caption); color: var(--em-text-secondary); font-variant-numeric: tabular-nums; }

/* P2 — tag: a generic labelled token (NOT a lifecycle status). It NEVER owns its
   own outer spacing — a token-row does — so the old `.em-badge { margin-left }` base
   rule and its nine-selector zeroing block are GONE (jamming is now structurally
   impossible, not patched per-context). Box (default, radius-sm) vs pill (999px) is
   an owner-approved real distinction (squared inline annotation vs rounded facet).
   Colour is reinforcement; the label text (verbatim, caller-owned) is the carrier. */
.em-tag {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--em-fs-caption);
  padding: 0 var(--em-space-1); border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm); color: var(--em-text-secondary);
  white-space: nowrap;
}
.em-tag--pill { border-radius: 999px; padding: 1px 7px; font-weight: var(--em-fw-medium); }
.em-tag--mono { font-family: var(--em-font-mono); color: var(--em-text-primary); }
.em-tag-g { flex: none; }
/* tone modifiers set the border + text colour together (both channels move). */
.em-tag--approved   { border-color: var(--em-tone-approved);   color: var(--em-tone-approved); }
.em-tag--observed   { border-color: var(--em-tone-observed);   color: var(--em-tone-observed); }
.em-tag--validated  { border-color: var(--em-tone-validated);  color: var(--em-tone-validated); }
.em-tag--candidate  { border-color: var(--em-tone-candidate);  color: var(--em-tone-candidate); }
.em-tag--conflict   { border-color: var(--em-tone-conflict);   color: var(--em-tone-conflict); }
.em-tag--disputed   { border-color: var(--em-tone-disputed);   color: var(--em-tone-disputed); }
.em-tag--rejected   { border-color: var(--em-tone-rejected);   color: var(--em-tone-rejected); }
.em-tag--accent     { border-color: var(--em-tone-accent);     color: var(--em-tone-accent); background: var(--em-accent-weak); }
.em-tag--neutral    { border-color: var(--em-border-strong);   color: var(--em-text-muted); }
/* the removable × affordance (from the old `.em-scope-tag-x`). */
.em-tag-x {
  font: inherit; line-height: 1; color: var(--em-text-muted);
  background: transparent; border: 0; cursor: pointer; padding: 0 2px;
}
.em-tag-x:hover { color: var(--em-state-rejected); }
.em-tag-x:focus-visible { outline: none; box-shadow: var(--em-focus-ring); border-radius: var(--em-radius-sm); }

.em-evref { margin-bottom: 2px; }
.em-cand { padding: var(--em-space-2) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-cand-marker { color: var(--em-state-candidate); font-weight: var(--em-fw-bold); }
.em-cand-meta { font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-confkind { font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.04em; color: var(--em-text-muted); }

/* declared type (Increment B) — the fine machine CLAIM, distinct from the coarse
   class (the node shape). Reuses the claim aesthetic; colour is reinforcement only
   (the status chip label + scheme text carry the meaning). */
.em-type-claim-list { list-style: none; margin: 0; padding: 0; }
.em-type-claim { padding: var(--em-space-2) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-type-claim:last-child { border-bottom: none; }
.em-type-claim-head { display: flex; flex-wrap: wrap; gap: var(--em-space-1); align-items: baseline; }
.em-type-claim-label { font-weight: var(--em-fw-bold); }
.em-type-claim-meta { font-size: var(--em-fs-caption); color: var(--em-text-secondary); margin-top: 2px; }
/* the "software agent — a claim, not fact" badge spacing is now handled by the
   consistent base `.em-badge { margin-left }` rule (no per-section patch). */
.em-type-claim-conf { font-size: var(--em-fs-caption); color: var(--em-text-secondary); margin-top: 2px; }
/* declared-type table cell — text/glyph, never colour alone */
.em-tbl-declared { color: var(--em-text-secondary); }

/* ── Increment C — CONTRIBUTE surface + annotations ─────────────────────────── */
.em-contrib-panel {
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm);
  padding: var(--em-space-2); margin: var(--em-space-2) 0; background: var(--em-surface-1);
}
.em-contrib-h { margin: 0 0 var(--em-space-1); font-size: var(--em-fs-strong); }
.em-contrib-label {
  display: block; margin: var(--em-space-2) 0 2px; font-size: var(--em-fs-caption);
  color: var(--em-text-secondary);
}
/* the contribution select/reason/note controls now use the shared ui/form skin
   (.em-input / .em-select / .em-textarea) — their bespoke box rules were removed as
   redundant; only the contribution-specific label/warn/counter styles remain. */
.em-contrib-disabled { color: var(--em-text-muted); }
/* the no-secrets/PII warning — a non-colour cue (⚠ + text), reinforced by border */
.em-contrib-warn {
  border: 1px solid var(--em-state-conflict); border-radius: var(--em-radius-sm);
  padding: var(--em-space-1) var(--em-space-2); margin: var(--em-space-1) 0;
  font-size: var(--em-fs-caption); color: var(--em-text-secondary);
}
.em-contrib-counter {
  font-size: var(--em-fs-caption); color: var(--em-text-muted);
  text-align: right; font-variant-numeric: tabular-nums; margin: 2px 0;
}
.em-contrib-counter.em-contrib-over { color: var(--em-state-conflict); font-weight: var(--em-fw-bold); }
.em-contrib-pending {
  display: flex; align-items: baseline; gap: var(--em-space-2);
  padding: var(--em-space-1) var(--em-space-2); border-radius: var(--em-radius-sm);
  background: var(--em-surface-2); font-size: var(--em-fs-caption);
}
.em-contrib-outcome {
  border: 1px solid var(--em-state-conflict); border-radius: var(--em-radius-sm);
  padding: var(--em-space-2); margin: var(--em-space-1) 0;
}

.em-annotation-list { list-style: none; margin: 0; padding: 0; }
.em-annotation { padding: var(--em-space-2) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-annotation:last-child { border-bottom: none; }
.em-annotation-text { white-space: pre-wrap; word-break: break-word; }
.em-annotation-meta {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--em-space-1);
  font-size: var(--em-fs-caption); color: var(--em-text-secondary); margin: 2px 0;
}

.em-action { margin-bottom: var(--em-space-2); }
.em-action-btn {
  background: var(--em-control-face); border: 1px solid var(--em-control-border);
  border-radius: var(--em-radius-sm); padding: 2px var(--em-space-2); color: var(--em-text-secondary);
  font: inherit; cursor: pointer;
}
.em-action-btn:hover:not(:disabled) { background: var(--em-control-face-hover); border-color: var(--em-accent); }
.em-action-btn:active:not(:disabled) { background: var(--em-control-face-active); }
/* Only genuinely disabled actions (display-only / future authority-gated) read as
   non-interactive — muted, dimmed, and losing the pointer (never colour alone). */
.em-action-btn:disabled {
  color: var(--em-text-muted); cursor: not-allowed; opacity: 0.6;
}

/* assertion statement */
.em-statement { font-size: var(--em-fs-strong); margin-bottom: var(--em-space-2); }
.em-pred { color: var(--em-text-secondary); }
.em-lit { font-family: var(--em-font-mono); background: var(--em-surface-2); padding: 0 4px; border-radius: 2px; }
.em-two-axes { display: flex; gap: var(--em-space-4); flex-wrap: wrap; }

/* confidence — its OWN axis, NEUTRAL treatment (never coloured as good/bad) */
.em-conf {
  border: 1px solid var(--em-border-subtle); border-left: 3px solid var(--em-accent);
  border-radius: var(--em-radius-sm); padding: var(--em-space-2); background: var(--em-surface-1);
}
.em-conf-head { display: flex; align-items: baseline; gap: var(--em-space-2); flex-wrap: wrap; }
.em-conf-axis { font-weight: var(--em-fw-medium); }
.em-conf-caveat { font-size: var(--em-fs-caption); color: var(--em-text-muted); font-style: italic; }
.em-conf-value { font-size: var(--em-fs-title); font-variant-numeric: tabular-nums; }
.em-conf-band { font-size: var(--em-fs-caption); color: var(--em-text-muted); text-transform: uppercase; letter-spacing: 0.04em; }

/* support figures */
.em-support-block { background: var(--em-surface-1); border-radius: var(--em-radius-sm); padding: var(--em-space-2); }
.em-support-figs { display: flex; align-items: baseline; gap: var(--em-space-2); flex-wrap: wrap; }
.em-support-fig { font-variant-numeric: tabular-nums; }
.em-support-fig strong { font-size: var(--em-fs-strong); }
.em-support-sep { color: var(--em-text-muted); }
/* a token-row (`.em-token-row` owns display/gap); this shim adds only the top margin. */
.em-support-badges { margin-top: var(--em-space-1); }

/* conflict — a distinct, unmissable block (§15) */
.em-conflict-block {
  border: 1px solid var(--em-state-conflict); border-radius: var(--em-radius-sm);
  padding: var(--em-space-2); background: var(--em-surface-1);
}
.em-conflict-head { font-weight: var(--em-fw-medium); color: var(--em-state-conflict); margin-bottom: var(--em-space-1); }
.em-conflict-glyph { font-weight: var(--em-fw-bold); }
/* AC-0C-38: each contradicting claim is SELECTABLE — the claim detail + a button to
   open it and rule on it sit on one row, so "see conflict → rule on the other claim"
   is one motion. Both claims stay distinct + visible (never averaged, §15). */
.em-conflict-item {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: var(--em-space-2); padding: var(--em-space-1) 0;
  border-top: 1px solid var(--em-border-subtle);
}
.em-conflict-item:first-child { border-top: none; }
.em-conflict-claim { flex: 1 1 16rem; min-width: 0; }
.em-conflict-act { flex: 0 0 auto; white-space: nowrap; }
.em-conflict-summary { margin-top: 2px; }
.em-related-list li { padding: var(--em-space-1) 0; }

/* approved-edge impact — verbatim server prose (IRIs humanised to labels) */
.em-impact-block { background: var(--em-surface-1); border-radius: var(--em-radius-sm); padding: var(--em-space-2); }
.em-impact-head { font-weight: var(--em-fw-medium); margin-bottom: var(--em-space-1); }
.em-impact-item { padding: var(--em-space-1) 0; }
.em-impact-reason { display: block; margin-top: 2px; color: var(--em-text-secondary); }
.em-impact-edge { margin-bottom: var(--em-space-1); }
.em-impact-endpoint { font-weight: 600; }         /* label; exact IRI on hover (title) */
.em-impact-pred { color: var(--em-text-secondary); }
.em-impact-supporters .em-mono { font-size: var(--em-fs-caption); }

/* off-graph relationship → bounded "bring into view" re-root (§18 frontier) */
.em-rel-bringin { white-space: nowrap; font-size: var(--em-fs-caption); }

.em-history { margin-top: var(--em-space-3); }
.em-history > summary { cursor: pointer; color: var(--em-text-secondary); font-size: var(--em-fs-caption); }
.em-history-list { margin-top: var(--em-space-2); padding-left: var(--em-space-4); }
.em-history-list li { margin-bottom: var(--em-space-2); }

/* Attribution (ADR-0080 Q9): the id-on-demand toggle, and the server's attribution
   finding. The finding is deliberately NOT muted — "No account record" is the visible
   symptom of identity forking and the thing most likely to make somebody investigate,
   so it must not read as a missing value. */
.em-history-controls { margin-top: var(--em-space-1); }
.em-linkish {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--em-accent);
  font: inherit; font-size: var(--em-fs-caption); text-decoration: underline;
}
.em-linkish:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-insp-flag { color: var(--em-state-conflict); font-size: var(--em-fs-caption); }

.em-prov-summary { margin: 0 0 var(--em-space-1); }
.em-digest { word-break: break-all; font-size: var(--em-fs-caption); }

/* ── Stage 7: live review actions + optimistic transitions ─────────────────── */
/* generic buttons (live, unlike the disabled Stage-6 .em-action-btn) */
/* Buttons read as CONTROLS, not as text on a panel (owner, 2026-08-29: "on macOS a
   button is a solid colour so it stands out"). `--em-surface-1` is also the panel
   background, so a secondary button sitting on a panel was the SAME colour as the panel
   and disappeared into it — only its 1px border said it was pressable. It now takes a
   distinctly darker fill than any surface it can sit on, with a subtle lift and a real
   pressed state. Both themes are handled: the dark-theme block below re-points the fill
   token upward (lighter than its surfaces) rather than darker, so "stands out" survives
   the inversion instead of vanishing into the background. */
.em-btn {
  background: var(--em-control-face); border: 1px solid var(--em-control-border);
  border-radius: var(--em-radius-sm); padding: var(--em-space-1) var(--em-space-3);
  color: var(--em-text-primary); font: inherit; font-weight: var(--em-fw-medium);
  cursor: pointer;
  box-shadow: 0 1px 0 rgba(16, 22, 30, 0.04);
}
.em-btn:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--em-control-face-hover); border-color: var(--em-accent); }
/* a real pressed state: the lift collapses, so the control reads as depressed */
.em-btn:active:not(:disabled):not([aria-disabled="true"]) { background: var(--em-control-face-active); box-shadow: none; }
.em-btn:disabled {
  color: var(--em-text-muted); cursor: not-allowed;
  background: var(--em-surface-1); border-color: var(--em-border-subtle); box-shadow: none;
}
.em-btn[aria-busy="true"] { cursor: progress; }
/* the PRIMARY action is filled with the accent and reversed out — it should win against a
   secondary that is now itself solid, which a pale accent-weak tint no longer did. */
.em-btn-primary {
  border-color: var(--em-accent); background: var(--em-accent);
  color: var(--em-text-inverse); font-weight: var(--em-fw-bold);
}
.em-btn-primary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--em-accent-strong); border-color: var(--em-accent-strong); }
.em-btn-primary:active:not(:disabled):not([aria-disabled="true"]) { background: var(--em-accent-strong); }
.em-btn-primary:disabled {
  background: var(--em-surface-2); color: var(--em-text-muted); border-color: var(--em-border-subtle);
}
/* P4 button variants (ui/button) — same `.em-btn` base, so the established look is
   preserved; these only add the quiet Cancel treatment + a destructive treatment. */
.em-btn-ghost { border-color: transparent; background: transparent; }
.em-btn-ghost:hover:not(:disabled):not([aria-disabled="true"]) { border-color: var(--em-border-strong); background: var(--em-control-face); }
.em-btn-ghost { box-shadow: none; font-weight: var(--em-fw-regular); }
/* Raised control faces need the primary text contrast; the rejected border and
   filled hover state retain the destructive cue alongside the action label. */
.em-btn-danger { border-color: var(--em-state-rejected); color: var(--em-text-primary); }
.em-btn-danger:hover:not(:disabled):not([aria-disabled="true"]) {
  border-color: var(--em-state-rejected); background: var(--em-state-rejected); color: var(--em-text-inverse);
}
/* ── AN `aria-disabled` CONTROL IS NOT A LIVE CONTROL (owner, 2026-08-30) ──────
   `aria-disabled` announces the state to assistive tech and changes NOTHING visually
   on its own. A `{:variant :primary :aria-disabled true}` therefore rendered in full
   saturated accent, bold reversed text, pointer cursor AND live hover/active — the
   owner clicked one, nothing happened, and they reported the feature as broken. That
   was the correct inference from what was on screen.

   `.em-btn-inert` (emitted by `ui.button` for the aria-disabled-ONLY case) carries the
   state on FOUR channels, so it is never dimming alone:
     1. the variant FILL is dropped to the neutral surface — the single strongest
        signal, and the one that was missing: a live primary is solid accent;
     2. the border goes DASHED — a non-colour, non-opacity structural difference that
        survives monochrome and reads at a glance. It also distinguishes this state
        from a natively `:disabled` control (solid border), which is honest: this one
        is still focusable and still announces its reason;
     3. `cursor: default` — the pointer no longer promises an action;
     4. no hover and no active response (the `:not([aria-disabled])` guards above).
   The focus ring is DELIBERATELY kept: the control is still in the tab order, because
   that is the whole reason `aria-disabled` was chosen over the native attribute.
   Text stays at the readable muted token — never faded below legibility. */
.em-btn-inert,
.em-btn-inert.em-btn-primary,
.em-btn-inert.em-btn-danger,
.em-btn-inert.em-btn-ghost {
  background: var(--em-surface-1);
  color: var(--em-text-muted);
  border: 1px dashed var(--em-border-strong);
  font-weight: var(--em-fw-regular);
  box-shadow: none;
  cursor: default;
}
.em-btn-inert:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }

.em-action-live { width: 100%; text-align: left; }
.em-ra-buttons { display: flex; gap: var(--em-space-2); margin-top: var(--em-space-2); flex-wrap: wrap; }

/* ── the persistent DECISION surface (external UX review) ────────────────────
   approve/dispute/reject/re-type were below the fold on a tall assertion. This
   band is `position: sticky` pinned to the BOTTOM of the inspector's OWN scroll
   region (`.em-insp-tabpanel`, NOT the viewport). It is the last child of that
   scroller, so no explanatory content scrolls under it and nothing is hidden; the
   sections above scroll while the decision + current status stay visible. The
   opaque background + border-top carry the separation (non-colour) from the
   content that scrolls beneath. The band caps its height and scrolls internally so
   the arm→confirm / conflict card can never overrun the pane or reach the
   splitter. Presentation only — the actions are exactly review-actions'
   server-derived set. */
.em-insp-decision {
  position: sticky; bottom: 0; z-index: 2;
  padding: var(--em-space-2) var(--em-space-3) var(--em-space-3);
  background: var(--em-surface-1);
  border-top: 1px solid var(--em-border-strong);
  max-height: 60%; overflow-y: auto;
}
.em-insp-decision-status {
  display: flex; align-items: baseline; gap: var(--em-space-2);
  margin-bottom: var(--em-space-2);
}
.em-insp-decision-cap {
  font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--em-text-muted);
}

/* PENDING indicator — confirmed status + intent shown together, NEVER merged.
   The dashed border + "PENDING" tag mark optimistic (not-yet-confirmed) state
   distinctly from any confirmed chip (SPEC-3 §8; ADR-0045). */
.em-pending {
  display: flex; align-items: center; gap: var(--em-space-2); flex-wrap: wrap;
  padding: var(--em-space-2); border: 1px dashed var(--em-state-pending);
  border-radius: var(--em-radius-sm); background: var(--em-surface-1);
}
.em-pending-tag {
  font-size: var(--em-fs-caption); font-weight: var(--em-fw-bold);
  letter-spacing: 0.06em; color: var(--em-state-pending);
  border: 1px solid var(--em-state-pending); border-radius: 2px; padding: 0 var(--em-space-1);
}
.em-pending-arrow { color: var(--em-state-pending); font-weight: var(--em-fw-bold); }
.em-pending-intent { color: var(--em-state-pending); font-variant-numeric: tabular-nums; }
.em-pending-note { flex-basis: 100%; margin: var(--em-space-1) 0 0; color: var(--em-text-muted); font-size: var(--em-fs-caption); font-style: italic; }

/* outcome recovery cards — each preserves the user's work */
.em-ra-outcome { margin-top: var(--em-space-2); padding: var(--em-space-2); border-radius: var(--em-radius-sm); border: 1px solid var(--em-border-strong); background: var(--em-surface-1); }
.em-ra-outcome-head { font-weight: var(--em-fw-medium); margin-bottom: var(--em-space-1); }
.em-ra-message { margin: var(--em-space-1) 0; color: var(--em-text-secondary); }
.em-ra-conflict  { border-color: var(--em-state-conflict); }
.em-ra-conflict  .em-ra-outcome-head { color: var(--em-state-conflict); }
.em-ra-authority { border-color: var(--em-state-rejected); }
.em-ra-authority .em-ra-outcome-head { color: var(--em-state-rejected); }
.em-ra-rejected  { border-color: var(--em-state-rejected); }
.em-ra-rejected  .em-ra-outcome-head { color: var(--em-state-rejected); }
.em-ra-uncertain { border-color: var(--em-state-disputed); }
.em-ra-uncertain .em-ra-outcome-head { color: var(--em-state-disputed); }

/* version-conflict diff — proposed vs server-current, side by side */
.em-ra-diff { display: flex; gap: var(--em-space-3); flex-wrap: wrap; margin: var(--em-space-2) 0; }
.em-ra-diff-col { flex: 1 1 45%; min-width: 160px; padding: var(--em-space-2); background: var(--em-surface-2); border-radius: var(--em-radius-sm); }
.em-ra-diff-label { display: block; font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.04em; color: var(--em-text-muted); margin-bottom: 2px; }

/* confirmation card (consequence + reason before execution) */
.em-ra-confirm { padding: var(--em-space-2); border: 1px solid var(--em-accent); border-radius: var(--em-radius-sm); background: var(--em-surface-1); }
.em-ra-confirm-head { font-weight: var(--em-fw-medium); margin-bottom: var(--em-space-1); display: flex; align-items: center; gap: var(--em-space-1); flex-wrap: wrap; }
.em-ra-consequence { margin: var(--em-space-1) 0; color: var(--em-text-secondary); }
.em-ra-impact { margin: var(--em-space-1) 0; display: flex; gap: var(--em-space-2); align-items: baseline; flex-wrap: wrap; }
.em-ra-impact-reason { color: var(--em-text-secondary); font-size: var(--em-fs-caption); }
.em-ra-reason { display: block; margin: var(--em-space-2) 0; }
.em-ra-reason-label { display: block; font-size: var(--em-fs-caption); color: var(--em-text-secondary); margin-bottom: 2px; }
/* the reason textarea now carries the shared ui/form skin (.em-input .em-textarea) —
   its bespoke box rule was removed as redundant; .em-ra-reason owns only its layout. */
.em-ra-error { color: var(--em-state-rejected); font-size: var(--em-fs-caption); margin: var(--em-space-1) 0 0; }

.em-devfix-outcome { font-size: var(--em-fs-caption); color: var(--em-text-secondary); display: inline-flex; align-items: center; gap: var(--em-space-1); }
.em-devfix-outcome select { font: inherit; }

/* ── Identity-candidate review (Stage 8; SPEC-3 §14; D6) ────────────────────── */
/* The two resources are drawn as DISTINCT columns and are NEVER merged. Left and
 * right carry different accent edges so they never read as one object. */
.em-idn { display: flex; flex-direction: column; height: 100%; overflow: auto; padding: var(--em-space-3); gap: var(--em-space-3); }
.em-idn-titlebar { display: flex; align-items: center; gap: var(--em-space-3); }
.em-idn-titlebar-label { font-weight: var(--em-fw-bold); font-size: var(--em-fs-strong); }
.em-idn-body { display: flex; flex-direction: column; gap: var(--em-space-4); }
.em-idn-section { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-idn-h { margin: 0; font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.05em; color: var(--em-text-muted); }

.em-idn-framing {
  padding: var(--em-space-3); border: 1px solid var(--em-state-candidate);
  border-left: 4px solid var(--em-state-candidate); border-radius: var(--em-radius-sm);
  background: color-mix(in srgb, var(--em-state-candidate) 8%, var(--em-surface-0));
  color: var(--em-text-secondary); line-height: var(--em-lh-body);
}
.em-idn-status { font-weight: var(--em-fw-medium); }

.em-idn-score { padding: var(--em-space-2) var(--em-space-3); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); background: var(--em-surface-1); display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--em-space-2); }
.em-idn-score-value { font-size: var(--em-fs-title); font-weight: var(--em-fw-bold); font-variant-numeric: tabular-nums; color: var(--em-state-candidate); }

/* the two columns, side by side, visually distinct */
.em-idn-sides { display: flex; gap: var(--em-space-2); align-items: stretch; flex-wrap: wrap; }
.em-idn-side { flex: 1 1 320px; min-width: 280px; padding: var(--em-space-3); background: var(--em-surface-1); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-idn-record.em-idn-left  { border-top: 3px solid var(--em-accent); }
.em-idn-record.em-idn-right { border-top: 3px solid var(--em-state-observed); }
/* (`.em-idn-chips`/`.em-idn-chip` removed — identifiers/aliases now render as mono
   `.em-tag`s inside a `.em-token-row`.) */

.em-idn-versus { flex: 0 0 auto; align-self: center; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 0 var(--em-space-1); }
.em-idn-versus-mark { font-weight: var(--em-fw-bold); color: var(--em-text-muted); }
.em-idn-versus-note { font-size: var(--em-fs-caption); color: var(--em-text-muted); text-align: center; max-width: 84px; }

.em-idn-nbhd { margin-top: var(--em-space-1); border-top: 1px dashed var(--em-border-subtle); padding-top: var(--em-space-2); }
.em-idn-nbhd-head { font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.04em; color: var(--em-text-muted); margin-bottom: var(--em-space-1); }
.em-idn-nbhd-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.em-idn-nbhd-row { font-size: var(--em-fs-body); display: flex; align-items: baseline; gap: var(--em-space-1); flex-wrap: wrap; }
.em-idn-self { font-style: italic; color: var(--em-text-muted); }
.em-idn-pred { color: var(--em-text-secondary); }
.em-idn-arrow { color: var(--em-text-muted); }
/* (`.em-idn-edge-state` removed — the neighbourhood edge state is now a `.em-tag`.) */

.em-idn-reasons { display: flex; gap: var(--em-space-3); flex-wrap: wrap; }
.em-idn-reasons-col { flex: 1 1 45%; min-width: 220px; padding: var(--em-space-2); border-radius: var(--em-radius-sm); }
.em-idn-matching { background: color-mix(in srgb, var(--em-state-approved) 8%, var(--em-surface-0)); border: 1px solid color-mix(in srgb, var(--em-state-approved) 30%, var(--em-border-subtle)); }
.em-idn-conflicting { background: color-mix(in srgb, var(--em-state-conflict) 8%, var(--em-surface-0)); border: 1px solid color-mix(in srgb, var(--em-state-conflict) 30%, var(--em-border-subtle)); }
.em-idn-reasons-head { font-weight: var(--em-fw-medium); margin-bottom: var(--em-space-1); }
.em-idn-reasons-glyph { font-weight: var(--em-fw-bold); }
.em-idn-conflicting .em-idn-reasons-glyph { color: var(--em-state-conflict); }
.em-idn-reasons-list { margin: 0; padding-left: var(--em-space-4); }
.em-idn-resolve { border-top: 1px solid var(--em-border-subtle); padding-top: var(--em-space-2); }

/* visually-hidden but available to assistive tech */
.em-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Stage 10: accessible non-graph representations ────────────────────────── */

/* left rail scroll host so the queue never overflows the region */
/* `.em-rail-scroll` is the direct child of `.em-split-p1` (a flex ROW), and it paints
 * the rail surface + right border, so it must fill that pane box in BOTH axes at ANY
 * divider width, even when the queue is empty (`Review queue (0)`).
 *
 * WIDTH (the bug): in a flex row a child with no flex-grow is main-axis sized to its
 * CONTENT, so `.em-rail-scroll` stayed content-width — leaving a vertical `surface-0`
 * (white) strip to its RIGHT, between the rail content and the divider, whenever the
 * pane was wider than the content. `align-items:stretch` only stretches the CROSS
 * axis (height), never width. Fix: `flex: 1 1 0` so it GROWS to fill the pane width,
 * exactly like the sibling workspace pane (`.em-workspace`, re-com `size 1`).
 *
 * HEIGHT: no explicit `height` — `align-items:stretch` on `.em-split-p1` sizes the
 * height to the full pane box regardless of content. (Do NOT re-add `height:100%`:
 * the pane height isn't "definite" for % resolution here, so it computed to content
 * height AND disabled the stretch.)
 * Result: surface-1 reaches the divider (width) and the bottom (height) at every
 * divider position and content length. */
.em-rail-scroll {
  flex: 1 1 0;
  min-height: 0; min-width: 0;
  overflow-y: auto;
  display: flex; flex-direction: column;
  background: var(--em-surface-1);
  border-right: 1px solid var(--em-border-subtle);
}

/* graph ↔ table view toggle (top of the workspace) */
.em-view-toggle {
  display: flex; align-items: center; gap: var(--em-space-1);
  padding: var(--em-space-2) var(--em-space-3);
  border-bottom: 1px solid var(--em-border-subtle); background: var(--em-surface-2);
}
.em-view-toggle-btn {
  font-size: var(--em-fs-caption); padding: 2px var(--em-space-2);
  border: 1px solid var(--em-control-border); background: var(--em-control-face);
  color: var(--em-text-secondary); border-radius: var(--em-radius-sm); cursor: pointer;
}
.em-view-toggle-btn.em-active {
  border-color: var(--em-accent); background: var(--em-accent-weak); color: var(--em-text-primary);
  font-weight: var(--em-fw-medium);
}
.em-view-toggle-btn[disabled] { opacity: 0.5; cursor: default; }

/* ── height-containment chain (fixed-viewport shell; ONLY .em-tables-body scrolls)
   Every level below MUST be min-height:0 contained so a tall table (250 / 1000+
   rows) is bounded at each ancestor and delegates its overflow to the single
   overflow:auto leaf. The page cannot scroll (overflow:hidden on html/body/#app),
   and no row is ever clipped-and-unreachable because the leaf scroller reaches all
   of them. Level → containment:
     html/body/#app       height:100% + overflow:hidden (backstop; the page root)
     .em-shell            re-com v-box, height:100% + min-height:0  (added)
       └ .em-split (body) flex:1 1 0 + min-height:0                 (column child)
         └ .em-split-p2   flex:1 1 0 + min-height:0 + overflow:hidden
           └ .em-inner-split → .em-split-p1 (workspace pane)  min-height:0 + overflow:hidden
             └ .em-workspace    re-com box, min-height:0 + overflow:hidden (added)
               └ .em-workspace-stack  flex:1 1 0 + min-height:0    (column)
                 └ .em-table-host     flex:1 1 0 + min-height:0
                   └ .em-tables       flex:1 1 0 + min-height:0 + overflow:hidden
                     └ .em-tables-body flex:1 1 0 + min-height:0 + OVERFLOW:AUTO  ← the one scroller
   The two previously-missing links were the re-com wrappers `.em-shell` and
   `.em-workspace` (re-com never sets min-height); with those closed the chain is
   airtight, which is what makes the overflow:hidden page backstop safe. */

/* the table host mirrors the graph host — it FILLS the workspace pane (no dead
   strip between the table and the inspector): full width + flex stretch top to
   bottom, and the <table> takes the full pane width. */
.em-table-host { display: flex; flex-flow: column nowrap; flex: 1 1 0; width: 100%; min-height: 0; min-width: 0; }
.em-tables { display: flex; flex-flow: column nowrap; flex: 1 1 0; width: 100%; height: 100%; min-height: 0; min-width: 0; overflow: hidden; }
.em-tables-head {
  display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: var(--em-space-2);
  width: 100%; padding: var(--em-space-2) var(--em-space-3); border-bottom: 1px solid var(--em-border-subtle);
}
.em-tables-title { font-size: var(--em-fs-strong); font-weight: var(--em-fw-medium); }
.em-tables-controls { display: flex; align-items: center; gap: var(--em-space-3); }
.em-tbl-status {
  display: flex; flex-wrap: wrap; gap: var(--em-space-3); align-items: center;
  padding: var(--em-space-1) var(--em-space-3); font-size: var(--em-fs-caption);
  color: var(--em-text-secondary); font-variant-numeric: tabular-nums;
}
.em-tbl-count-summary { font-variant-numeric: tabular-nums; }
/* No vertical padding on the scroll container: with padding-top the sticky
   `top:0` header leaves a gap above it where a scrolling row peeks through. */
.em-tables-body { flex: 1 1 0; width: 100%; min-height: 0; min-width: 0; overflow: auto; padding: 0 var(--em-space-3); }

/* the tables themselves — semantic <table>, calm borders, tabular numerics. The
   table fills the pane; the primary (Resource/Relationship/Claim) column absorbs
   the slack while the numeric/state columns stay sized to their content. */
.em-tbl { width: 100%; border-collapse: collapse; font-size: var(--em-fs-body); table-layout: auto; }
.em-tbl th {
  text-align: left; font-weight: var(--em-fw-medium); color: var(--em-text-secondary);
  border-bottom: 1px solid var(--em-border-strong); padding: var(--em-space-1) var(--em-space-2);
  /* z-index so the sticky header stacks ABOVE the scrolling rows (otherwise the
     <td> cells paint over/through it as they scroll past). */
  position: sticky; top: 0; z-index: 2; background: var(--em-surface-0); white-space: nowrap;
}
.em-tbl td { padding: var(--em-space-1) var(--em-space-2); border-bottom: 1px solid var(--em-border-subtle); vertical-align: top; }
.em-tbl-row.em-tbl-selected > td { background: var(--em-accent-weak); }
/* the primary column takes the remaining width; others shrink to content */
.em-tbl-primary { width: 100%; min-width: 220px; }
/* sortable column header — a real <button>; indicator is glyph + the column text */
.em-th-sort {
  display: inline-flex; align-items: baseline; gap: var(--em-space-1);
  background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit;
  font-weight: var(--em-fw-medium); cursor: pointer;
}
.em-th-sort:hover { text-decoration: underline; }
.em-th-sort-ind { font-variant-numeric: tabular-nums; color: var(--em-text-muted); }
.em-th-sorted .em-th-sort-ind { color: var(--em-text-secondary); }
.em-tbl-rowbtn {
  display: inline-block; text-align: left; width: 100%;
  background: none; border: 0; padding: 2px 0; cursor: pointer;
  color: var(--em-text-primary); font: inherit;
}
.em-tbl-rowbtn:hover .em-tbl-rowlabel { text-decoration: underline; }
/* nodes-table row: breathing room between the resource name and its trailing tags
 * (⧉ candidate / ⊕ more / ⌂ anchor / review-role). The row button is `inline-block`
 * (not a flex/token-row), so the tags — which are direct children of the button —
 * need a scoped left margin. SCOPED to a `.em-tag` that is a direct child of a row
 * button (only the nodes/edge tables), so it never touches token-rows elsewhere. */
.em-tbl-rowbtn > .em-tag { margin-left: var(--em-space-2); }
.em-tbl-rowlabel { font-weight: var(--em-fw-medium); }
.em-tbl-pred { color: var(--em-text-secondary); }
/* subject/object rendered as a human label; the exact IRI is on hover (title) */
.em-tbl-endpoint { font-weight: var(--em-fw-medium); }
/* (`.em-tbl-frontier`/`.em-tbl-reviewrole` removed — the boundary '⊕ more' tag now
   uses the `.em-tag--accent` tone, and the review-role mirror uses `.em-tag--validated`.) */
.em-tbl-expand { margin-left: var(--em-space-2); white-space: nowrap; font-size: var(--em-fs-caption); }
.em-tbl-arrow { color: var(--em-accent); font-weight: var(--em-fw-bold); }
.em-tbl-glyph { font-weight: var(--em-fw-bold); }
/* State / Support / Counts / Conflict cells stay NORMAL table-cells so the column
   model honours their min-width (a `display:flex` <td> is removed from the column
   model and collapses to its longest word — the per-word-wrap regression). The gap'd
   flex ROW lives in an inner `.em-tbl-cellflex` div. Readable floors keep the State
   label ("Approved (+ unapproved support)") + its badges on 1–2 lines; the primary
   column still absorbs the slack (it keeps `width:100%`), so the table fills the pane
   without starving the other columns. */
.em-tbl-cellflex { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--em-space-2); }
.em-tbl-state    { min-width: 15rem; white-space: normal; }
.em-tbl-support  { min-width: 9rem; white-space: normal; }
.em-tbl-conflict { min-width: 7.5rem; white-space: nowrap; }
.em-tbl-counts   { min-width: 8rem; }
.em-tbl-support, .em-tbl-counts, .em-tbl-asrt { font-variant-numeric: tabular-nums; font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-tbl-count { white-space: nowrap; }

/* AC-0C-38: the Conflict column is an ACTIONABLE control, not a dead-end badge —
   a link-like button that jumps to ruling on the claims. The badge text + glyph
   carry the meaning; the "rule ▸" cue signals it acts. */
.em-tbl-conflict-act {
  display: inline-flex; align-items: baseline; gap: var(--em-space-1);
  background: none; border: 0; padding: 0; margin: 0; font: inherit; cursor: pointer;
}
.em-tbl-conflict-act:hover .em-tbl-conflict-cue,
.em-tbl-conflict-act:focus-visible .em-tbl-conflict-cue { text-decoration: underline; }
.em-tbl-conflict-cue { color: var(--em-accent); font-weight: var(--em-fw-medium); white-space: nowrap; }

/* path list + claims note */
/* Path is a reading LIST — a readable max-width is fine. Claims is a TABLE — it
   should FILL the pane like Nodes/Relationships (the max-width was starving it). */
/* Path tab fills the panel like Nodes/Relationships/Claims (was capped at 900px, so
 * its rows + per-row borders/selection background stopped short of the panel edge). */
.em-tbl-path { width: 100%; }
.em-tbl-claims { width: 100%; }
.em-path-list { list-style: none; margin: 0; padding: 0; width: 100%; }
.em-path-hop { width: 100%; padding: var(--em-space-1) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-path-hop.em-tbl-selected { background: var(--em-accent-weak); }
.em-path-n { color: var(--em-text-muted); font-variant-numeric: tabular-nums; }

/* review-queue list (Work rail) */
.em-rq { display: flex; flex-flow: column nowrap; gap: var(--em-space-2); }
.em-rq-head { display: flex; align-items: baseline; justify-content: space-between; }
.em-rq-title { font-weight: var(--em-fw-medium); }
.em-rq-ordering {
  font-size: var(--em-fs-caption); color: var(--em-text-muted); margin: 0;
  line-height: 1.4;
}
.em-rq-filters { font-size: var(--em-fs-caption); }
.em-rq-filters > summary { cursor: pointer; color: var(--em-text-secondary); }
.em-rq-list { list-style: none; margin: 0; padding: 0; display: flex; flex-flow: column nowrap; gap: var(--em-space-2); }
.em-rq-item { border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-md); background: var(--em-surface-0); }
.em-rq-item.em-tbl-selected { border-color: var(--em-accent); box-shadow: 0 0 0 1px var(--em-accent); }

/* ── card WASH — the decision being asked, as a pastel tint (owner, 2026-08-29) ──
   The wash is keyed to the assertion STATUS, because the status is the KIND OF WORK:
   an Observed claim needs a first review; a Validated one is waiting on approval; a
   Disputed one needs resolving. Colour is REINFORCEMENT only (SPEC-3 §21) — the status
   badge names the status and its scheme in text, and the conflict signal keeps its own
   glyph and count. Both channels are derived from the SAME `--em-tone-*` token the badge
   uses, so the card and its badge speak ONE colour language.

   `color-mix` against `--em-surface-0` is what keeps this a single palette: in the light
   theme it lands a ~6% pastel over white; in the dark theme surface-0 is near-black, so
   the same rule yields a correspondingly deep tint with no second set of tokens to keep
   in sync. The 3px left stripe adds a SHAPE channel at the card's edge, which is what
   separates the two warm states (observed sand vs disputed amber) for a reader who
   cannot separate them by hue. A status the palette does not name gets NO wash — an
   unwashed card is the honest default, never a guessed colour. */
.em-rq-item[data-wash] {
  background: color-mix(in srgb, var(--em-wash-tone) 6%, var(--em-surface-0));
  border-left: 3px solid color-mix(in srgb, var(--em-wash-tone) 45%, var(--em-surface-0));
}
.em-rq-item[data-wash="observed"]   { --em-wash-tone: var(--em-tone-observed); }
.em-rq-item[data-wash="validated"]  { --em-wash-tone: var(--em-tone-validated); }
.em-rq-item[data-wash="approved"]   { --em-wash-tone: var(--em-tone-approved); }
.em-rq-item[data-wash="disputed"]   { --em-wash-tone: var(--em-tone-disputed); }
.em-rq-item[data-wash="rejected"]   { --em-wash-tone: var(--em-tone-rejected); }
.em-rq-item[data-wash="superseded"] { --em-wash-tone: var(--em-tone-superseded); }
/* selection still wins: a selected card keeps the accent border on all four sides, so
   "which card am I on" is never ambiguous against the wash's own left stripe. */
.em-rq-item[data-wash].em-tbl-selected { border-left-color: var(--em-accent); }
/* A forced-colours (Windows high contrast) user gets NO decorative wash: the tint and
   stripe are dropped so the OS palette drives, and the badge text carries the status. */
@media (forced-colors: active) {
  .em-rq-item[data-wash] { background: Canvas; border-left-color: CanvasText; }
}
/* The row IS the interactive listbox option (role="option" + roving tabindex);
   guarantee a visible focus ring even under programmatic (arrow-key) focus (AX-02). */
.em-rq-item[role="option"] { cursor: pointer; }
.em-rq-item:focus { outline: none; box-shadow: var(--em-focus-ring); }
.em-rq-btn {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; padding: var(--em-space-2); font: inherit; color: var(--em-text-primary);
}
/* ── PRIMARY zone — the facts the reviewer scans at a glance ─────────────────── */
/* the affected-resources title is the card's anchor: strongest weight, read first */
.em-rq-summary { font-weight: var(--em-fw-bold); line-height: 1.3; margin: 0 0 var(--em-space-1); color: var(--em-text-primary); }
/* THE ASK — the decision the reviewer must make; prominent (a CTA, never an
   authority claim), second only to the title */
/* ── Queue card (item 3) ─────────────────────────────────────────────────────
   The card carries what DISCRIMINATES between rows. Facts every shown row agrees
   on are printed once in `.em-rq-shared` and dropped from the cards; the moment a
   fact stops being shared it returns to every card. Nothing is hidden per-item. */
.em-rq-meta {
  font-size: var(--em-fs-caption);
  margin-top: var(--em-space-1);
  flex-wrap: wrap;
}
.em-rq-conflict {
  display: flex; align-items: baseline; gap: var(--em-space-1);
  margin-top: var(--em-space-1);
  font-size: var(--em-fs-caption); font-weight: var(--em-fw-medium);
  color: var(--em-state-conflict);
}
.em-rq-conflict-glyph { font-weight: var(--em-fw-bold); }
.em-rq-conf { color: var(--em-text-secondary); font-variant-numeric: tabular-nums; }
.em-rq-reason { margin-top: var(--em-space-1); font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-rq-source { margin-top: 2px; font-size: var(--em-fs-caption); }

/* The ranking basis, disclosed on SELECTION (§12) — a nested <details> is
   impossible inside a role=option, so the selected row is the disclosure. */
.em-rq-selected-detail {
  margin-top: var(--em-space-2); padding-top: var(--em-space-2);
  border-top: 1px solid var(--em-border-subtle);
  font-size: var(--em-fs-caption); color: var(--em-text-muted);
}
.em-rq-priority { font-variant-numeric: tabular-nums; font-weight: var(--em-fw-medium); color: var(--em-text-secondary); }
.em-rq-reasons { display: inline; list-style: none; margin: 0; padding: 0; }
.em-rq-reason-item { display: inline; }
.em-rq-reason-item::before { content: " \00b7 "; color: var(--em-text-muted); }
.em-rq-conf-detail { margin-top: var(--em-space-1); color: var(--em-text-muted); }

/* Shared facts — said ONCE, at queue level. */
.em-rq-shared {
  padding: var(--em-space-2); border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-md); background: var(--em-surface-1);
  font-size: var(--em-fs-caption); line-height: 1.4;
}
.em-rq-shared-state { flex-wrap: wrap; }
.em-rq-shared-lead { color: var(--em-text-muted); }
.em-rq-shared-reason { color: var(--em-text-secondary); }
.em-rq-shared-ask {
  display: block; margin: var(--em-space-1) 0 0;
}
.em-rq-ask-tag {
  font-size: var(--em-fs-caption); font-weight: var(--em-fw-bold);
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--em-accent-strong); margin-right: var(--em-space-1);
}
.em-rq-ask-text { color: var(--em-text-primary); }
.em-rq-shared-detail { margin-top: var(--em-space-2); }
.em-rq-shared-detail > summary { cursor: pointer; color: var(--em-text-secondary); }
.em-rq-shared-detail > summary:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-rq-shared-prio, .em-rq-shared-conf {
  margin: var(--em-space-1) 0 0; color: var(--em-text-muted);
}
.em-rq-empty { display: flex; flex-flow: column nowrap; gap: var(--em-space-2); align-items: flex-start; }

/* provenance timeline (inspector) — an accessible ordered <ol> */
.em-prov-timeline { list-style: none; margin: 0; padding: 0; }
.em-prov-step {
  display: grid; grid-template-columns: 1.2em 1fr; gap: var(--em-space-1) var(--em-space-2);
  padding: var(--em-space-1) 0; border-left: 2px solid var(--em-border-subtle);
  padding-left: var(--em-space-2); margin-left: 4px;
}
.em-prov-glyph { color: var(--em-accent); font-weight: var(--em-fw-bold); }
.em-prov-step-label { font-weight: var(--em-fw-medium); }
.em-prov-step-detail { grid-column: 2; font-size: var(--em-fs-caption); color: var(--em-text-secondary); }

/* ── Tier-0D Discovery Frontier — catalogue (Stage 3) ───────────────────────
   Reuses the Tier-0C tokens; the card treatment the profile/target catalogue needs
   (the `.em-card*` + `.em-frontier-*` classes) lives here, not in the workbench. */
.em-frontier-catalogue {
  padding: var(--em-space-5);
  height: 100%; overflow-y: auto;
  /* size-query container: the two-column split responds to the ACTUAL middle-pane width
     (splitter-controlled), not the viewport — so it stays correct when the launch inspector
     is resized. Below the threshold it collapses to one stacked column.
     `width: 100%` is REQUIRED here: the catalogue is a flex item (of `.em-workspace`), and
     inline-size containment overrides the default flex stretch — without an explicit width
     the container would collapse to its padding. 100% resolves against the flex parent. */
  width: 100%;
  container-type: inline-size;
}
/* Asymmetric catalogue: the short BYO stepper sits in a fixed-ish LEFT SIDEBAR (~340–400px),
   and the profile/drafts/targets sections take the wide MAIN column (1fr). The stepper is a
   5-step checklist (short), so a narrow sidebar is right for it, while the main column is wide
   enough for the profile card grid to show 2–3 cards per row (no jammed single-column stack).
   DOM order is stepper → sections, so reading order + the single-column collapse are correct.
   We only split when the MAIN column itself has room for ≥2 cards (≥1100px container); below
   that it collapses to ONE column where the profile grid already fills full-width (2+ cards),
   so the jammed-narrow-column case never occurs. */
.em-frontier-cat-grid {
  display: grid; grid-template-columns: 1fr; gap: var(--em-space-6);
  align-items: start;
}
.em-frontier-cat-primary, .em-frontier-cat-secondary { min-width: 0; }
.em-frontier-cat-secondary {
  display: flex; flex-direction: column; gap: var(--em-space-6);
}
@container (min-width: 960px) {
  .em-frontier-cat-grid { grid-template-columns: minmax(300px, 360px) 1fr; }
  /* keep the short guide in view while the tall main column scrolls (its content is short,
     so it never overflows the viewport). Only in two-column mode — a sticky stepper over a
     single stacked column would cover the sections it sits above. */
  .em-frontier-cat-primary { position: sticky; top: var(--em-space-5); align-self: start; }
}
/* The profile card list is an auto-fill grid (base `.em-card-list`) — let it FILL the
   middle pane so a wide viewport shows 2–3+ cards per row instead of a narrow capped
   column with a dead right gutter. The Targets/Drafts tables already fill (width:100%).
   Prose stays readable via measure caps (the stepper step text + the term cue are capped),
   so only the CARD/TABLE content stretches — never long lines of text. Within the two-column
   catalogue the profile grid fills its (right) column — 1–2 cards per row per the auto-fill. */
.em-frontier-catalogue .em-card-list { max-width: none; }

/* left rail — Frontier navigation (mirrors the workbench rail treatment) */
.em-rail-inner { display: flex; flex-direction: column; gap: 2px; padding: var(--em-space-3); }
.em-rail-head {
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--em-text-muted); font-weight: var(--em-fw-bold);
  padding: var(--em-space-1) var(--em-space-2) var(--em-space-2);
}
.em-frontier-nav {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--em-space-2);
  padding: var(--em-space-2); border-radius: var(--em-radius-sm);
  font-size: var(--em-fs-body); color: var(--em-text-secondary); cursor: default;
}
.em-frontier-nav.em-active {
  background: var(--em-accent-weak); color: var(--em-text-primary);
  font-weight: var(--em-fw-medium);
}
.em-frontier-nav-note {
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--em-text-muted); border: 1px solid var(--em-border-subtle);
  border-radius: 999px; padding: 0 6px;
}

/* right inspector — the moving detail panel (empty placeholder for Stage 3) */
.em-frontier-inspector-empty { padding: var(--em-space-5); }
.em-frontier-inspector-title {
  font-weight: var(--em-fw-bold); margin: 0 0 var(--em-space-1); color: var(--em-text-secondary);
}
.em-frontier-inspector-empty .em-muted { margin: 0; line-height: 1.5; }
.em-frontier-panel { display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-frontier-panel > .em-tables-title { margin: 0; }

/* profile cards — a responsive grid, not a bulleted dump */
.em-card-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: var(--em-space-4);
}
.em-card {
  background: var(--em-surface-0);
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-md);
  padding: var(--em-space-4);
  display: flex; flex-direction: column; gap: var(--em-space-3);
}
.em-card:hover { border-color: var(--em-border-strong); }
.em-card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--em-space-3);
}
.em-card-title { font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); line-height: 1.25; }
.em-card > .em-muted { margin: 0; line-height: 1.45; }
.em-card-meta {
  display: grid; grid-template-columns: max-content 1fr;
  gap: var(--em-space-1) var(--em-space-4);
  margin: 0; font-size: var(--em-fs-caption);
  padding: var(--em-space-3) 0;
  border-top: 1px solid var(--em-border-subtle);
  border-bottom: 1px solid var(--em-border-subtle);
}
.em-card-meta > div { display: contents; }
.em-card-meta dt {
  color: var(--em-text-muted); text-transform: uppercase;
  letter-spacing: 0.04em; font-size: 0.66rem; align-self: baseline;
}
.em-card-meta dd { margin: 0; color: var(--em-text-primary); }
.em-card-foot {
  display: flex; align-items: center; gap: var(--em-space-3);
  flex-wrap: wrap; font-size: var(--em-fs-caption);
}

/* the server-evaluated compatible-profiles cell in the target table */
.em-tbl-compat { vertical-align: top; }
.em-frontier-compat-list {
  list-style: none; margin: var(--em-space-1) 0 0; padding: 0;
  font-family: var(--em-font-mono); font-size: var(--em-fs-caption);
}
.em-frontier-compat-list li { margin-bottom: 2px; color: var(--em-text-secondary); }
.em-frontier-catalogue .em-tbl td { vertical-align: top; }
/* Target name cell: lay the label + its kind/blocked badges out with real breathing
 * room (name↔badge and badge↔badge), wrapping cleanly on a narrow column instead of
 * crowding against the name. Scoped to the frontier targets table, so the profile
 * cards' badges are untouched. */
/* The cell stays a real table-cell (so it stretches to the row height and its border
 * aligns at the row bottom); the flex label+badge layout lives in an INNER wrapper.
 * (display:flex on the <td> itself breaks the table row-height model -> mid-row borders.) */
.em-frontier-catalogue .em-tbl-primary { min-width: 200px; }
.em-frontier-catalogue .em-tbl-primary-inner {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: var(--em-space-2) var(--em-space-3);
}

/* ── Tier-0D Discovery Frontier — credential step (Stage 4) ─────────────────
   Launch step ③, rendered in the inspector. Reuses the `.em-card*` layout + the
   canonical token primitives (`.em-tag` / `.em-status-badge` / `.em-token-row`);
   non-colour state channels throughout (glyph + text carry selection / eligibility /
   warnings — colour is only reinforcement). */

/* arm affordance in the catalogue's compatible-profiles cell */
/* The Arm control is a BUTTON and now looks like one. It was `background: transparent`
   over a white cell — the case the owner spotted: a control that is the same colour as
   what it sits on, readable only by its 1px border. It also wrapped "Arm — SAP" over
   three lines because a mono caption in a shrink-to-fit column has nowhere to go, so the
   label is kept on one line and the column is given room to widen instead. */
.em-frontier-arm-btn {
  display: inline-block; width: 100%;
  font: inherit; font-size: var(--em-fs-caption); font-family: var(--em-font-mono);
  color: var(--em-accent); background: var(--em-control-face);
  border: 1px solid var(--em-control-border); border-radius: var(--em-radius-sm);
  padding: 1px var(--em-space-2); margin-bottom: 2px; cursor: pointer; text-align: left;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  box-shadow: 0 1px 0 rgba(16, 22, 30, 0.04);
}
.em-frontier-arm-btn:hover { border-color: var(--em-accent); background: var(--em-control-face-hover); }
.em-frontier-arm-btn:active { background: var(--em-control-face-active); box-shadow: none; }
/* give the Launch column enough room that the label is not truncated in practice */
.em-tbl-compat { min-width: 168px; }
.em-frontier-arm-btn:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-frontier-arm-hint {
  font-family: var(--em-font-mono); font-size: var(--em-fs-caption);
  color: var(--em-accent); white-space: nowrap;
}

/* ── Design-system form controls (P5, ui/form) ──────────────────────────────
   Token-driven skins over REAL native controls — the elements stay focusable,
   labelled and keyboard-operable; only their appearance is owned. Light/dark aware
   via the `--em-*` custom properties (no hard-coded colours). Shared by any form;
   the frontier declare/upload/approve forms are the first adopters (Slice 2–3).
   NOTE: `.em-form-field` is a DISTINCT class from the inspector's horizontal
   `.em-field` — form fields stack label-over-control, so it never inherits the
   inspector's 96px label column. */
.em-form-field { display: flex; flex-direction: column; gap: var(--em-space-1); margin: 0; }
.em-form-field > label {
  font-weight: var(--em-fw-medium); color: var(--em-text-secondary);
}
.em-req { color: var(--em-state-rejected); font-weight: var(--em-fw-bold); }
.em-field-hint  { margin: 2px 0 0; font-size: var(--em-fs-caption); }
/* A field error must not be distinguishable from the HINT above it by colour alone —
   in monochrome, at 200% zoom, or to a person who cannot tell the two greys apart,
   two caption paragraphs under one control read as two descriptions. The left rule is
   the same "this is a problem" shape the rest of the product already uses
   (`.em-admin-state--problem`, `.em-login-panel--problem`), so it adds a channel
   without adding a vocabulary. SHARED PRIMITIVE — this changes every form in the
   product, deliberately, and is flagged for design-system review. */
.em-field-error {
  margin: var(--em-space-1) 0 0; padding-left: var(--em-space-2);
  border-left: 3px solid var(--em-state-rejected);
  font-size: var(--em-fs-caption); color: var(--em-state-rejected);
  line-height: var(--em-lh-body);
}

.em-input, .em-select {
  font: inherit; color: var(--em-text-primary);
  background: var(--em-surface-0);
  border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-sm);
  padding: var(--em-space-2) var(--em-space-3);
  width: 100%; max-width: 100%;
}
.em-input:hover, .em-select:hover { border-color: var(--em-accent); }
.em-input:focus-visible, .em-select:focus-visible {
  outline: none; border-color: var(--em-accent); box-shadow: var(--em-focus-ring);
}
.em-input::placeholder { color: var(--em-text-muted); }
.em-input:disabled, .em-select:disabled {
  color: var(--em-text-muted); background: var(--em-surface-1); cursor: not-allowed;
}
/* keep the platform dropdown indicator (theme-correct + accessible); own only the box */
.em-select { cursor: pointer; }

/* multi-line variant of the .em-input skin (ui/form textarea) */
.em-textarea { width: 100%; max-width: 100%; resize: vertical; min-height: 3.5em; line-height: var(--em-lh-body); }

/* Styled REAL file input — the native "Choose File" chrome was the ugliest control.
   The dashed frame reads as an upload zone; `::file-selector-button` re-skins the
   in-place native trigger as an `.em-btn`-like control. The input stays a real,
   focusable, single-`<label for>`-named file input (never a fake div-trigger). */
.em-file-input {
  font: inherit; color: var(--em-text-secondary);
  width: 100%; max-width: 100%;
  background: var(--em-surface-0);
  border: 1px dashed var(--em-border-strong);
  border-radius: var(--em-radius-sm);
  padding: var(--em-space-2);
  cursor: pointer;
}
.em-file-input:hover { border-color: var(--em-accent); }
.em-file-input:focus-visible {
  outline: none; border-color: var(--em-accent); box-shadow: var(--em-focus-ring);
}
.em-file-input::file-selector-button {
  font: inherit; font-weight: var(--em-fw-medium);
  margin-right: var(--em-space-3);
  padding: var(--em-space-1) var(--em-space-3);
  color: var(--em-text-primary);
  background: var(--em-surface-1);
  border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-sm);
  cursor: pointer;
}
.em-file-input:hover::file-selector-button,
.em-file-input::file-selector-button:hover {
  border-color: var(--em-accent); background: var(--em-accent-weak);
}

/* Numeric bound input — narrower than a free-text field (it holds a count/seconds). */
.em-input-number { max-width: 12rem; }

/* Checkbox GROUP (ui/form checkbox-group) — a real <fieldset>/<legend> so the group is
   named to assistive tech; the options stack with their own bound <label for>. */
.em-form-fieldset {
  display: flex; flex-direction: column; gap: var(--em-space-1);
  margin: 0; padding: 0; border: 0; min-width: 0;
}
.em-form-fieldset > legend {
  padding: 0; margin-bottom: var(--em-space-1);
  font-weight: var(--em-fw-medium); color: var(--em-text-secondary);
}
.em-form-check { display: flex; align-items: baseline; gap: var(--em-space-2); }
.em-form-check > label { color: var(--em-text-primary); }
.em-checkbox {
  flex: none; width: 15px; height: 15px; margin: 0;
  accent-color: var(--em-accent); cursor: pointer;
  position: relative; top: 2px;
}
.em-checkbox:focus-visible { outline: none; box-shadow: var(--em-focus-ring); border-radius: var(--em-radius-sm); }
.em-form-check > input:disabled + label { color: var(--em-text-muted); }
.em-checkbox:disabled { cursor: not-allowed; }

/* ── Frontier declare / upload / approve form layout ────────────────────────
   The panel header carries the two trigger buttons; each opened form is a calm,
   bounded card (surface + border + generous internal rhythm) that reads as part of
   the app, not a raw run of native controls. */
.em-frontier-panel-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--em-space-4); flex-wrap: wrap;
}
.em-frontier-panel-actions { display: flex; gap: var(--em-space-2); flex-wrap: wrap; }

.em-frontier-declare, .em-frontier-upload { display: block; }

.em-frontier-declare-form,
.em-frontier-upload-form,
.em-frontier-template-form,
.em-frontier-authoring-form,
.em-frontier-authoring-empty {
  display: flex; flex-direction: column; gap: var(--em-space-4);
  max-width: 520px;
  margin-top: var(--em-space-3);
  padding: var(--em-space-4);
  background: var(--em-surface-1);
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-md);
}
/* the author form carries checkbox groups + bounds — give it more room than a plain form */
.em-frontier-authoring-form { max-width: 640px; }
.em-frontier-declare-form > .em-muted,
.em-frontier-upload-form > .em-muted,
.em-frontier-authoring-form > .em-muted,
.em-frontier-authoring-empty > .em-muted { margin: 0; line-height: 1.45; }

.em-frontier-declare-actions,
.em-frontier-upload-actions,
.em-frontier-authoring-actions {
  display: flex; gap: var(--em-space-2); flex-wrap: wrap;
  margin-top: var(--em-space-1);
}

.em-frontier-declare-status,
.em-frontier-upload-status,
.em-frontier-authoring-status:not(:empty),
.em-frontier-template-status:not(:empty) { margin-top: var(--em-space-1); }

/* the narrowed-bounds fieldset lays its optional caps out as a compact two-column grid */
.em-frontier-authoring-bounds {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--em-space-3);
}
.em-frontier-authoring-bounds > legend { grid-column: 1 / -1; }

/* per-draft publish affordance in the drafts table */
.em-frontier-draft-publish { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-frontier-drafts .em-tbl td { vertical-align: top; }

/* the approve-as-template step nests inside the succeeded upload status; give it a
   clear top separation from the success line above it. */
.em-frontier-template-approve {
  margin-top: var(--em-space-3);
  padding-top: var(--em-space-3);
  border-top: 1px solid var(--em-border-subtle);
}

.em-cred-panel { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.em-cred-panel .em-insp-body { flex: 1 1 0; min-height: 0; overflow-y: auto; }
.em-insp-target { font-family: var(--em-font-mono); font-size: var(--em-fs-caption); color: var(--em-text-muted); }

/* stepper — plain numbers, ✓ on done, no rings/dots (owner-fixed) */
.em-cred-stepper {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--em-space-2);
  padding: var(--em-space-2) var(--em-space-3);
  border-bottom: 1px solid var(--em-border-subtle);
  background: var(--em-surface-0); font-size: var(--em-fs-caption);
}
.em-cred-step { color: var(--em-text-muted); font-variant-numeric: tabular-nums; }
.em-cred-step-check { color: var(--em-state-approved); }
.em-cred-step-done { color: var(--em-text-secondary); }
.em-cred-step-current { color: var(--em-text-primary); font-weight: var(--em-fw-bold); }
.em-cred-step-current.em-cred-step-skip { color: var(--em-text-secondary); font-weight: var(--em-fw-medium); }
.em-cred-step-sep { color: var(--em-text-muted); }
/* a completed step rendered as a clickable Back affordance — reads as a link, not a
   button chrome, so the stepper still looks like plain text (owner-fixed look). */
.em-cred-step-nav {
  appearance: none; background: none; border: none; padding: 0; margin: 0;
  font: inherit; font-variant-numeric: tabular-nums; cursor: pointer;
  color: var(--em-text-secondary); text-decoration: underline;
  text-decoration-color: var(--em-border-subtle); text-underline-offset: 2px;
}
.em-cred-step-nav:hover { color: var(--em-text-primary); text-decoration-color: currentColor; }
.em-cred-step-nav:focus-visible { outline: 2px solid var(--em-focus-ring, currentColor); outline-offset: 2px; }

.em-cred-h {
  font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--em-text-muted); font-weight: var(--em-fw-bold); margin: 0 0 var(--em-space-2);
}
.em-cred-note { color: var(--em-text-muted); font-size: var(--em-fs-caption); font-style: italic; margin: var(--em-space-3) 0 0; }

/* honesty banners (persistent) */
.em-cred-honesty {
  border-left: 3px solid var(--em-state-approved); background: var(--em-surface-1);
  padding: var(--em-space-2) var(--em-space-3); margin: 0 0 var(--em-space-3);
  border-radius: 0 var(--em-radius-sm) var(--em-radius-sm) 0;
  font-size: var(--em-fs-caption); color: var(--em-text-secondary);
}
.em-cred-honesty strong { color: var(--em-text-primary); font-weight: var(--em-fw-medium); }
.em-cred-honesty-preflight { border-left-color: var(--em-accent); }

/* State A — no credential required */
.em-cred-nocred {
  border: 1px solid var(--em-border-subtle); border-left: 3px solid var(--em-state-approved);
  border-radius: var(--em-radius-md); background: var(--em-surface-1); padding: var(--em-space-4);
}
.em-cred-nocred-title {
  margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold);
  display: flex; align-items: baseline; gap: var(--em-space-2);
}
.em-cred-nocred-glyph { color: var(--em-state-approved); font-size: 1.1rem; line-height: 1; }
.em-cred-nocred p { margin: var(--em-space-3) 0 0; color: var(--em-text-secondary); font-size: var(--em-fs-body); }
.em-cred-nocred-lead { color: var(--em-text-primary) !important; }
.em-cred-nocred-why {
  margin-top: var(--em-space-3) !important; padding-top: var(--em-space-3);
  border-top: 1px solid var(--em-border-subtle);
  font-size: var(--em-fs-caption) !important; color: var(--em-text-muted) !important;
}
.em-cred-nocred-actions { margin-top: var(--em-space-4); display: flex; gap: var(--em-space-3); align-items: center; flex-wrap: wrap; }
.em-cred-nocred-hint { font-size: var(--em-fs-caption); color: var(--em-text-muted); }

/* State B — reference cards (a radiogroup) */
.em-cred-group { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-cred-card { cursor: pointer; gap: var(--em-space-2); }
.em-cred-card:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-cred-selected { border-color: var(--em-accent); box-shadow: 0 0 0 1px var(--em-accent); background: var(--em-accent-weak); }
.em-cred-selected:focus-visible { box-shadow: var(--em-focus-ring); }
.em-cred-ineligible { cursor: not-allowed; background: var(--em-surface-1); border-style: dashed; }
.em-cred-ineligible .em-card-title { color: var(--em-text-secondary); }
.em-cred-head { display: flex; align-items: baseline; gap: var(--em-space-2); justify-content: space-between; flex-wrap: wrap; }
.em-cred-headleft { display: flex; align-items: baseline; gap: var(--em-space-2); min-width: 0; }
.em-cred-mark { font-size: 0.85rem; line-height: 1; color: var(--em-text-secondary); width: 1em; text-align: center; flex: none; }
.em-cred-selected .em-cred-mark { color: var(--em-accent); }
.em-cred-selstate { font-size: var(--em-fs-caption); font-weight: var(--em-fw-medium); color: var(--em-text-muted); white-space: nowrap; }
.em-cred-selected .em-cred-selstate { color: var(--em-accent); }
.em-cred-caps { font-family: var(--em-font-mono); font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-cred-foot { display: flex; align-items: center; gap: var(--em-space-2); flex-wrap: wrap; font-size: var(--em-fs-caption); }
/* (`.em-cred-badge-secret-free` removed — the 'reference — no secret' token is a
   `.em-tag--neutral`.) */

.em-cred-flags { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-cred-flag {
  display: flex; align-items: flex-start; gap: 6px; font-size: var(--em-fs-caption);
  color: var(--em-state-disputed); border-left: 3px solid var(--em-state-disputed);
  background: var(--em-surface-1); padding: 2px var(--em-space-2); border-radius: 0 2px 2px 0;
}
.em-cred-flag-g { flex: none; }
.em-cred-reason { display: flex; align-items: flex-start; gap: 6px; font-size: var(--em-fs-caption); color: var(--em-state-rejected); font-weight: var(--em-fw-medium); }
.em-cred-reason-g { flex: none; }

/* degraded / empty states */
.em-cred-state {
  border: 1px dashed var(--em-border-strong); border-radius: var(--em-radius-md);
  padding: var(--em-space-3); background: var(--em-surface-1); font-size: var(--em-fs-body);
  color: var(--em-text-secondary);
}
.em-cred-state p { margin: var(--em-space-1) 0 0; font-size: var(--em-fs-caption); }
.em-cred-state-title { font-weight: var(--em-fw-medium); margin: 0 !important; color: var(--em-text-primary); font-size: var(--em-fs-body) !important; }
.em-cred-state-error { border-color: var(--em-state-rejected); color: var(--em-state-rejected); }
.em-cred-retry {
  margin-top: var(--em-space-2); font: inherit; font-size: var(--em-fs-caption);
  color: var(--em-accent); border: 1px solid var(--em-accent); border-radius: var(--em-radius-sm);
  padding: 2px var(--em-space-2); background: transparent; cursor: pointer;
}
.em-cred-retry:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }

/* ── Discovery Frontier — Stage 5 bounded SCOPE editor ──────────────────────────
   Launch step ④, rendered in the inspector. Reuses the `.em-*` token vocabulary;
   non-colour state channels throughout (glyph + text + disabled), absolute values +
   units only (no percentage / denominator, §17/§21). Distinct from the context-bar
   `.em-scope-tenant/programme/*` scope classes — these are all `.em-scope-` editor
   parts with their own suffixes. */
.em-scope-editor { display: flex; flex-direction: column; gap: var(--em-space-4); }
.em-scope-h {
  margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold);
  color: var(--em-text-primary);
}
.em-scope-honesty {
  font-size: var(--em-fs-caption); color: var(--em-text-secondary);
  background: var(--em-surface-1); border-left: 3px solid var(--em-border-strong);
  border-radius: var(--em-radius-sm); padding: var(--em-space-2) var(--em-space-3);
}
.em-scope-honesty strong { color: var(--em-text-primary); font-weight: var(--em-fw-medium); }
.em-scope-warnings {
  border: 1px solid var(--em-state-disputed); border-radius: var(--em-radius-sm);
  padding: var(--em-space-2) var(--em-space-3); background: var(--em-surface-1);
}
.em-scope-warning { margin: 0; font-size: var(--em-fs-caption); color: var(--em-state-disputed); }
.em-scope-controls { display: flex; flex-direction: column; gap: var(--em-space-5); }

/* fieldsets (capabilities, object-types, date-range) */
.em-scope-fs { border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-md); padding: var(--em-space-3); margin: 0; }
.em-scope-legend { font-weight: var(--em-fw-medium); color: var(--em-text-secondary); padding: 0 var(--em-space-1); font-size: var(--em-fs-caption); }
.em-scope-caps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-scope-cap-label { display: flex; align-items: baseline; gap: var(--em-space-2); cursor: pointer; }
.em-scope-cap-label input { flex: none; }
.em-scope-cap-label input:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-scope-cap-text { color: var(--em-text-primary); }
.em-scope-cap-locked .em-scope-cap-text { color: var(--em-text-secondary); }
.em-scope-cap-blocked .em-scope-cap-label { cursor: not-allowed; }
.em-scope-cap-blocked .em-scope-cap-text { color: var(--em-text-muted); text-decoration: line-through; }
.em-scope-cap-reason {
  display: block; font-size: var(--em-fs-caption); color: var(--em-state-rejected);
  font-weight: var(--em-fw-medium); margin-top: 2px;
}
.em-scope-cap-reason-g { margin-right: 4px; }
.em-scope-tag-req {
  font-size: var(--em-fs-caption); color: var(--em-text-muted);
  border: 1px solid var(--em-border-strong); border-radius: var(--em-radius-sm);
  padding: 0 var(--em-space-1); text-transform: uppercase; letter-spacing: 0.03em;
}
.em-scope-block {
  display: flex; align-items: flex-start; gap: var(--em-space-2);
  font-size: var(--em-fs-caption); color: var(--em-state-rejected); font-weight: var(--em-fw-medium);
  border: 1px solid var(--em-state-rejected); border-radius: var(--em-radius-sm);
  padding: var(--em-space-2); margin-bottom: var(--em-space-3);
}
.em-scope-block-g { flex: none; }
.em-scope-block strong { color: var(--em-state-rejected); }

/* generic bounded field */
.em-scope-field { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-scope-field-label { font-weight: var(--em-fw-medium); color: var(--em-text-secondary); font-size: var(--em-fs-caption); }
.em-scope-cap-note { margin: 2px 0 0; font-size: var(--em-fs-caption); color: var(--em-text-muted); }

/* numeric bound stepper — plain number + unit, no ring/percentage */
.em-scope-num-row { display: flex; align-items: baseline; gap: var(--em-space-2); }
.em-scope-num {
  width: 8rem; font: inherit; font-variant-numeric: tabular-nums;
  color: var(--em-text-primary); background: var(--em-surface-0);
  border: 1px solid var(--em-border-strong); border-radius: var(--em-radius-sm);
  padding: 2px var(--em-space-2);
}
.em-scope-num:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-scope-num-unit { color: var(--em-text-secondary); font-size: var(--em-fs-caption); }

/* tag list (globs / identifiers) — opaque strings, never executed. The tokens are now
   mono removable `.em-tag`s in a `.em-token-row`; this shim adds only the bottom margin
   (the old `.em-scope-tag`/`.em-scope-tag-x` styling moved into `.em-tag--mono`/`.em-tag-x`). */
.em-scope-tags { margin: 0 0 var(--em-space-2); }
.em-scope-add-row { display: flex; gap: var(--em-space-2); }
.em-scope-add {
  flex: 1 1 0; min-width: 0; font: inherit; color: var(--em-text-primary);
  background: var(--em-surface-0); border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-sm); padding: 2px var(--em-space-2);
}
.em-scope-add:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-scope-add[aria-invalid="true"] { border-color: var(--em-state-rejected); }
.em-scope-add-btn {
  flex: none; font: inherit; font-size: var(--em-fs-caption); color: var(--em-accent);
  background: var(--em-control-face); border: 1px solid var(--em-accent); border-radius: var(--em-radius-sm);
  padding: 2px var(--em-space-3); cursor: pointer;
}
.em-scope-add-btn:disabled { color: var(--em-text-muted); border-color: var(--em-border-subtle); cursor: not-allowed; }
.em-scope-add-btn:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }

/* select (environment) + date-range */
.em-scope-select {
  font: inherit; color: var(--em-text-primary); background: var(--em-surface-0);
  border: 1px solid var(--em-border-strong); border-radius: var(--em-radius-sm); padding: 2px var(--em-space-2);
}
.em-scope-select:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-scope-date-row { display: flex; align-items: baseline; gap: var(--em-space-2); flex-wrap: wrap; }

/* read-only confirmation (rule (c)) — honest, never a fake-empty form */
.em-scope-readonly { display: flex; flex-direction: column; gap: var(--em-space-4); }
.em-scope-readonly-banner {
  font-size: var(--em-fs-caption); color: var(--em-text-secondary);
  background: var(--em-surface-1); border-left: 3px solid var(--em-accent);
  border-radius: var(--em-radius-sm); padding: var(--em-space-3);
}
.em-scope-readonly-banner strong { color: var(--em-text-primary); font-weight: var(--em-fw-medium); }
.em-scope-readonly-g { margin-right: 4px; }
.em-scope-readonly-sec { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-scope-readonly-sec .em-scope-h { font-size: var(--em-fs-body); }
.em-scope-ro-caps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-scope-ro-cap { display: flex; align-items: baseline; gap: var(--em-space-2); color: var(--em-text-primary); }
.em-scope-ro-g { color: var(--em-state-approved); flex: none; }
.em-scope-ro-bounds { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 2px var(--em-space-3); }
.em-scope-ro-bounds dt { color: var(--em-text-secondary); font-size: var(--em-fs-caption); }
.em-scope-ro-bounds dd { margin: 0; color: var(--em-text-primary); font-variant-numeric: tabular-nums; }
.em-scope-h4 { margin: 0; }

/* actions */
.em-scope-actions { display: flex; align-items: center; gap: var(--em-space-3); flex-wrap: wrap; margin-top: var(--em-space-2); }
.em-scope-arm-hint { font-size: var(--em-fs-caption); color: var(--em-text-muted); flex: 1 1 100%; }

/* ─────────────────────────────────────────────────────────────────────────────
   Preflight surface (Stage 6; SPEC-4 §14). Honest per-check results — status is
   carried by a glyph + a status WORD, never colour alone; a :skipped check reads a
   subdued N/A, never a tick; no fabricated progress. Built on the existing --em-*
   tokens (light + dark inherit automatically) — no fork.
   ───────────────────────────────────────────────────────────────────────────── */
.em-preflight-run { display: flex; flex-direction: column; gap: var(--em-space-4); }
.em-preflight-result { display: flex; flex-direction: column; gap: var(--em-space-4); }

/* overall status banner — the glyph tints per state, but the WORD + headline carry it */
.em-preflight-banner {
  display: flex; align-items: baseline; gap: var(--em-space-2);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-border-subtle); background: var(--em-surface-1);
  font-size: var(--em-fs-body);
}
.em-preflight-banner-g { flex: none; font-size: var(--em-fs-strong); }
.em-preflight-banner-word { font-weight: var(--em-fw-bold); color: var(--em-text-primary); }
.em-preflight-banner-passed  { border-color: var(--em-state-approved); }
.em-preflight-banner-passed  .em-preflight-banner-g { color: var(--em-state-approved); }
.em-preflight-banner-warning { border-color: var(--em-state-disputed); }
.em-preflight-banner-warning .em-preflight-banner-g { color: var(--em-state-disputed); }
.em-preflight-banner-failed  { border-color: var(--em-state-rejected); }
.em-preflight-banner-failed  .em-preflight-banner-g { color: var(--em-state-rejected); }

/* short-lived expiry — honest deadline, not a countdown bar */
.em-preflight-expiry {
  margin: 0; display: flex; align-items: baseline; gap: var(--em-space-2);
  font-size: var(--em-fs-caption); color: var(--em-text-muted);
}
.em-preflight-expiry-g { flex: none; }
.em-preflight-expiry-lapsed { color: var(--em-state-rejected); font-weight: var(--em-fw-medium); }

/* per-check rows */
.em-preflight-checks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-preflight-check {
  display: flex; align-items: baseline; gap: var(--em-space-2);
  padding: var(--em-space-2); border-radius: var(--em-radius-sm);
  border: 1px solid var(--em-border-subtle); background: var(--em-surface-0);
}
.em-preflight-check-g { flex: none; width: 1.1em; text-align: center; font-weight: var(--em-fw-bold); }
.em-preflight-check-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.em-preflight-check-head { display: flex; align-items: baseline; gap: var(--em-space-2); flex-wrap: wrap; }
.em-preflight-check-label { font-weight: var(--em-fw-medium); color: var(--em-text-primary); }
.em-preflight-check-status {
  font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.04em;
  border: 1px solid var(--em-border-strong); border-radius: var(--em-radius-sm);
  padding: 0 var(--em-space-1); color: var(--em-text-secondary);
}
.em-preflight-check-msg { font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-preflight-check-passed  .em-preflight-check-g { color: var(--em-state-approved); }
.em-preflight-check-warning .em-preflight-check-g { color: var(--em-state-disputed); }
.em-preflight-check-warning .em-preflight-check-status { border-color: var(--em-state-disputed); color: var(--em-state-disputed); }
.em-preflight-check-failed  { border-color: var(--em-state-rejected); }
.em-preflight-check-failed  .em-preflight-check-g { color: var(--em-state-rejected); }
.em-preflight-check-failed  .em-preflight-check-status { border-color: var(--em-state-rejected); color: var(--em-state-rejected); }
/* a skipped check is a SUBDUED N/A — never a tick, never a pass colour */
.em-preflight-check-na { background: var(--em-surface-1); }
.em-preflight-check-na .em-preflight-check-g { color: var(--em-text-muted); }
.em-preflight-check-na .em-preflight-check-label { color: var(--em-text-secondary); }
.em-preflight-check-na .em-preflight-check-status { border-style: dashed; color: var(--em-text-muted); }

/* non-blocking warnings + report-only confirmations */
.em-preflight-warnings {
  display: flex; flex-direction: column; gap: var(--em-space-3);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-state-disputed); background: var(--em-surface-1);
}
.em-preflight-warn-head { margin: 0 0 var(--em-space-1); display: flex; align-items: baseline; gap: var(--em-space-2); color: var(--em-text-primary); }
.em-preflight-warn-g { flex: none; color: var(--em-state-disputed); }
.em-preflight-warn-list { margin: 0; padding-left: var(--em-space-4); }
.em-preflight-warn-item { color: var(--em-text-primary); }
.em-preflight-confirm-block { border-top: 1px solid var(--em-border-subtle); padding-top: var(--em-space-3); }
.em-preflight-confirm-note { margin: var(--em-space-1) 0 0; font-size: var(--em-fs-caption); color: var(--em-text-muted); }

/* blocked panel (failed) — prominent, names the failure, NO bypass */
.em-preflight-blocked {
  display: flex; flex-direction: column; gap: var(--em-space-2);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-state-rejected); background: var(--em-surface-1);
}
.em-preflight-blocked-head { margin: 0; display: flex; align-items: baseline; gap: var(--em-space-2); color: var(--em-state-rejected); }
.em-preflight-blocked-g { flex: none; font-weight: var(--em-fw-bold); }
.em-preflight-blocked-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-preflight-blocked-item { display: flex; flex-direction: column; gap: 2px; }
.em-preflight-blocked-name { margin: 0; font-weight: var(--em-fw-medium); color: var(--em-text-primary); }
.em-preflight-blocked-badge {
  font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.04em;
  border: 1px solid var(--em-state-rejected); border-radius: var(--em-radius-sm);
  padding: 0 var(--em-space-1); color: var(--em-state-rejected);
}
.em-preflight-blocked-msg { margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-preflight-blocked-fix { margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-primary); }
.em-preflight-blocked-fix-label { font-weight: var(--em-fw-medium); }
.em-preflight-blocked-raw { font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-preflight-blocked-raw summary { cursor: pointer; }
.em-preflight-blocked-note { margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-muted); }

/* actions */
.em-preflight-actions { display: flex; align-items: center; gap: var(--em-space-3); flex-wrap: wrap; margin-top: var(--em-space-2); }
.em-preflight-arm-hint { font-size: var(--em-fs-caption); color: var(--em-text-muted); flex: 1 1 100%; }

/* ─────────────────────────────────────────────────────────────────────────────
   Confirm & launch surface (Stage 7; SPEC-4 §15/§30/§33). The §30 human launch
   authorisation. Honest by construction: a read-only summary + a WEIGHTY authorise
   confirm; the queued confirmation shows NO fabricated progress; the secondary-approval
   notice is report-only and never dual-approval theatre. Built on the --em-* tokens.
   ───────────────────────────────────────────────────────────────────────────── */
.em-confirm-authorise { display: flex; flex-direction: column; gap: var(--em-space-4); }

/* read-only launch summary */
.em-confirm-summary {
  display: flex; flex-direction: column; gap: var(--em-space-4);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-border-subtle); background: var(--em-surface-1);
}
.em-confirm-facts { margin: 0; display: grid; gap: var(--em-space-2); }
.em-confirm-kv { display: grid; grid-template-columns: 8.5rem 1fr; gap: var(--em-space-3); align-items: baseline; }
.em-confirm-kv-k { margin: 0; font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.04em; color: var(--em-text-muted); }
.em-confirm-kv-v { margin: 0; font-size: var(--em-fs-body); color: var(--em-text-primary); min-width: 0; overflow-wrap: anywhere; }
.em-confirm-sub { color: var(--em-text-secondary); }

.em-confirm-scope { border-top: 1px solid var(--em-border-subtle); padding-top: var(--em-space-3); display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-confirm-scope-h { margin: 0; font-size: var(--em-fs-body); font-weight: var(--em-fw-medium); color: var(--em-text-primary); }
.em-confirm-scope-sec { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-confirm-scope-label { font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: 0.04em; color: var(--em-text-muted); }
/* (`.em-confirm-chips`/`.em-confirm-chip` removed — the confirmed scope values render as
   `.em-tag`s in a `.em-token-row`.) */
.em-confirm-bounds { margin: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: var(--em-space-2); }
.em-confirm-bounds dt { font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-confirm-bounds dd { margin: 0; font-size: var(--em-fs-body); color: var(--em-text-primary); }
.em-confirm-scope-note { margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-muted); }

/* report-only secondary-approval notice (AMB-4) — information, never a gate */
.em-confirm-secondary {
  display: flex; flex-direction: column; gap: var(--em-space-1);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-state-disputed); background: var(--em-surface-1);
}
.em-confirm-secondary-head { margin: 0; display: flex; align-items: baseline; gap: var(--em-space-2); color: var(--em-text-primary); }
.em-confirm-secondary-g { flex: none; color: var(--em-state-disputed); }
.em-confirm-secondary-lead { margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-confirm-secondary-list { margin: var(--em-space-1) 0 0; padding-left: var(--em-space-4); font-size: var(--em-fs-caption); color: var(--em-text-primary); }

/* actions + the weighty authorise button */
.em-confirm-actions { display: flex; align-items: center; gap: var(--em-space-3); flex-wrap: wrap; margin-top: var(--em-space-2); }
.em-confirm-authorise-hint { font-size: var(--em-fs-caption); color: var(--em-text-muted); flex: 1 1 100%; }
.em-btn-launch {
  border-color: var(--em-state-approved); background: var(--em-state-approved);
  color: var(--em-text-inverse); font-weight: var(--em-fw-bold);
  padding: var(--em-space-2) var(--em-space-4);
}
.em-btn-launch:disabled { opacity: 0.55; background: var(--em-surface-2); color: var(--em-text-muted); border-color: var(--em-border-strong); }

/* queued confirmation — honest; NO progress bar / percentage / running state */
.em-confirm-queued {
  display: flex; flex-direction: column; gap: var(--em-space-3);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-state-approved); background: var(--em-surface-1);
}
.em-confirm-queued-banner { display: flex; align-items: baseline; gap: var(--em-space-2); }
.em-confirm-queued-g { flex: none; color: var(--em-state-approved); font-weight: var(--em-fw-bold); }
.em-confirm-queued-headline { color: var(--em-text-primary); }
.em-confirm-run-id { font-family: var(--em-font-mono); font-size: var(--em-fs-caption); overflow-wrap: anywhere; }
.em-confirm-run-status { text-transform: uppercase; letter-spacing: 0.04em; font-size: var(--em-fs-caption); border: 1px solid var(--em-state-approved); border-radius: var(--em-radius-sm); padding: 0 var(--em-space-1); color: var(--em-state-approved); }
.em-confirm-replay { margin: 0; display: flex; align-items: baseline; gap: var(--em-space-2); font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-confirm-replay-g { flex: none; }
.em-confirm-observation-note { margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-muted); }

/* stale (409) / refused (uniform) / network-uncertain panels */
.em-confirm-stale, .em-confirm-refused {
  display: flex; flex-direction: column; gap: var(--em-space-2);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-state-rejected); background: var(--em-surface-1);
}
.em-confirm-uncertain {
  display: flex; flex-direction: column; gap: var(--em-space-2);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-state-disputed); background: var(--em-surface-1);
}
.em-confirm-stale-head { margin: 0; display: flex; align-items: baseline; gap: var(--em-space-2); color: var(--em-text-primary); }
.em-confirm-stale-g { flex: none; font-weight: var(--em-fw-bold); }
.em-confirm-stale p, .em-confirm-refused p, .em-confirm-uncertain p { margin: 0; font-size: var(--em-fs-body); color: var(--em-text-primary); }
.em-confirm-stale-note { font-size: var(--em-fs-caption) !important; color: var(--em-text-muted) !important; }

/* ── Tier-0D Discovery Frontier — Runs view (Stage 8, observe) ───────────────
   The operator's window onto their launched runs: the queue, one run's detail, its
   immutable attempt timeline. Reuses the Tier-0C tokens + the `.em-card*`/`.em-btn`
   vocabulary. Status is text-first; colour + glyph only reinforce (never the sole
   channel), and partially-succeeded is visibly distinct from success. */

/* rail nav — a LIVE item is a full-width button; keep the row treatment identical to
   the passive `.em-frontier-nav` so the active/inactive look is unchanged. */
.em-frontier-nav-link {
  font: inherit; background: none; border: none; width: 100%;
  text-align: left; cursor: pointer;
  /* a persistent active MARKER lives in this bar so active ≠ hover is unmistakable */
  border-left: 3px solid transparent;
}
/* hover is a hover AFFORDANCE ONLY — it must never wash out the active item. Scope it to
   NON-active links so hovering the active row keeps its accent background + marker, and
   hovering another row highlights only that row (owner point 5: the states were conflated). */
.em-frontier-nav-link:not(.em-active):hover { background: var(--em-surface-1); }
/* the active item stays visibly active: its accent-weak background + a left accent bar are
   preserved on hover (a distinct, stronger treatment than the plain surface-1 hover). */
.em-frontier-nav-link.em-active { border-left-color: var(--em-accent); }
.em-frontier-nav-link.em-active:hover { background: var(--em-accent-weak); }
.em-frontier-nav-link:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-frontier-nav[aria-disabled="true"] { cursor: default; }

.em-runs-panel { width: 100%; min-width: 0; }
.em-runs-header {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--em-space-3);
}
.em-runs-header h2 { margin: 0; }
.em-runs-header p { margin: var(--em-space-2) 0; }
.em-runs-refresh { flex: none; }
.em-runs-empty { padding: var(--em-space-5) 0; max-width: 620px; }
.em-runs-empty .em-muted { margin: var(--em-space-1) 0 0; line-height: 1.5; }

/* the queue — one keyboard-operable card per run */
.em-run-card-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-run-card-li { margin: 0; }
.em-run-card {
  font: inherit; text-align: left; width: 100%; cursor: pointer;
  background: var(--em-surface-0); border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-md); padding: var(--em-space-3) var(--em-space-4);
  display: flex; flex-direction: column; gap: var(--em-space-2);
}
.em-run-card:hover { border-color: var(--em-border-strong); }
.em-run-card:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-run-card-head { flex-wrap: wrap; display: flex; align-items: center; justify-content: space-between; gap: var(--em-space-3); }
.em-run-card-target { font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); line-height: 1.25; }
.em-run-card-meta {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
  gap: var(--em-space-1) var(--em-space-5); margin: 0; font-size: var(--em-fs-caption);
}
.em-run-card-meta > div { display: flex; flex-direction: column; }
.em-run-card-meta dt { color: var(--em-text-muted); text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.62rem; }
.em-run-card-meta dd { margin: 0; color: var(--em-text-primary); }
.em-run-card-foot { font-size: var(--em-fs-caption); }
.em-run-id { font-family: var(--em-font-mono); font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-run-connector { font-family: var(--em-font-mono); font-size: var(--em-fs-caption); color: var(--em-text-secondary); }

/* (The run status chip — the old `.em-run-status*` family, 17 rules — is DELETED.
   Runs now render the canonical P1 `.em-status-badge` via `ui/status-badge`; the run
   lifecycle tones, including partially-succeeded → the DISTINCT :disputed amber (never
   success-green), live in the one status registry, keyed off the run status name.) */

/* run detail */
.em-run-detail { display: flex; flex-direction: column; gap: var(--em-space-4); width: 100%; min-width: 0; }
.em-run-detail-titlebar { display: flex; align-items: center; gap: var(--em-space-3); flex-wrap: wrap; }
.em-run-detail-title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--em-space-3); }
.em-run-detail-target { margin: 0; overflow-wrap: anywhere; font-size: 1.35rem; font-weight: var(--em-fw-bold); }
.em-run-back { flex: none; }
.em-run-detail-body { display: flex; flex-direction: column; gap: var(--em-space-5); }
.em-run-section { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-run-section-h { margin: 0; font-size: var(--em-fs-body); font-weight: var(--em-fw-bold); color: var(--em-text-secondary); }

/* progress — phases + qualified counters, NEVER a bar without a bounded total */
.em-run-progress { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-run-progress-phase { font-weight: var(--em-fw-medium); }

/* ── run detail: CONTROL + EXPLANATION rows ────────────────────────────────────
   Every row below pairs a control with the sentence that explains it. They had NO
   rule at all — a bare <div> with a <button> then a <span>, and hiccup emits no
   whitespace between siblings, so the two rendered exactly flush ("Cancel runRequests
   cancellation…"). This is the same defect as the jammed status badge and it was NOT
   a badge problem: it is what happens when a container holding a control next to text
   has no layout. `align-items: baseline` sits the explanation on the button's text
   baseline rather than centring it against the button's box.
   `cljs-css-coverage-test` now fails if a class is used in the UI with no rule here,
   so a new container cannot silently arrive unstyled again. */
.em-run-cancel,
.em-run-handoff,
.em-run-follow-on {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--em-space-3);
}
/* the error is a full-width row under the control, never inline beside it */
.em-run-cancel-error { flex-basis: 100%; margin: 0; color: var(--em-tone-rejected); }
.em-run-cancel-btn, .em-run-handoff-btn, .em-run-followon-btn { flex: none; }
.em-run-cancel-note, .em-run-handoff-note { max-width: 70ch; }

.em-run-progress-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--em-space-3); }

/* review queue: the run-scope banner pairs a label with a "Show all" link-button —
   same control-next-to-text shape as the run-detail rows above, same missing rule. */
.em-rq-runscope { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-rq-runscope-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--em-space-3); }
.em-rq-runscope-label { font-weight: var(--em-fw-medium); }
.em-rq-runscope-note { margin: 0; max-width: 70ch; }
.em-run-counter-text { font-variant-numeric: tabular-nums; }

/* follow-on panel */
.em-run-followon-open { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-run-followon-head { display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: var(--em-space-3); }
.em-run-followon-note, .em-run-followon-empty, .em-run-followon-more { margin: 0; max-width: 70ch; }
.em-run-followon-list { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-run-followon-item { display: flex; }
/* the target button holds a label + a muted type; they need their own gap too */
.em-run-followon-target { display: inline-flex; align-items: baseline; gap: var(--em-space-1);
  text-align: left; }
.em-run-followon-target-label { font-weight: var(--em-fw-medium); }
.em-run-phases { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap;
  /* space-2, not space-1: at 4px the '→' sat on the pill's border and read as part
     of it rather than as the separator between two phases. */
  gap: var(--em-space-2); font-size: var(--em-fs-caption); }
.em-run-phase { padding: 1px var(--em-space-2); border: 1px solid var(--em-border-subtle); border-radius: 999px; color: var(--em-text-muted); }
.em-run-phase-current { color: var(--em-text-primary); border-color: var(--em-state-validated); font-weight: var(--em-fw-bold); }
.em-run-phase-arrow { align-self: center; color: var(--em-text-muted); font-size: var(--em-fs-caption); }
.em-run-counters { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.em-run-counter { display: flex; align-items: baseline; gap: var(--em-space-2); font-size: var(--em-fs-body); }
.em-run-counter-pct { font-family: var(--em-font-mono); color: var(--em-text-secondary); }
.em-run-progress-note { font-size: var(--em-fs-caption) !important; margin: 0; }

/* outcome note — a partial/failed run states its outcome EXPLICITLY (never a plain
   success) and surfaces the server's honest `reason` prominently: WHAT did not
   complete. The reason is the load-bearing line, so it reads at body size, not muted. */
.em-run-outcome {
  display: flex; flex-direction: column; gap: var(--em-space-1);
  padding: var(--em-space-3); border-radius: var(--em-radius-md);
  border: 1px solid var(--em-state-disputed); border-left-width: 3px;
  background: var(--em-surface-1); font-size: var(--em-fs-body);
}
.em-run-outcome-failed { border-color: var(--em-state-conflict); }
/* a clean-success outcome (no partial/failed banner) is neutral — the amber/red border
   is only for partial/failed; a success just carries its honest "what it produced" line */
.em-run-outcome-plain { border-color: var(--em-border-subtle); }
.em-run-outcome-head { margin: 0; display: flex; align-items: baseline; gap: var(--em-space-2); flex-wrap: wrap; }
.em-run-outcome-g { color: var(--em-state-disputed); font-weight: var(--em-fw-bold); flex: none; }
.em-run-outcome-failed .em-run-outcome-g { color: var(--em-state-conflict); }
.em-run-outcome-lede { color: var(--em-text-secondary); }
.em-run-outcome-reason { margin: 0; color: var(--em-text-primary); }
.em-run-outcome-reason-k { font-weight: var(--em-fw-bold); color: var(--em-text-secondary); }
.em-run-outcome-summary { margin: 0; font-size: var(--em-fs-caption) !important; }
/* a run's operator / programme / organisation identity — a compact readable id, never
   a wrapped full URL (the raw IRI is the hover title) */
.em-run-operator, .em-run-programme { font-family: var(--em-font-mono); font-size: var(--em-fs-caption); }

/* attempt timeline — immutable; fenced surfaced as an honest STATE */
.em-run-timeline { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-run-attempts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-run-attempt {
  padding: var(--em-space-2) var(--em-space-3); border: 1px solid var(--em-border-subtle);
  border-left-width: 3px; border-radius: var(--em-radius-sm); background: var(--em-surface-0);
  display: flex; flex-direction: column; gap: 2px;
}
.em-run-attempt-fenced { border-left-color: var(--em-state-conflict); }
.em-run-attempt-head { display: flex; align-items: baseline; gap: var(--em-space-3); flex-wrap: wrap; }
.em-run-attempt-num { font-weight: var(--em-fw-medium); }
.em-run-attempt-time { font-family: var(--em-font-mono); font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-run-attempt-fencenote { margin: 0; font-size: var(--em-fs-caption); color: var(--em-state-conflict); }
.em-run-attempt-detail { margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-run-attempts-empty { margin: 0; }

/* ── Discovery Story (Stage C3): coverage matrix + frontier table + fog map ──────
   Every state is carried by shape + label + icon; colour is redundant. The meter fill
   appears ONLY on a credible-denominator dimension (never on ∞/? tracks). */
.em-ds-scroll { overflow: auto; height: 100%; padding: var(--em-space-4) var(--em-space-5) var(--em-space-6); min-height: 0;
  /* fill the middle pane: a full-height flex column so the map band can grow into the
     remaining vertical space and every band uses the full pane WIDTH (owner ask). */
  display: flex; flex-direction: column; }
.em-ds-head { margin-bottom: var(--em-space-4); }
.em-ds-title-row { display: flex; align-items: baseline; gap: var(--em-space-3); }
.em-ds-title { font-size: var(--em-fs-title); font-weight: var(--em-fw-bold); margin: 0; }
.em-ds-lede { color: var(--em-text-secondary); font-size: var(--em-fs-caption); margin: var(--em-space-1) 0 0; max-width: 72ch; }
.em-ds-asof { color: var(--em-text-muted); font-size: var(--em-fs-caption); margin: 0 0 var(--em-space-2); }
.em-ds-error { color: var(--em-state-rejected); font-weight: var(--em-fw-medium); }

.em-ds-band { margin-top: var(--em-space-5); }
.em-ds-band-h { display: flex; align-items: baseline; gap: var(--em-space-3); margin: 0 0 var(--em-space-1); }
.em-ds-band-h h3, .em-ds-band-h h4 { font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); margin: 0; }
.em-ds-tag { font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-ds-sub { color: var(--em-text-secondary); font-size: var(--em-fs-caption); margin: 0 0 var(--em-space-3); max-width: 72ch; }
.em-ds-footnote { color: var(--em-text-muted); font-size: var(--em-fs-caption); margin-top: var(--em-space-2); }

.em-ds-card { background: var(--em-surface-0); border: 1px solid var(--em-line, var(--em-surface-2)); border-radius: var(--em-radius-md); }
.em-ds-fam { padding: var(--em-space-3) var(--em-space-4); border-bottom: 1px solid var(--em-surface-2); }
.em-ds-fam:last-child { border-bottom: none; }
.em-ds-fam-h { font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: .06em; color: var(--em-text-muted); font-weight: var(--em-fw-bold); margin: 0 0 var(--em-space-2); }
.em-ds-dimtable { width: 100%; border-collapse: collapse; }
.em-ds-dimrow > th, .em-ds-dimrow > td { text-align: left; vertical-align: top; padding: var(--em-space-2) var(--em-space-3) var(--em-space-2) 0; border-top: 1px solid var(--em-surface-2); font-weight: var(--em-fw-regular); }
.em-ds-fam .em-ds-dimtable tr:first-child > * { border-top: none; }
.em-ds-dim-name { font-weight: var(--em-fw-medium); }
.em-ds-dsrc { color: var(--em-text-muted); font-size: var(--em-fs-caption); }
.em-ds-measure { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-ds-state-lbl { font-size: var(--em-fs-caption); font-weight: var(--em-fw-medium); }
.em-ds-glyph { font-family: var(--em-font-mono, monospace); font-weight: 700; }
.em-ds-meter { height: 8px; border-radius: 5px; background: var(--em-surface-2); overflow: hidden; border: 1px solid var(--em-text-muted); }
.em-ds-meter > i { display: block; height: 100%; background: var(--em-text-secondary); }
.em-ds-track-open { height: 8px; border-radius: 5px; border: 1px dashed var(--em-text-muted); background: repeating-linear-gradient(45deg, transparent 0 5px, var(--em-surface-2) 5px 10px); }
.em-ds-track-unknown { height: 8px; border-radius: 5px; border: 1px dotted var(--em-text-muted); background: transparent; }
.em-ds-mval { font-variant-numeric: tabular-nums; font-size: var(--em-fs-caption); }
.em-ds-note { color: var(--em-text-muted); }

.em-ds-limits { border: 1px solid var(--em-surface-2); border-left: 4px solid var(--em-state-disputed); border-radius: var(--em-radius-md); background: var(--em-surface-0); padding: var(--em-space-3) var(--em-space-4); margin-top: var(--em-space-3); }
.em-ds-limits h4 { font-size: var(--em-fs-body); font-weight: var(--em-fw-bold); margin: 0 0 var(--em-space-2); }
.em-ds-limits ol { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-ds-limits li { color: var(--em-text-secondary); }

.em-ds-delta { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1px; background: var(--em-surface-2); border-radius: var(--em-radius-md); overflow: hidden; }
.em-ds-cell { background: var(--em-surface-0); padding: var(--em-space-3); }
.em-ds-k { font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-ds-v { font-size: var(--em-fs-title); font-weight: var(--em-fw-bold); font-variant-numeric: tabular-nums; margin-top: 2px; }
.em-ds-v.em-ds-nm { font-size: var(--em-fs-body); font-weight: var(--em-fw-regular); color: var(--em-text-muted); font-style: italic; }
.em-ds-q { font-size: var(--em-fs-caption); color: var(--em-text-secondary); margin-top: 2px; }

/* (`.em-ds-chip`/`.em-ds-c-*` removed — the discovery-story tokens now use the canonical
   `.em-tag--pill` with the :observed/:candidate/:accent/:neutral tones.) */

.em-ds-fr { border: 1px solid var(--em-surface-2); border-radius: var(--em-radius-md); overflow: hidden; background: var(--em-surface-0); }
.em-ds-fr-cat-h { display: flex; gap: var(--em-space-2); align-items: center; padding: var(--em-space-2) var(--em-space-4); background: var(--em-surface-1); font-size: var(--em-fs-caption); font-weight: var(--em-fw-bold); color: var(--em-text-secondary); border-top: 1px solid var(--em-surface-2); }
.em-ds-fr-cat:first-child .em-ds-fr-cat-h { border-top: none; }
.em-ds-fr-row { border-top: 1px solid var(--em-surface-2); border-left: 3px solid var(--em-state-disputed); }
.em-ds-fr-row.em-ds-cand { border-left-color: var(--em-state-candidate); }
.em-ds-fr-sum { display: grid; grid-template-columns: auto 1fr auto; gap: var(--em-space-3); align-items: center; padding: var(--em-space-3) var(--em-space-4); cursor: pointer; }
.em-ds-fr-kind { font-size: var(--em-fs-caption); font-weight: 700; color: var(--em-text-muted); font-family: var(--em-font-mono, monospace); min-width: 34px; }
.em-ds-fr-what b { font-weight: var(--em-fw-bold); }
.em-ds-q-txt { color: var(--em-text-secondary); display: block; margin-top: 2px; font-size: var(--em-fs-caption); }
.em-ds-fr-right { display: flex; align-items: center; gap: var(--em-space-2); }
.em-ds-launch { font: inherit; font-size: var(--em-fs-caption); font-weight: var(--em-fw-medium); border: 1px solid var(--em-accent); color: var(--em-accent); background: var(--em-accent-weak); border-radius: var(--em-radius-md); padding: 4px 10px; cursor: pointer; white-space: nowrap; }
.em-ds-launch-row { display: flex; align-items: baseline; gap: var(--em-space-3); flex-wrap: wrap; }
.em-ds-launch-row .em-ds-note { color: var(--em-text-muted); font-size: var(--em-fs-caption); }
/* an at-a-glance, NON-interactive launchable indicator in the summary (the button lives
   in the revealed detail body — DS-A11Y-1a) */
.em-ds-launchable-tag { font-size: var(--em-fs-caption); font-weight: var(--em-fw-medium); color: var(--em-accent); white-space: nowrap; }
.em-ds-no-launch { font-size: var(--em-fs-caption); color: var(--em-text-muted); max-width: 22ch; text-align: right; }
.em-ds-fr-detail { padding: var(--em-space-1) var(--em-space-4) var(--em-space-3) 52px; display: flex; flex-direction: column; gap: var(--em-space-2); background: var(--em-surface-1); }
.em-ds-kv { font-size: var(--em-fs-caption); }
.em-ds-kv .em-ds-k { text-transform: uppercase; letter-spacing: .04em; font-weight: var(--em-fw-medium); }
.em-ds-open-world { color: var(--em-text-primary); background: var(--em-surface-2); border: 1px dashed var(--em-text-muted); border-radius: var(--em-radius-md); padding: var(--em-space-2) var(--em-space-3); font-size: var(--em-fs-caption); }
.em-ds-handles { display: flex; gap: var(--em-space-3); flex-wrap: wrap; }
.em-ds-handle { border: 1px solid var(--em-state-candidate); border-radius: var(--em-radius-md); padding: 6px 9px; font-size: var(--em-fs-caption); }
.em-ds-hl { color: var(--em-state-candidate); font-weight: var(--em-fw-medium); text-transform: uppercase; letter-spacing: .04em; font-size: .68rem; }
.em-ds-hn { color: var(--em-text-primary); font-weight: var(--em-fw-medium); }
.em-ds-hid { color: var(--em-text-muted); font-size: .68rem; word-break: break-all; }
.em-ds-empty-cat { padding: var(--em-space-3) var(--em-space-4); color: var(--em-text-secondary); font-size: var(--em-fs-caption); font-style: italic; }
.em-ds-summ-strip { display: flex; gap: var(--em-space-4); flex-wrap: wrap; padding: var(--em-space-2) var(--em-space-4); border-top: 1px solid var(--em-surface-2); background: var(--em-surface-1); font-size: var(--em-fs-caption); }
.em-ds-s b { font-variant-numeric: tabular-nums; color: var(--em-text-primary); }
.em-ds-s span { color: var(--em-text-muted); }
.em-ds-nc { font-style: italic; color: var(--em-text-muted); font-weight: var(--em-fw-regular); }

.em-ds-toggle { display: inline-flex; border: 1px solid var(--em-text-muted); border-radius: var(--em-radius-md); overflow: hidden; }
.em-ds-toggle button { font: inherit; font-size: var(--em-fs-caption); font-weight: var(--em-fw-medium); border: none; background: var(--em-surface-1); color: var(--em-text-muted); padding: 4px 12px; cursor: pointer; }
.em-ds-toggle button[aria-pressed="true"] { background: var(--em-accent); color: var(--em-text-inverse); }
.em-ds-mapwrap { border: 1px solid var(--em-surface-2); border-radius: var(--em-radius-md); background: var(--em-surface-0); overflow: hidden; }
.em-ds-maptop { display: flex; justify-content: space-between; padding: var(--em-space-2) var(--em-space-4); border-bottom: 1px solid var(--em-surface-2); font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-ds-mapwrap svg { display: block; width: 100%; height: auto; }
.em-ds-legend { display: grid; grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); gap: 8px 18px; padding: var(--em-space-3) var(--em-space-4); border-top: 1px solid var(--em-surface-2); font-size: var(--em-fs-caption); }
.em-ds-li { display: flex; gap: var(--em-space-2); align-items: flex-start; }
.em-ds-sw { flex: none; font-family: var(--em-font-mono, monospace); font-weight: 700; width: 20px; }
.em-ds-lt { color: var(--em-text-secondary); }

/* (The former generic `.em-chip`/`.em-chips` pill — the source of the `.em-chip`
   collision that overrode the workbench status colours to grey — is DELETED. Its
   callers now use the canonical `ui/tag` (P2, `.em-tag--pill`) inside a `ui/token-row`
   (P3, `.em-token-row`). Renaming it off `.em-chip` restores the workbench
   `.em-status-badge` left-rule status colours.) */

/* ── coverage: at-a-glance headline + scannable dimension list (no matrix density) ── */
.em-ds-headline {
  font-size: var(--em-fs-body); color: var(--em-text-primary); margin: 0 0 var(--em-space-3);
  max-width: 80ch; line-height: 1.5;
}
.em-ds-covrow {
  display: grid;
  grid-template-columns: minmax(150px, 1.3fr) minmax(150px, 1fr) minmax(170px, 1.3fr) minmax(120px, 1fr);
  gap: var(--em-space-1) var(--em-space-4); align-items: baseline;
  padding: var(--em-space-2) 0; border-top: 1px solid var(--em-surface-2);
}
.em-ds-fam .em-ds-covrow:first-of-type { border-top: none; }
/* display:contents lifts the state + measure into the row's OWN grid columns so they align
   across every row (meter vs ?/∞), while keeping the `.em-ds-measure` ancestor the a11y gate
   reads. */
.em-ds-covrow .em-ds-measure { display: contents; }
.em-ds-mval-cell { display: flex; align-items: center; gap: var(--em-space-2); }
.em-ds-covrow .em-ds-meter { width: 84px; flex: none; }

/* ── the interactive fog-of-war map ─────────────────────────────────────────────── */
/* the map band GROWS to fill the pane height; its canvas fills the remaining space so the
   graph gets room (no dead white area below a short fixed box) — owner ask. */
.em-ds-band-map { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; margin-top: var(--em-space-4); }
.em-ds-band-map .em-ds-mapwrap { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; width: 100%; }
/* the stage is the flex-GROW child — the canvas + overlays fill it edge to edge. The legend
   and fog panel OVERLAY it (absolute) so they steal no height; only the thin maptop bar sits
   above, so the map canvas uses nearly the whole card. */
.em-ds-mapstage { position: relative; flex: 1 1 auto; min-height: 320px; }
.em-ds-map-canvas {
  width: 100%; height: 100%; display: block;
  /* a faint hatch = the unknown field the nodes float over (fog-of-war), CSP-safe. */
  background-color: var(--em-surface-0);
  background-image: repeating-linear-gradient(45deg, transparent 0 9px, var(--em-surface-1) 9px 10px);
}
/* a COMPACT single-strip legend below the canvas: a flex-wrap row of short items (not the
   tall multi-row grid), so it steals minimal height and the canvas above stays dominant. */
.em-ds-band-map .em-ds-legend {
  flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 2px var(--em-space-4); align-items: center;
  padding: var(--em-space-2) var(--em-space-3); border-top: 1px solid var(--em-surface-2);
  font-size: var(--em-fs-caption); grid-template-columns: none;
}
.em-ds-band-map .em-ds-legend .em-ds-li { flex: 0 1 auto; }
.em-ds-map-canvas:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--em-accent); }
.em-ds-fog-badge {
  position: absolute; top: 12px; right: 12px; pointer-events: none;
  display: flex; gap: var(--em-space-2); align-items: flex-start; max-width: 200px;
  background: var(--em-surface-2); border: 1px dashed var(--em-text-muted);
  border-radius: var(--em-radius-md); padding: var(--em-space-2) var(--em-space-3);
  font-size: var(--em-fs-caption); color: var(--em-text-muted);
}
.em-ds-fog-glyph { font-family: var(--em-font-mono, monospace); font-weight: 700; }

/* ── frontier row: human label leads; caret toggle; informational status tags ────── */
.em-ds-fr-cat-title { font-weight: var(--em-fw-bold); color: var(--em-text-primary); text-transform: none; letter-spacing: 0; }
.em-ds-fr-sum { display: flex; flex-direction: column; gap: var(--em-space-1); padding: var(--em-space-3) var(--em-space-4); cursor: pointer; }
.em-ds-fr-sum::-webkit-details-marker { display: none; }  /* our caret replaces the native marker */
.em-ds-fr-lead { display: flex; align-items: center; gap: var(--em-space-2); flex-wrap: wrap; }
.em-ds-fr-label { font-weight: var(--em-fw-bold); }
/* the disclosure affordance — a caret that clearly marks the ROW as the toggle, distinct
   from the (non-actionable) informational status tags beside it. Rotates when open. */
.em-ds-fr-caret { flex: none; color: var(--em-text-muted); display: inline-block;
  transition: transform 0.12s ease; line-height: 1; }
.em-ds-fr-caret::before { content: "\25B8"; }  /* ▸ */
.em-ds-fr-row[open] > .em-ds-fr-sum .em-ds-fr-caret { transform: rotate(90deg); }
.em-ds-f1-collapse > summary.em-ds-f1-sum {
  padding: var(--em-space-2) var(--em-space-4); cursor: pointer;
  font-size: var(--em-fs-caption); font-weight: var(--em-fw-medium); color: var(--em-text-secondary);
  border-top: 1px solid var(--em-surface-2); background: var(--em-surface-1);
}
.em-ds-iri {
  font-family: var(--em-font-mono, monospace); font-size: var(--em-fs-caption);
  color: var(--em-text-muted); word-break: break-all; cursor: text;
}

/* ── the Discovery-Story right inspector (selected item / cluster detail) ────────── */
/* the panel (`.em-inspector`) is overflow:hidden, so this owns the scroll; long IRIs and
   labels WRAP rather than clip at the panel edge (owner-flagged truncation bug). */
.em-ds-inspector { padding: var(--em-space-4); display: flex; flex-direction: column; gap: var(--em-space-2);
  height: 100%; overflow: auto; box-sizing: border-box; }
.em-ds-inspector, .em-ds-inspector * { min-width: 0; }
.em-ds-insp-kind { font-size: var(--em-fs-caption); text-transform: uppercase; letter-spacing: .05em; color: var(--em-text-muted); font-weight: var(--em-fw-bold); margin: 0; }
.em-ds-insp-title { font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); margin: 0; overflow-wrap: anywhere; }
.em-ds-insp-why { color: var(--em-text-secondary); font-size: var(--em-fs-caption); margin: 0; }
.em-ds-inspector .em-ds-handle .em-mono,
.em-ds-inspector .em-ds-insp-adv { overflow-wrap: anywhere; word-break: break-word; }
.em-ds-inspector .em-ds-handle { max-width: 100%; box-sizing: border-box; }
.em-ds-insp-open { color: var(--em-text-primary); background: var(--em-surface-1); border: 1px dashed var(--em-text-muted); border-radius: var(--em-radius-md); padding: var(--em-space-2) var(--em-space-3); font-size: var(--em-fs-caption); }
.em-ds-insp-members { margin: 0; padding-left: 18px; color: var(--em-text-secondary); font-size: var(--em-fs-caption); display: flex; flex-direction: column; gap: 2px; }
.em-ds-insp-facts { display: grid; grid-template-columns: auto 1fr; gap: 2px var(--em-space-3); margin: var(--em-space-1) 0; font-size: var(--em-fs-caption); }
.em-ds-insp-facts dt { color: var(--em-text-muted); text-transform: uppercase; letter-spacing: .04em; font-size: .68rem; }
.em-ds-insp-facts dd { margin: 0; color: var(--em-text-primary); word-break: break-word; }
.em-ds-insp-adv { font-size: var(--em-fs-caption); color: var(--em-text-secondary); margin: 0; }
.em-ds-insp-next { font-size: var(--em-fs-caption); color: var(--em-text-secondary); margin: var(--em-space-1) 0 0; }

/* ── ui/modal — the accessible dialog primitive (P6) ───────────────────────────
   Token-driven, both themes (every value is a --em-* custom property, so the dark
   overrides apply automatically). The fade is suppressed by the reduced-motion rule
   below. Focus-trap / focus-restoration / scroll-lock are the component's; the ring
   is the shared --em-focus-ring. */
/* Entrance is a subtle SLIDE only — never an opacity fade: fading a text-bearing surface
   drops its effective contrast mid-animation (axe scans a snapshot and would flag a genuine
   transient contrast failure). Transform does not affect colour, so contrast is correct on
   every frame. Suppressed under prefers-reduced-motion by the global rule below. */
@keyframes em-modal-in {
  from { transform: translateY(-6px); }
  to   { transform: translateY(0); }
}
.em-modal-overlay {
  position: fixed; inset: 0;
  background: rgba(16, 22, 30, 0.44);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 10vh var(--em-space-4) var(--em-space-4);
  z-index: 1000;
  overflow-y: auto;
}
.em-modal-panel {
  width: min(620px, 94vw);            /* :md default — right-sized, not a big empty box */
  max-height: 84vh;
  display: flex; flex-direction: column;
  background: var(--em-surface-0);
  border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-md);
  box-shadow: var(--em-elevation-overlay);
  animation: em-modal-in var(--em-motion-base) var(--em-motion-ease);
}
/* content-appropriate widths (ui/modal :size). Small = a short form (upload); large =
   a dense/wide modal (kept available for future use). The hosted body fills whichever. */
.em-modal-panel.em-modal-sm { width: min(460px, 94vw); }
.em-modal-panel.em-modal-lg { width: min(860px, 94vw); }
.em-modal-panel:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--em-space-3);
  padding: var(--em-space-4);
  border-bottom: 1px solid var(--em-border-subtle);
}
/* Short forms hosted by the shared modal: padded content and a stable action row. */
.em-modal-body {
  padding: var(--em-space-4); overflow-y: auto; min-height: 0;
  display: flex; flex-direction: column; gap: var(--em-space-3);
}
.em-modal-body > p { margin: 0; }
.em-modal-foot {
  display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--em-space-3);
  flex: none; padding: var(--em-space-4); border-top: 1px solid var(--em-border-subtle);
}
.em-modal-title { margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); color: var(--em-text-primary); }
.em-modal-close {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border: 1px solid transparent; border-radius: var(--em-radius-sm);
  background: none; color: var(--em-text-secondary); cursor: pointer;
  font-size: var(--em-fs-strong); line-height: 1;
}
.em-modal-close:hover { background: var(--em-surface-1); color: var(--em-text-primary); }
.em-modal-close:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
/* the hosted form body FILLS the panel (full-bleed to the modal padding) and scrolls
   inside it while the head stays pinned. The nested card chrome + narrow max-width the
   form carries when shown INLINE (in the catalogue) are dropped here, so there is no
   card-in-card and no whitespace column to the right — the modal IS the container. */
.em-modal-panel > .em-frontier-upload-form,
.em-modal-panel > .em-frontier-authoring-form,
.em-modal-panel > .em-frontier-authoring-empty {
  margin: 0; padding: var(--em-space-4); overflow-y: auto; min-height: 0;
  max-width: none; background: none; border: none; border-radius: 0;
}

/* the wizard body fills the panel exactly as the bare form bodies do above */
.em-modal-panel > .em-wiz {
  margin: 0; padding: var(--em-space-4); overflow-y: auto; min-height: 0;
  display: flex; flex-direction: column; gap: var(--em-space-4);
}
/* a form body nested INSIDE the wizard is already inside the modal's padding, so it
   drops its own card chrome — no card-in-card. */
.em-wiz .em-frontier-upload-form,
.em-wiz .em-frontier-template-approve {
  margin: 0; padding: 0; max-width: none; background: none; border: none;
}

/* ── the inline ADD-A-TARGET row (owner, 2026-08-29) ───────────────────────────
   Adding a target happens IN the table: a trailing `+` row that becomes editable in
   place, so the thing being created sits where it will live and the table never jumps.
   Each control keeps a real `<label for>` — VISUALLY HIDDEN, because a dense row has no
   space for visible labels but a screen reader still needs the accessible name. */
.em-tgt-add-row > td { padding: var(--em-space-2); border-bottom: none; }
.em-tgt-add-btn.em-linklike {
  display: inline-flex; align-items: center; gap: var(--em-space-2);
  padding: var(--em-space-1) var(--em-space-2); border-radius: var(--em-radius-sm);
  text-decoration: none; color: var(--em-accent); font-weight: var(--em-fw-medium);
}
.em-tgt-add-btn.em-linklike:hover { background: var(--em-accent-weak); }
.em-tgt-add-plus {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: var(--em-radius-sm);
  border: 1px solid currentColor; font-weight: var(--em-fw-bold); line-height: 1;
}
/* the open row is tinted so it reads as a DRAFT — something being composed, not a
   recorded target. It is reinforced by the row's own "Recorded as a declared objective"
   sentence, so the tint is never the only signal that this row is not yet real. */
.em-tgt-new-row > td {
  background: color-mix(in srgb, var(--em-accent) 5%, var(--em-surface-0));
  vertical-align: top;
}
.em-tgt-new-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--em-space-2); margin-bottom: var(--em-space-2);
}
.em-tgt-new-field { display: flex; min-width: 0; }
.em-tgt-new-field > .em-input, .em-tgt-new-field > .em-select { width: 100%; }
.em-tgt-new-actions { display: table-cell; white-space: nowrap; }
.em-tgt-new-actions > button { display: block; width: 100%; }
.em-tgt-new-actions > button + button { margin-top: var(--em-space-2); }
.em-tgt-new-status:empty { display: none; }
.em-tgt-empty-row > td { padding: var(--em-space-3) var(--em-space-2); }

/* ── discovery SOURCE picker — the two ways in, both visible at arrival ─────────
   Two equal-weight choices, not a spine with a hidden second route. Each card is a real
   <button>, so the choice is keyboard-operable and announced. The unavailable source is
   dimmed but NOT hidden and NOT disabled: its dialog is where the honest explanation
   lives, and hiding it recreates the very problem this replaced. Its state is carried in
   a TEXT tag, so the dimming is reinforcement and never the only signal. */
.em-src-picker { display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-src-head { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-src-title { margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); }
.em-src-lead { margin: 0; max-width: 70ch; font-size: var(--em-fs-body); }
.em-src-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--em-space-3);
}
.em-src-item { display: flex; }
/* the card overrides the .em-btn chrome it inherits from the button primitive: a button
   is the right SEMANTIC (one click, one choice), a chrome button is the wrong shape. */
.em-src-card.em-btn {
  flex: 1; display: flex; align-items: flex-start; gap: var(--em-space-3);
  width: 100%; height: auto; text-align: left; white-space: normal;
  padding: var(--em-space-4);
  background: var(--em-surface-0);
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-md);
  font-weight: var(--em-fw-regular);
}
.em-src-card.em-btn:hover:not(:disabled) {
  border-color: var(--em-accent); background: var(--em-accent-weak);
}
.em-src-card.em-btn:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }
.em-src-card.is-unavailable { opacity: 0.72; }
.em-src-glyph { flex: none; font-size: 1.5rem; line-height: 1.2; }
.em-src-text { display: flex; flex-direction: column; gap: var(--em-space-1); min-width: 0; }
.em-src-label { font-weight: var(--em-fw-bold); color: var(--em-text-primary); }
.em-src-blurb { font-size: var(--em-fs-body); color: var(--em-text-secondary); }
.em-src-detail { font-size: var(--em-fs-caption); line-height: 1.45; }
.em-src-state { margin-top: var(--em-space-1); }

/* ── wizard — the steps, inside a dialog ───────────────────────────────────────
   The spine is a compact horizontal progress bar. State is carried in TEXT (a visually
   hidden "done / you are here / not reached yet" per step) as well as the class, so it
   is never colour alone. A step beyond what has been reached is `disabled` — a usability
   hint only; nothing downstream trusts it, and the server re-checks every act. */
.em-wiz { display: flex; flex-direction: column; gap: var(--em-space-4); }
.em-wiz-spine {
  list-style: none; margin: 0; padding: 0 0 var(--em-space-3);
  display: flex; flex-wrap: wrap; gap: var(--em-space-1);
  border-bottom: 1px solid var(--em-border-subtle);
}
.em-wiz-spine-step { display: flex; align-items: center; }
.em-wiz-spine-step + .em-wiz-spine-step::before {
  content: "›"; color: var(--em-text-muted); padding: 0 var(--em-space-1);
}
.em-wiz-spine-btn.em-linklike {
  display: inline-flex; align-items: center; gap: var(--em-space-1);
  padding: var(--em-space-1) var(--em-space-2); border-radius: var(--em-radius-sm);
  color: var(--em-text-muted); text-decoration: none; font-size: var(--em-fs-caption);
}
.em-wiz-spine-btn.em-linklike:disabled { cursor: default; opacity: 0.65; }
.em-wiz-spine-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%;
  border: 1px solid currentColor; font-size: 0.6875rem; line-height: 1;
}
.is-done .em-wiz-spine-btn.em-linklike { color: var(--em-tone-approved); }
.is-shown .em-wiz-spine-btn.em-linklike {
  color: var(--em-text-primary); font-weight: var(--em-fw-bold);
  background: var(--em-accent-weak);
}
.em-wiz-desc { margin: 0; max-width: 68ch; font-size: var(--em-fs-body); line-height: 1.5; }
.em-wiz-panel { display: flex; flex-direction: column; gap: var(--em-space-3); min-height: 0; }
.em-wiz-body { display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-wiz-hint { margin: 0; font-size: var(--em-fs-body); }
.em-wiz-done { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-wiz-prereqs { margin: 0; padding-left: var(--em-space-4); display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-wiz-launch { display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-wiz-nav {
  display: flex; justify-content: flex-end; gap: var(--em-space-2);
  padding-top: var(--em-space-3); border-top: 1px solid var(--em-border-subtle);
}
/* the honest "not built yet" notice — a tag (text) plus the reason, never a dim button
   left to speak for itself. */
.em-wiz-notice {
  display: flex; flex-direction: column; gap: var(--em-space-2);
  padding: var(--em-space-3);
  background: color-mix(in srgb, var(--em-tone-conflict) 7%, var(--em-surface-0));
  border: 1px solid color-mix(in srgb, var(--em-tone-conflict) 30%, var(--em-surface-0));
  border-radius: var(--em-radius-md);
}
.em-wiz-notice-text { margin: 0; font-size: var(--em-fs-body); line-height: 1.5; color: var(--em-text-primary); }

/* ── BYO guided stepper ────────────────────────────────────────────────────────
   A legible ordered flow. State is carried in TEXT (Done / You are here / To do) and
   a left rule — never colour alone; exactly one row is aria-current="step". */
/* The stepper FILLS its catalogue column (left half of the pane) — no narrow box, no side
   gutter. On an ultrawide monitor a half-pane column can get very wide, so the step TEXT has
   a generous readability ceiling (~760px) to avoid over-long lines; the panel itself still
   fills the column. */
.em-byo-stepper {
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-md);
  background: var(--em-surface-1);
  padding: var(--em-space-4);
  display: flex; flex-direction: column; gap: var(--em-space-3);
}
.em-byo-head { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-byo-title { margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); color: var(--em-text-primary); }
.em-byo-lead { margin: 0; line-height: 1.45; max-width: 760px; }
.em-byo-next {
  margin: var(--em-space-1) 0 0; font-size: var(--em-fs-body); color: var(--em-text-secondary);
  padding: var(--em-space-1) var(--em-space-3);
  border-left: 3px solid var(--em-accent); background: var(--em-accent-weak);
  border-radius: 0 var(--em-radius-sm) var(--em-radius-sm) 0;
}
.em-byo-next strong { color: var(--em-text-primary); }
.em-byo-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-2); }
/* A COLLAPSED spine: the journey stays visible, only the step being worked on opens.
   Five equal-weight cards meant four rows of always-irrelevant prose were as loud as the
   one row that mattered, and a step hosting real work (approve, publish) could not fit. */
.em-byo-step {
  border: 1px solid var(--em-border-subtle);
  border-left: 3px solid var(--em-border-strong);
  border-radius: var(--em-radius-sm);
  background: var(--em-surface-0);
}
.em-byo-step-head {
  display: flex; align-items: center; gap: var(--em-space-3);
  width: 100%;
  padding: var(--em-space-2) var(--em-space-3);
  background: none; border: 0; border-radius: var(--em-radius-sm);
  font: inherit; text-align: left; cursor: pointer;
  color: var(--em-text-primary);
}
.em-byo-step-head:hover { background: var(--em-surface-1); }
.em-byo-step-head:focus-visible {
  outline: 2px solid var(--em-focus); outline-offset: -2px;
}
.em-byo-step-head .em-byo-step-label { flex: 1 1 auto; min-width: 0; }
.em-byo-panel {
  padding: 0 var(--em-space-3) var(--em-space-3) calc(var(--em-space-3) + 26px + var(--em-space-3));
  display: flex; flex-direction: column; gap: var(--em-space-2);
}
.em-byo-panel[hidden] { display: none; }
.em-byo-prereqs {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--em-space-1);
  font-size: var(--em-fs-body); color: var(--em-text-secondary);
}
.em-byo-prereqs li { padding-left: var(--em-space-3); position: relative; }
.em-byo-prereqs li::before {
  content: "•"; position: absolute; left: 0; color: var(--em-text-tertiary);
}
.em-byo-step.is-current { border-left-color: var(--em-accent); background: var(--em-accent-weak); }
.em-byo-step.is-done    { border-left-color: var(--em-state-approved); }
.em-byo-step.is-todo    { opacity: 0.92; }
.em-byo-num {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px;
  border: 1px solid var(--em-border-strong); border-radius: 50%;
  font-size: var(--em-fs-caption); font-weight: var(--em-fw-bold);
  color: var(--em-text-secondary); background: var(--em-surface-1);
}
.em-byo-step.is-current .em-byo-num { border-color: var(--em-accent); color: var(--em-accent); }
.em-byo-step.is-done .em-byo-num { border-color: var(--em-state-approved); color: var(--em-state-approved); }
.em-byo-body { display: flex; flex-direction: column; gap: var(--em-space-1); min-width: 0; }
.em-byo-step-head { display: flex; align-items: baseline; gap: var(--em-space-2); flex-wrap: wrap; }
.em-byo-step-label { font-weight: var(--em-fw-medium); color: var(--em-text-primary); }
.em-byo-state {
  font-size: var(--em-fs-caption); font-weight: var(--em-fw-bold);
  letter-spacing: 0.03em; text-transform: uppercase;
  padding: 1px var(--em-space-2); border-radius: var(--em-radius-sm);
  border: 1px solid var(--em-border-strong); color: var(--em-text-secondary);
}
.em-byo-state.is-current { border-color: var(--em-accent); color: var(--em-accent); }
.em-byo-state.is-done { border-color: var(--em-state-approved); color: var(--em-state-approved); }
.em-byo-step-desc { margin: 0; line-height: 1.4; max-width: 760px; }
.em-byo-step-action { margin-top: var(--em-space-1); }
.em-byo-hint { margin: 0; font-size: var(--em-fs-caption); }

/* terminology cue by the Targets header (objective vs discovered target) */
.em-frontier-panel-heading { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-frontier-term-cue { margin: 0; font-size: var(--em-fs-caption); line-height: 1.45; max-width: 78ch; }
.em-frontier-term-cue strong { color: var(--em-text-secondary); font-weight: var(--em-fw-bold); }

/* Respect reduced-motion: suppress all non-essential motion (SPEC-3 §21). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Review-task header, inspector hierarchy, pane collapse, and the responsive
   policy (brief items 2, 6, 9, 10).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── (item 2) The review-task header ─────────────────────────────────────────
   The QUESTION, not the facets, is the dominant thing in the inspector. It sits
   above the tab bar so it stays put while the reviewer moves between Resource /
   Assertion / Evidence / Provenance — those are ways of interrogating ONE
   question, not four pages. */
.em-insp-task { display: flex; flex-flow: column nowrap; gap: var(--em-space-2); }
.em-insp-task-claim {
  margin: 0;
  font-size: var(--em-fs-strong);
  font-weight: var(--em-fw-bold);
  line-height: 1.3;
  color: var(--em-text-primary);
  overflow-wrap: anywhere;
}
.em-insp-task-facts { flex-wrap: wrap; font-size: var(--em-fs-caption); }
.em-insp-task-conf { color: var(--em-text-secondary); font-variant-numeric: tabular-nums; }
.em-insp-task-reason {
  margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-secondary);
  line-height: var(--em-lh-body);
}

/* ── (item 6) Inspector hierarchy ────────────────────────────────────────────
   The decision band already pinned to the bottom of the inspector's own scroll
   region; these rules stop the sections above competing with it for weight, and
   let the decision buttons WRAP so they never push the reason field or the
   confirm control below the fold on a 900px-tall laptop. */
.em-insp-decision-from {
  font-size: var(--em-fs-caption); color: var(--em-text-muted);
  margin: 0 var(--em-space-1);
}
/* Decisions PACK two-up when the pane affords it and fall to one-up at the 320px
   floor — they never overflow the pane, and the reason field + confirm control
   stay above the fold on a 900px-tall laptop. */
.em-action-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: var(--em-space-2);
}
.em-action { display: flex; flex-flow: column nowrap; gap: 2px; min-width: 0; }
.em-action .em-action-live { width: 100%; text-align: left; white-space: normal; }
.em-action-why { font-size: var(--em-fs-caption); line-height: 1.35; }
/* (item 6) Approve must NOT be visually easier than dispute or reject. Every
   decision uses the SAME secondary control face — no primary/danger accent is
   spent on any one of them, so the interface expresses no preference about which
   way a reviewer should rule. */
.em-action-live { font-size: var(--em-fs-body); }

/* Origin / activity progressive disclosure (item 7). */
.em-origin-label { font-weight: var(--em-fw-medium); }
.em-origin-more, .em-activity-more { margin-top: var(--em-space-1); }
.em-activity-short { font-size: var(--em-fs-caption); }
.em-activity-iri { margin: var(--em-space-1) 0 0; font-size: var(--em-fs-caption); overflow-wrap: anywhere; }

/* ── (item 9) Independently collapsible panes ────────────────────────────────
   PRESENTATION only — a collapsed pane hides nothing that one keystroke cannot
   restore, preserves the user's own drag width, keeps its subtree mounted, and is
   never access control. */
/* The wrapper is a flex ITEM of `.em-inspector` and a flex CONTAINER for the
   panel. Both `min-width:0` and `width:100%` are load-bearing: without them the
   default `min-width:auto` lets the panel size to its CONTENT, so a long evidence
   line or a wide decision button pushed the inspector past its pane and clipped
   the tab bar, the Clear control and the right-hand half of every row. */
.em-inspector-inner {
  display: flex; flex-flow: column nowrap;
  width: 100%; min-width: 0; height: 100%; min-height: 0;
}
.em-inspector { min-width: 0; }
.em-insp-titlebar-actions { display: inline-flex; align-items: center; gap: var(--em-space-2); }
.em-inspector-collapsed { border-left: 1px solid var(--em-border-subtle); background: var(--em-surface-1); }
.em-pane-collapse {
  font: inherit; font-size: var(--em-fs-caption); line-height: 1;
  color: var(--em-text-secondary);
  background: var(--em-control-face); border: 1px solid var(--em-control-border);
  border-radius: var(--em-radius-sm); padding: 2px var(--em-space-1); cursor: pointer;
}
.em-pane-collapse:hover  { background: var(--em-control-face-hover); color: var(--em-text-primary); }
.em-pane-collapse:active { background: var(--em-control-face-active); }
.em-pane-collapse:focus-visible { outline: none; box-shadow: var(--em-focus-ring); }

/* The inspector tab bar must never CLIP a facet name — `Provenance` was cut to
   `Provenan` at 1280. It WRAPS to a second row rather than scrolling: a scroll
   region would leave the fourth facet invisible with no affordance, and every
   facet has to be visibly reachable. */
.em-insp-tabs { flex-wrap: wrap; }
.em-insp-tab { white-space: nowrap; }

/* ── (item 9) The responsive policy ──────────────────────────────────────────
   The central graph/table workspace is PRIMARY at every width. The graph's side
   rail (focus · legend · live activity · fps) is the first thing to give way,
   because it is contextual rather than the workspace itself.

   ≥1600px  three panes plus a 240px side rail — the shipped wide layout.
   1400–1599px  the side rail narrows to 200px. Both auxiliary panes stay open;
                either can be collapsed by pointer or keyboard.
   <1400px  the side rail STACKS BENEATH the canvas across the full width, so the
                canvas takes the whole workspace: measured at 1280, that is 700px of
                canvas instead of 495px beside a 205px rail. The rail's content is
                unchanged and still fully reachable — it moves, it is not dropped.

   Nothing becomes permanently inaccessible at any width, and no content is
   removed — the panes reflow, and both collapse toggles remain operable from the
   keyboard (⌘K "Show or hide the inspector"; the rail's own toggle). Resizing
   changes only geometry: selection, filters and any typed reason live in app-db
   and are untouched. */
@media (max-width: 1599px) {
  .em-graph-side { flex-basis: 200px; padding: var(--em-space-2); gap: var(--em-space-3); }
}
@media (max-width: 1399px) {
  .em-graph-main { flex-flow: column nowrap; }
  .em-graph-side {
    flex: 0 0 auto; max-height: 40%;
    border-left: none; border-top: 1px solid var(--em-border-subtle);
    /* side by side rather than stacked, so the reclaimed height is not spent */
    display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--em-space-3); align-content: start;
  }
}

/* ── (item 10) Typography + section rhythm ───────────────────────────────────
   Restrained identity, spent in two controlled places: the brand wordmark and the
   ONE accent that marks the reviewer's decision. Everything else stays quiet and
   precise. No gradients, no shadows beyond the existing overlay token, no new
   palette — these rules only re-rank weight and spacing using tokens that already
   exist, and they change no semantic status encoding. */
.em-brand { letter-spacing: -0.01em; }
.em-rq-title { letter-spacing: -0.005em; }

/* Long evidence metadata (filename · media type · integrity · classification) must
   WRAP inside the inspector pane, never clip at its right edge. */
.em-evref-list { list-style: none; margin: 0; padding: 0; }
.em-evref-list > li { overflow-wrap: anywhere; line-height: var(--em-lh-body); margin-bottom: var(--em-space-1); }
.em-prov-summary, .em-statement, .em-impact-edge { overflow-wrap: anywhere; }

/* the selected row's priority breakdown reads as one sentence, so the separators
   need real space around them (an inline ::before space collapses). */
.em-rq-reason-item::before { content: "\00b7"; margin: 0 var(--em-space-1); color: var(--em-text-muted); }

/* ══════════════════════════════════════════════════════════════════════════════
   ADMINISTRATION module (ADR-0080 D6, increment A-2 — READ-ONLY)

   NO new visual language and NO second token set. Every rule below is composed
   from the EXISTING `--em-*` tokens (surfaces, borders, text, tone, spacing,
   radius) and every structural pattern is one the workbench and the frontier
   already use: `.em-tbl` for tables, `.em-tables*` for the workspace frame,
   `.em-rail-head` for rail group headings, `.em-card`-family borders for panels.
   Nothing here introduces a gradient, a shadow, an animation or a colour that did
   not already exist, and nothing carries meaning by colour alone.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── rail ─────────────────────────────────────────────────────────────────── */
.em-admin-rail { display: flex; flex-direction: column; gap: var(--em-space-4); padding: var(--em-space-3); }
.em-admin-rail-group { display: flex; flex-direction: column; gap: 2px; }
.em-admin-rail-group > .em-rail-head { padding: 0 var(--em-space-2) var(--em-space-1); }
.em-admin-nav {
  display: flex; align-items: baseline; gap: var(--em-space-2);
  width: 100%; text-align: left; font: inherit;
  padding: var(--em-space-1) var(--em-space-2);
  border: 1px solid transparent; border-radius: var(--em-radius-sm);
  background: transparent; color: var(--em-text-secondary); cursor: pointer;
}
.em-admin-nav:hover { background: var(--em-surface-2); }
.em-admin-nav.em-active {
  background: var(--em-accent-weak); border-color: var(--em-accent);
  color: var(--em-text-primary); font-weight: var(--em-fw-medium);
}
.em-admin-nav-label { flex: 1 1 auto; }
.em-admin-nav-count { color: var(--em-text-muted); font-variant-numeric: tabular-nums; font-size: var(--em-fs-caption); }
/* "later" is a WORD, not a colour or an opacity: a section that does not exist yet
   must read as such in monochrome and at 200% zoom. */
.em-admin-nav-later { color: var(--em-text-muted); }
.em-admin-nav-note {
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--em-text-muted); border: 1px solid var(--em-border-subtle);
  border-radius: 999px; padding: 0 6px;
}

/* ── workspace + inspector chrome ─────────────────────────────────────────── */
.em-admin-sub { margin: 2px 0 0; color: var(--em-text-secondary); font-size: var(--em-fs-caption); max-width: 92ch; }
/* THE RECORD SCROLLS; THE ACTS ARE PINNED (2026-08-31).
   `height: 100%` on this made it claim the whole pane while `.em-admin-acts` is its
   FLEX SIBLING, so the two together exceeded the container and the acts were pushed
   down by however much the record happened to need — which is why "What you can do
   about this account" sat at the bottom for one person and mid-pane for another
   (owner-reported: the authority list is 36 actions for one and 29 for another).
   `flex: 1 1 0` + `min-height: 0` lets it TAKE the leftover space instead of
   demanding all of it. */
.em-admin-inspector { display: flex; flex-direction: column; gap: var(--em-space-3); padding: var(--em-space-3); overflow: auto; flex: 1 1 0; min-height: 0; }
.em-admin-inspector-head { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-admin-inspector-title { margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); overflow-wrap: anywhere; }

.em-admin-panel {
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-md);
  background: var(--em-surface-0); padding: var(--em-space-3);
}
.em-admin-panel--note { background: var(--em-surface-1); }
.em-admin-panel-title { margin: 0 0 var(--em-space-1); font-size: var(--em-fs-body); font-weight: var(--em-fw-medium); }
.em-admin-panel-body { margin: 0; line-height: var(--em-lh-body); overflow-wrap: anywhere; }

/* ── identity integrity (2026-08-30) ─────────────────────────────────────────
   Four tones, and they must stay distinguishable: a check that found something, a
   check that found nothing, a check that could not run, and a read that failed. The
   last two are NOT green — an unreadable store is not a clean one, and the colour is
   never the only channel (each tone has its own headline sentence). */
.em-admin-panel--integrity { margin-top: var(--em-space-3); }
.em-admin-panel--findings { border-left: 3px solid var(--em-state-conflict); }
.em-admin-panel--ok { border-left: 3px solid var(--em-state-approved); }
.em-admin-panel--unchecked,
.em-admin-panel--failed { border-left: 3px solid var(--em-state-disputed); }

.em-admin-finding-list { list-style: none; margin: var(--em-space-2) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-admin-finding-label { margin: 0; font-weight: var(--em-fw-medium); }
.em-admin-finding-detail { margin: var(--em-space-1) 0 0; line-height: var(--em-lh-body);
  color: var(--em-text-secondary); max-width: 92ch; }
.em-admin-finding-ids { list-style: none; margin: var(--em-space-1) 0 0; padding: 0;
  font-size: var(--em-fs-caption); }
.em-admin-finding-ids li { overflow-wrap: anywhere; }

/* ── states (loading / failure / mismatch / empty) ────────────────────────── */
.em-admin-state {
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-md);
  background: var(--em-surface-1); padding: var(--em-space-4);
  display: flex; flex-direction: column; gap: var(--em-space-2);
  margin: var(--em-space-3) 0;
}
/* A PROBLEM is marked by a left rule + its own words, exactly like a status badge —
   never by fill alone, so it survives monochrome. */
.em-admin-state--problem { border-left: 3px solid var(--em-tone-conflict); }
.em-admin-state-title { margin: 0; font-weight: var(--em-fw-medium); }
.em-admin-state-body { margin: 0; line-height: var(--em-lh-body); }
.em-admin-state-note { margin: 0; font-size: var(--em-fs-caption); color: var(--em-text-muted); line-height: var(--em-lh-body); }
.em-admin-state-actions { display: flex; gap: var(--em-space-2); }
.em-admin-refreshing { margin: var(--em-space-1) 0 0; font-size: var(--em-fs-caption); color: var(--em-text-muted); }

/* ── the authority-changed notice (a shell-level bar under the context bar) ── */
.em-admin-notice {
  flex: 0 0 auto;
  display: flex; align-items: flex-start; gap: var(--em-space-3);
  padding: var(--em-space-2) var(--em-space-3);
  border-bottom: 1px solid var(--em-tone-conflict);
  background: var(--em-surface-2);
}
.em-admin-notice-g { color: var(--em-tone-conflict); font-weight: var(--em-fw-bold); }
.em-admin-notice-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.em-admin-notice-title { margin: 0; font-weight: var(--em-fw-medium); }
.em-admin-notice-body { margin: 0; font-size: var(--em-fs-caption); overflow-wrap: anywhere; }
.em-admin-notice-x { flex: 0 0 auto; }

/* ── the disabled write controls ──────────────────────────────────────────── */
/* SHOWN and EXPLAINED. The reason is VISIBLE text beside the control, never a
   tooltip: critical information is never hover-only. `aria-disabled` (not the
   native attribute) keeps the control focusable so the reason is announced. */
.em-admin-writeacts {
  margin: var(--em-space-4) 0 0; padding: var(--em-space-3) 0 0;
  border-top: 1px solid var(--em-border-subtle);
  display: flex; flex-direction: column; gap: var(--em-space-2);
}
.em-admin-writeacts-lead { margin: 0; font-size: var(--em-fs-caption); max-width: 92ch; line-height: var(--em-lh-body); }
.em-admin-writeact { display: flex; align-items: baseline; gap: var(--em-space-3); flex-wrap: wrap; }
/* The shared `.em-btn-inert` now carries fill/border/cursor; this only keeps the
   extra recession that distinguishes an act this build never offers from one that
   is merely unavailable right now. */
.em-admin-writeact-btn[aria-disabled="true"] { opacity: 0.75; cursor: default; }
.em-admin-writeact-why { flex: 1 1 24ch; min-width: 0; font-size: var(--em-fs-caption); line-height: var(--em-lh-body); }

/* ── people table cells ───────────────────────────────────────────────────── */
.em-admin-cap { white-space: nowrap; font-size: var(--em-fs-caption); }
.em-admin-sub, .em-tbl .em-admin-sub { display: block; }
.em-tbl-rowbtn .em-admin-sub { font-size: var(--em-fs-caption); }
.em-admin-iri { margin: var(--em-space-1) 0 0; overflow-wrap: anywhere; }
.em-mono { font-family: var(--em-font-mono); font-size: var(--em-fs-caption); }

/* ── approval authority, stated on the record (2026-08-30) ────────────────────
   The table renders approval as a token under a column header; the record has no
   column header, so it states the answer in words. Quiet No / marked Yes — the
   asymmetry the table established, preserved. Neither uses colour as its only
   channel: the No is muted TEXT with a glyph, the Yes is a toned tag whose LABEL
   already says what it is. */
.em-admin-approval { margin: 0 0 var(--em-space-2); }
.em-admin-approval--no { font-size: var(--em-fs-caption); line-height: var(--em-lh-body); }
.em-admin-approval-why {
  margin: var(--em-space-1) 0 0; font-size: var(--em-fs-caption);
  color: var(--em-text-secondary); line-height: var(--em-lh-body);
}

/* ── grants + authority reference ─────────────────────────────────────────── */
.em-admin-grantgroups, .em-admin-authgroups { display: flex; flex-direction: column; gap: var(--em-space-4); }
.em-admin-grantgroup, .em-admin-authgroup {
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-md);
  background: var(--em-surface-0); overflow: hidden;
}
/* Group tables own their cell insets. Direct prose needs its own inset too,
   including every role card and the structural tenant-access card. */
.em-admin-authgroup > h3:not(.em-admin-authgroup-head),
.em-admin-authgroup > p {
  margin: var(--em-space-3) var(--em-space-4);
  overflow-wrap: anywhere;
}
.em-admin-authgroup > h3:not(.em-admin-authgroup-head):first-child {
  margin-top: var(--em-space-4);
}
.em-admin-authgroup > p:last-child { margin-bottom: var(--em-space-4); }
.em-admin-authgroup > .em-tbl > caption {
  padding: var(--em-space-3) var(--em-space-4);
  text-align: left; color: var(--em-text-secondary);
}
.em-admin-authgroup > .em-admin-authgroup-head,
.em-admin-authgroup > .em-tbl th,
.em-admin-authgroup > .em-tbl td {
  padding-inline: var(--em-space-4);
}
.em-admin-grantgroup-head, .em-admin-authgroup-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--em-space-3);
  margin: 0; padding: var(--em-space-2) var(--em-space-3);
  background: var(--em-surface-1); border-bottom: 1px solid var(--em-border-subtle);
  font-size: var(--em-fs-body); font-weight: var(--em-fw-regular);
}
.em-admin-grantgroup-blurb { font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-admin-grantgroup-count { color: var(--em-text-muted); font-variant-numeric: tabular-nums; font-size: var(--em-fs-caption); }
.em-admin-grantlist { list-style: none; margin: 0; padding: 0; }
.em-admin-grant { padding: var(--em-space-2) var(--em-space-3); border-bottom: 1px solid var(--em-border-subtle); }
.em-admin-grant:last-child { border-bottom: none; }
.em-admin-grant-head { display: flex; align-items: baseline; gap: var(--em-space-2); flex-wrap: wrap; }
.em-admin-grant-action { overflow-wrap: anywhere; }
.em-admin-grant-from { font-size: var(--em-fs-caption); }
/* The server's sentence where a granting-role list would go, for an action every
   account in the tenant holds. Muted like the role tags it replaces, never a tag —
   it is prose, and it is not a grant anyone made. */
.em-admin-grant-everyone { font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-admin-grant-label { margin: 2px 0 0; font-size: var(--em-fs-caption); }
.em-admin-grant-why { margin: 2px 0 0; font-size: var(--em-fs-caption); color: var(--em-text-secondary); line-height: var(--em-lh-body); }
.em-admin-authgroup .em-tbl { table-layout: fixed; }
.em-admin-authgroup .em-tbl th { position: static; }
/* Token rows inside a fixed-width cell must WRAP, or a two-token constraints cell
   clips at the column edge instead of taking a second line. */
.em-admin-authgroup .em-token-row { flex-wrap: wrap; row-gap: var(--em-space-1); }
.em-admin-authgroup td { overflow-wrap: anywhere; }
.em-admin-consequence { line-height: var(--em-lh-body); font-size: var(--em-fs-caption); color: var(--em-text-secondary); }

/* ── authority-reference filters ──────────────────────────────────────────── */
.em-admin-authcontrols { display: flex; align-items: flex-end; gap: var(--em-space-4); flex-wrap: wrap; margin: var(--em-space-2) 0 0; }
.em-admin-classfilter { display: flex; gap: var(--em-space-2); flex-wrap: wrap; }
/* SURFACE-0, deliberately, and NOT `--em-control-face`. The chip contains a toned
   `ui/tag`, whose tone colours are chosen against the SURFACE ramp; on the control
   face they fell to 3.7–4.5:1 and axe reported five serious contrast failures across
   both themes. A control that hosts a design-system token must sit on the background
   that token was designed against. The control affordance is carried by the border,
   the hover and the pressed state instead of by a filled face. */
.em-admin-chip {
  display: inline-flex; align-items: baseline; gap: var(--em-space-1);
  font: inherit; font-size: var(--em-fs-caption);
  padding: 2px var(--em-space-2);
  border: 1px solid var(--em-control-border); border-radius: var(--em-radius-sm);
  background: var(--em-surface-0); color: var(--em-text-primary); cursor: pointer;
}
.em-admin-chip:hover { background: var(--em-surface-1); }
/* The pressed chip is marked by a border AND a fill AND a weight AND `aria-pressed`
   — four channels, so the selected filter is never carried by colour alone. */
.em-admin-chip[aria-pressed="true"] { background: var(--em-accent-weak); border-color: var(--em-accent); font-weight: var(--em-fw-medium); }
/* `--em-text-secondary`, not `--em-text-muted`: the count is information, and muted
   failed contrast in dark. Muted is reserved for genuinely secondary prose on a
   surface. */
.em-admin-chip-count { color: var(--em-text-secondary); font-variant-numeric: tabular-nums; }
.em-admin-authcontrols .em-form-field { margin: 0; min-width: 22ch; }

/* ── record ───────────────────────────────────────────────────────────────── */
.em-admin-history { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-admin-history-item { border-left: 2px solid var(--em-border-strong); padding-left: var(--em-space-2); }
.em-admin-history-what { margin: 0; }
.em-admin-history-meta { margin: 0; font-size: var(--em-fs-caption); }
.em-admin-history-reason { margin: 2px 0 0; font-size: var(--em-fs-caption); color: var(--em-text-secondary); overflow-wrap: anywhere; }

/* The Authority reference's scroll region is KEYBOARD-REACHABLE.

   Its content is fifty rows of text with no focusable element in it, so the scroller
   was unreachable by keyboard — axe `scrollable-region-focusable`, serious, in both
   themes. A long region a keyboard user cannot scroll is unreadable to them. It is
   therefore a named, focusable region. The People table does NOT need this: its rows
   are real buttons, so the same scroller already has focusable content. */
.em-admin-scrollregion:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--em-accent);
}

/* The status cell keeps its badge and its MANDATORY scheme text on one line. The
   scheme is structural in `ui.status-badge` (G7) and is not negotiable; giving the
   column room is the way to honour it without spending three lines of vertical space
   on every row. Mirrors `.em-tbl-state`'s min-width in the workbench tables. */
/* Fixed layout so the colgroup percentages are honoured. The shared
   `.em-tbl-primary { width: 100% }` rule must be neutralised inside this table or it
   overrides them and pushes the last column off the edge. */
.em-admin-people-tbl { table-layout: fixed; }
.em-admin-people-tbl .em-tbl-primary { width: auto; min-width: 0; }
.em-admin-people-tbl td, .em-admin-people-tbl th { overflow-wrap: anywhere; }
.em-admin-tbl-status .em-status-badge { white-space: nowrap; }

/* ═══════════════════════════════════════════════════════════════════════════
   SIGN IN (ADR-0080 A-1) — the first screen anyone sees, and until it is passed
   the only one.

   NO NEW VISUAL LANGUAGE. Every value here is an existing `--em-*` token: the
   same surfaces, the same border and text colours, the same `--em-tone-*` state
   colours, the same radii and spacing scale, the same `--em-focus-ring`. The
   controls are the shipped `ui/form` + `ui/button` primitives with their own
   `.em-input` / `.em-btn` rules — nothing is re-skinned for this screen.

   It is quiet on purpose. A sign-in screen that looks like a landing page invites
   a person to read it as marketing and skip the four things on it they need.

   LAYOUT: one column, no fixed heights, `overflow:auto` on the scroller. That is
   what makes 200% zoom work — nothing is clipped, it just gets taller. Measured
   at 1440×900 and 1280×800, light and dark, at 100% and 200%.
   ══════════════════════════════════════════════════════════════════════════ */
.em-login {
  height: 100%;
  overflow: auto;
  background: var(--em-surface-0);
  color: var(--em-text-primary);
  font-family: var(--em-font-sans);
  font-size: var(--em-fs-body);
  line-height: var(--em-lh-body);
  /* Centred while it fits, top-aligned the moment it does not — `safe center`
     never pushes the top of the form above the viewport, which is the classic way
     a centred login form becomes unusable at 200% zoom. */
  display: flex;
  justify-content: center;
  align-items: safe center;
  padding: var(--em-space-6) var(--em-space-4);
}

.em-login-col {
  width: 100%;
  /* ~52ch of measure. Wide enough that the standing facts are not a ribbon of
     two-word lines; narrow enough that the form reads as a form. */
  max-width: 34rem;
  display: flex;
  flex-direction: column;
  gap: var(--em-space-5);
}

.em-login-head { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-login-brand {
  margin: 0;
  font-size: var(--em-fs-caption);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--em-text-secondary);
}
/* `--em-fs-title` is the largest type in the design system and it stops here.
   There is no marketing scale. */
.em-login-h { margin: 0; font-size: var(--em-fs-title); font-weight: var(--em-fw-bold); }

/* ── the form ───────────────────────────────────────────────────────────────
   Fields stack; `ui/form`'s own `.em-form-field` rules supply the label/control
   spacing, so this adds rhythm and nothing else. */
.em-login-form { display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-login-form .em-form-field { margin: 0; }
.em-login-form .em-input { width: 100%; box-sizing: border-box; }
.em-login-actions { display: flex; margin-top: var(--em-space-1); }
/* Full-width submit: it is the only action on the screen, so there is nothing for
   it to be ambiguous against. */
.em-login-submit { width: 100%; justify-content: center; }

/* ── outcome / notice panel ─────────────────────────────────────────────────
   ONE panel shape for every statement this screen makes. Status is carried by the
   TITLE TEXT; the left rule is reinforcement, so the panel still reads correctly
   in monochrome, at any zoom, and with the stylesheet off. Same idiom as
   `.em-admin-state`, deliberately — a person who has used the admin module has
   already learned to read it. */
/* ── the boot resume state (B7; SR-SESS-1) ────────────────────────────────────
   Shown for the single round trip in which the app does not yet know whether this
   browser holds a session. Deliberately plain: same column geometry and type scale as
   the sign-in screen so the swap to it is not a jump, no spinner (one same-origin GET
   does not need an animation to justify itself, and motion here would be decoration
   standing in for information), and no branding beyond the wordmark the sign-in screen
   already carries. Body text at the density floor — nothing is shrunk to fit. */
.em-resuming { min-height: 100%; display: flex; justify-content: center; padding: 12vh var(--em-space-4) var(--em-space-4); }
.em-resuming-col { width: min(56ch, 100%); display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-resuming-h { margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); color: var(--em-text-primary); }
.em-resuming-note { margin: 0; font-size: var(--em-fs-body); color: var(--em-text-secondary); line-height: var(--em-lh-body); max-width: 60ch; }

.em-login-panel {
  border: 1px solid var(--em-border-subtle);
  border-left: 3px solid var(--em-border-strong);
  border-radius: var(--em-radius-md);
  background: var(--em-surface-1);
  padding: var(--em-space-3);
  display: flex;
  flex-direction: column;
  gap: var(--em-space-2);
}
/* A JUDGED refusal, and a local validation refusal: the conflict tone, the same
   one every other contested/problem state in the product uses. */
.em-login-panel--problem { border-left-color: var(--em-tone-conflict); }
/* A SERVER THAT COULD NOT ANSWER. A DIFFERENT tone from a refused credential,
   because they are different facts and a person must not read an outage as a bad
   password.

   NEUTRAL, not amber, and the reason is semantic rather than aesthetic: an amber
   warning tone says "something you did needs attention", and there is nothing here
   for the person to correct — their credential was never read. `--em-tone-conflict`
   and `--em-tone-disputed` are also close enough in hue (#c05621 / #b7791f) that
   side by side they read as the same family, which would have made the reinforcement
   channel do no work at all. Colour is never the carrier here — the titles ("Sign in
   failed" vs "Sign in is unavailable") are — but reinforcement that reinforces
   nothing is decoration. */
.em-login-panel--degraded { border-left-color: var(--em-tone-neutral); }
/* A plain, expected notice (a clean sign-out): accent, not a warning tone. */
.em-login-panel--notice { border-left-color: var(--em-tone-accent); }

.em-login-panel-title { margin: 0; font-weight: var(--em-fw-medium); }
.em-login-panel-body  { margin: 0; }
.em-login-panel-note  {
  margin: 0;
  font-size: var(--em-fs-caption);
  color: var(--em-text-secondary);
  overflow-wrap: anywhere;
}
/* The focused outcome panel. It is focused programmatically after a refusal, so it
   MUST show a visible ring — an invisible focus move is worse than none, because
   the keyboard user's next Tab comes from a place they cannot see. */
.em-login-outcome:focus-visible,
.em-login-outcome:focus {
  outline: none;
  box-shadow: var(--em-focus-ring);
}

/* ── the four standing facts ────────────────────────────────────────────────
   A definition list, because that is what it is: a claim and its explanation.
   Placed after the form in document order (the form is the task) but present on
   arrival, never revealed on failure. */
.em-login-facts {
  border-top: 1px solid var(--em-border-subtle);
  padding-top: var(--em-space-4);
  display: flex;
  flex-direction: column;
  gap: var(--em-space-3);
}
.em-login-facts-h {
  margin: 0;
  font-size: var(--em-fs-body);
  font-weight: var(--em-fw-medium);
  color: var(--em-text-secondary);
}
.em-login-facts-list { margin: 0; display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-login-fact-t { margin: 0; font-weight: var(--em-fw-medium); }
/* `--em-text-secondary`, never `--em-text-muted`: this is information a person is
   expected to READ, and muted fails contrast in dark. Muted is for genuinely
   subordinate prose. */
.em-login-fact-d { margin: 0; color: var(--em-text-secondary); }
/* The provisioning command. Selectable, wrapping, monospace — it is meant to be
   copied into a terminal, so it must never be clipped at any zoom. */
.em-login-cmd {
  display: block;
  margin-top: var(--em-space-2);
  padding: var(--em-space-2);
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-sm);
  background: var(--em-surface-2);
  color: var(--em-text-primary);
  font-family: var(--em-font-mono);
  font-size: var(--em-fs-caption);
  overflow-wrap: anywhere;
  user-select: all;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE ACTIVATION SCREEN (ADR-0080 A-3) — `ui/activate.cljs`
   ═══════════════════════════════════════════════════════════════════════════
   It REUSES the sign-in screen's layout and panel classes on purpose. The two are
   peers, not a screen and a variant of it: a person who has read one has already
   learned how to read the other, and a second visual language for the second door
   would suggest a different kind of place. What is added here is only what
   activation genuinely has and sign-in does not:

     * an ALARM tone, for the one refusal that means somebody else may already
       hold this account;
     * a FIXABLE tone, for the refusals a person can correct in place without
       having spent anything;
     * the NEXT-ACTION line, at body weight, because four of the six refusals end
       at "ask the person who sent it" and that instruction is the only thing
       standing between the person and a dead end;
     * the code field, in monospace, so a mistyped character is visible.

   NO NEW VISUAL LANGUAGE. Every value is an existing `--em-*` token.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── the other door, on the sign-in screen ──────────────────────────────────
   A single line under the submit button, separated by a rule so it reads as a
   different route rather than a footnote to the form. It is the only thing on the
   sign-in screen that is not the form, its result, or a standing fact — it earns
   that by being what a person holding a code needs and cannot otherwise find. */
.em-login-alt {
  margin: 0;
  padding-top: var(--em-space-4);
  border-top: 1px solid var(--em-border-subtle);
}
.em-login-alt-link {
  color: var(--em-accent);
  font-weight: var(--em-fw-medium);
}
.em-login-alt-note { color: var(--em-text-secondary); }

/* ── the two tones activation adds ──────────────────────────────────────────
   THE ALARM. `auth.activation-used` is the only signal this product has that a
   conveyed code reached somebody else, so it gets weight the other refusals do
   not: a heavier rule and a faint conflict-tinted surface. Neither carries the
   meaning — the title ("Already used — and that may not have been you") and the
   emphasised action do, and the panel reads identically in monochrome and with the
   stylesheet off. The tint is what stops a person who has learned to skim
   refusals from skimming this one. */
.em-login-panel--alarm {
  border-left-width: 5px;
  border-left-color: var(--em-tone-conflict);
  background: color-mix(in srgb, var(--em-tone-conflict) 8%, var(--em-surface-1));
}
/* A THIRD, non-colour channel: the alarm's title is the only panel title in the
   product set in bold. Weight survives monochrome, a colour-blind reader and a
   printed page, which the tint and the rule do not. */
.em-login-panel--alarm .em-login-panel-title { font-weight: var(--em-fw-bold); }
/* FIXABLE — a short password, two passwords that differ, an empty field. The
   disputed/amber tone, and here it is the RIGHT one for exactly the reason the
   degraded panel above must not use it: amber says "something you did needs
   attention", and on these three there IS something for the person to correct,
   in place, having spent nothing. It must not look like the four refusals that
   are dead ends — a person who reads a fixable password as a spent code goes and
   asks for a replacement they do not need. */
.em-login-panel--fixable { border-left-color: var(--em-tone-disputed); }

/* THE NEXT ACTION. Body size, medium weight — not caption, because it is the
   sentence the person acts on, and the note beneath it is the one they may skip. */
.em-activate-action {
  margin: 0;
  font-weight: var(--em-fw-medium);
}

/* The focused outcome panel — its OWN selector, never `.em-login-outcome`, so the
   two screens' focus effects cannot reach into each other. Focused
   programmatically after a refusal, so it MUST show a visible ring: an invisible
   focus move is worse than none. */
.em-activate-outcome:focus-visible,
.em-activate-outcome:focus {
  outline: none;
  box-shadow: var(--em-focus-ring);
}

/* The code field. Monospace and wide-tracked, because the person is comparing 43
   characters against something in another window and `l`/`1`/`I` and `O`/`0` have
   to be distinguishable. Same reason as `.em-login-cmd`. */
#em-activate-token {
  font-family: var(--em-font-mono);
  letter-spacing: 0.02em;
}

/* ── sign out (context bar) ─────────────────────────────────────────────────
   A ghost button in the existing `.em-btn` family — quiet, because signing out is
   not the thing anyone came to do, but a real, keyboard-reachable, named control
   rather than an icon a person has to guess at. */
.em-signout { flex: 0 0 auto; white-space: nowrap; }

/* ═══════════════════════════════════════════════════════════════════════════
   ADD SOMEONE (ADR-0080 A-3) — the administration module's ONE write.

   NO NEW VISUAL LANGUAGE. Every value below is an existing `--em-*` token and
   every structural pattern is one this module already uses: `.em-admin-panel`
   for a statement, `.em-admin-state--problem`'s left rule for a problem,
   `.em-login-cmd`'s selectable monospace block for something meant to be copied
   out of the screen. Nothing here adds a gradient, a shadow, an animation or a
   colour that did not already exist, and nothing carries meaning by colour alone.
   ══════════════════════════════════════════════════════════════════════════ */

/* The one built act sits in the workspace head, beside the count it changes. */
.em-admin-toolbar { display: flex; gap: var(--em-space-2); margin-top: var(--em-space-2); }

/* A DECIDE panel. Marked by a left rule + its own heading words, exactly like
   `.em-admin-state--problem` — so it still reads in monochrome, at 200% zoom and
   with the stylesheet off. No tinted fill: the panel background stays a surface
   the `--em-tone-*` text colours were contrast-checked against. */
.em-admin-panel--decide { border-left: 3px solid var(--em-tone-rejected); }

/* ── the dialog body ──────────────────────────────────────────────────────── */
.em-admin-create {
  display: flex; flex-direction: column; gap: var(--em-space-4);
  padding: var(--em-space-4);
  /* The dialog is long by design — role choice, the derived grant, the count, the
     standing statements and the reason are all material. It scrolls; nothing is
     clipped and nothing is hidden to keep it short. */
  max-height: 72vh; overflow: auto;
}
/* NO SHRINKING. A scrolling flex COLUMN still applies `flex-shrink: 1` to its
   children, so when the content exceeds the max-height every child is squeezed — and
   a child with `overflow: hidden` (which `.em-admin-grantgroup` has) collapses to its
   borders rather than overflowing visibly. That silently erased the Decide grant
   group from the confirmation step: the panel above it and the count below it both
   rendered, and the five actions being granted were a 2px line between them. Caught
   on a 1440x900 screenshot; nothing in the DOM-free tests could have seen it. */
.em-admin-create > * { flex: 0 0 auto; }
.em-admin-create-fields { display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-admin-create-fields .em-form-field { margin: 0; }
.em-admin-create-fields .em-input { width: 100%; box-sizing: border-box; }

/* ── roles ────────────────────────────────────────────────────────────────── */
.em-admin-create-roles {
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-md);
  padding: var(--em-space-3); margin: 0;
  display: flex; flex-direction: column; gap: var(--em-space-2);
}
.em-admin-create-roles > legend {
  padding: 0 var(--em-space-1); font-weight: var(--em-fw-medium);
}
/* Two columns while there is room; one when there is not. Six roles at 1440 fit
   in two columns without any of them wrapping their summary to four lines. */
.em-admin-role {
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-md);
  padding: var(--em-space-2) var(--em-space-3);
  background: var(--em-surface-0);
}
/* Selection is carried by the REAL checkbox (native, announced) — this is
   reinforcement for a sighted scan, never the carrier. */
.em-admin-role-on { border-color: var(--em-accent); background: var(--em-accent-weak); }
.em-admin-role .em-form-check { align-items: baseline; }
.em-admin-role-name {
  display: inline-flex; align-items: baseline; gap: var(--em-space-2);
  font-weight: var(--em-fw-medium); flex-wrap: wrap;
}
.em-admin-role-why {
  margin: 2px 0 0 26px; font-size: var(--em-fs-caption);
  color: var(--em-text-secondary); line-height: var(--em-lh-body);
}
.em-admin-role-count { color: var(--em-text-muted); font-variant-numeric: tabular-nums; }
.em-admin-create-noroles { border-left: 3px solid var(--em-tone-disputed); padding-left: var(--em-space-2); }

/* ── the derived grant ────────────────────────────────────────────────────── */
.em-admin-create-grants { display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-admin-create-grants-head {
  display: flex; align-items: baseline; gap: var(--em-space-3); flex-wrap: wrap;
}
.em-admin-create-grants-head > .em-admin-state-note { flex: 1 1 24ch; }
/* The COMPACT class row still lists every action name — the toggle expands the
   server's consequence PROSE, never the facts. */
.em-admin-create-actions {
  margin: var(--em-space-2) 0 0; padding: 0 var(--em-space-3) var(--em-space-2);
  display: flex; flex-wrap: wrap; gap: var(--em-space-1) var(--em-space-2);
}
.em-admin-create-action { overflow-wrap: anywhere; }

/* ── the count that is the control ────────────────────────────────────────── */
/* Deliberately at body size and medium weight, not caption: this is the number an
   administrator must not be able to miss, and shrinking it to fit would be exactly
   the wrong economy. Tabular figures so 2 → 3 does not shift as it changes. */
.em-admin-create-count {
  margin: var(--em-space-2) 0 0;
  font-size: var(--em-fs-body); font-weight: var(--em-fw-medium);
  font-variant-numeric: tabular-nums;
}
.em-admin-create-count--quiet {
  font-weight: var(--em-fw-regular); color: var(--em-text-secondary);
}

.em-admin-create-reason-echo { white-space: pre-wrap; overflow-wrap: anywhere; }

/* ── footer ───────────────────────────────────────────────────────────────── */
.em-admin-create-foot {
  display: flex; gap: var(--em-space-2); flex-wrap: wrap;
  padding-top: var(--em-space-3); border-top: 1px solid var(--em-border-subtle);
}

/* ── the issued state — created, and NOT able to sign in ──────────────────── */
/* There is deliberately no success colour, no tick and no green rule anywhere in
   this block. The account exists and cannot sign in; a reassuring mark would be
   the most misleading thing this screen could render. */
.em-admin-create-issued-head { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-admin-create-issued-title {
  margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold);
}

.em-admin-activation { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-admin-activation-label {
  margin: var(--em-space-2) 0 0; font-size: var(--em-fs-caption);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--em-text-secondary);
}
/* Selectable, wrapping, monospace and at BODY size — it is meant to be read off the
   screen and typed into another one, so it must never be clipped and must never be
   caption-sized. Same idiom as `.em-login-cmd`, which exists for the same reason.
   There is deliberately no copy-to-clipboard control: writing a live credential into
   the system clipboard leaves it there for every other application on the machine,
   and this screen could then say nothing true about where it went. */
.em-admin-activation-code {
  display: block;
  padding: var(--em-space-3);
  border: 1px solid var(--em-border-strong);
  border-radius: var(--em-radius-sm);
  background: var(--em-surface-2);
  color: var(--em-text-primary);
  font-family: var(--em-font-mono);
  font-size: var(--em-fs-body);
  letter-spacing: 0.04em;
  overflow-wrap: anywhere;
  user-select: all;
}
/* The server's own sentence about the residual — that the administrator can see this
   code and could use it themselves. Given a rule of its own so it is not skimmed as
   another caption. */
.em-admin-activation-residual {
  margin: 0; padding-left: var(--em-space-3);
  border-left: 3px solid var(--em-tone-conflict);
  line-height: var(--em-lh-body); overflow-wrap: anywhere;
}

/* At 1440 the roles read best as two columns; below that, one. `@media` on width
   rather than a fixed grid, so 200% zoom collapses to one column instead of
   clipping. */
@media (min-width: 900px) {
  .em-admin-create-roles { display: grid; grid-template-columns: 1fr 1fr;
    /* START, not the implicit STRETCH. A grid row is as tall as its tallest cell, so
       one long card made its row-mate a mostly-empty box of the same height — which
       reads as missing content rather than as a short card. Each card is now its own
       height. Owner, 2026-08-31: "too much verbage here". */
    align-items: start; }
  .em-admin-create-roles > legend,
  .em-admin-create-roles > .em-admin-state-note,
  .em-admin-create-roles > .em-admin-actrole-shared,
  .em-admin-create-noroles { grid-column: 1 / -1; }
}

/* A refusal that applies to SEVERAL roles, stated once above them (see
   `shared-refusals`). Full width, because it is a fact about the act and not about
   any one card, and the cards it covers each keep their own badge. */
.em-admin-actrole-shared { margin-bottom: var(--em-space-1); }
.em-admin-actrole-shared .em-admin-refusal { margin: 0; }

/* Two SCHEMES in one status cell — the administrative status and, when it is
   exceptional, the sign-in state. They stack, each keeping its own badge and its own
   scheme text, so "Active (account status)" and "Cannot sign in yet (sign-in state)"
   read as two answers to two questions rather than one confused one. */
/* THE CELL STAYS A CELL. `display: flex` on a `<td>` takes it out of table layout:
   the browser generates an anonymous table cell around it, the td's background paints
   only its own content height, and the remainder of the row shows through — which on
   a SELECTED row (blue `> td` background) rendered as a white block under the badge,
   in that column, on every row. Reported by the owner 2026-08-31; same class as the
   stranded-overlay white rectangle, and the same lesson: a `<td>` may not be a flex
   container. `.em-tbl-cellflex` in the workbench tables is the established pattern. */
.em-admin-tbl-status-stack {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--em-space-1);
}
.em-admin-tbl-status .em-status-badge { flex: 0 0 auto; }

/* The step container takes focus when a step mounts, so Esc still reaches the dialog
   and the next Tab starts inside it. `:focus` as well as `:focus-visible`, because
   the focus move is PROGRAMMATIC and a focus move nobody can see is worse than none
   — the same reason the sign-in outcome panel carries both. */
.em-admin-create:focus,
.em-admin-create:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--em-accent); }

/* ══════════════════════════════════════════════════════════════════════════
   ADMIN — THE THREE ACCOUNT ACTS (ADR-0080 A-4)
   change roles · reset credential (re-issue OR reset) · suspend

   NO NEW VISUAL LANGUAGE. Every value is an existing `--em-*` token and every
   structural pattern is one this module already uses: `.em-admin-panel` for a
   statement, the `.em-admin-state--problem` left rule for a problem, the
   `.em-admin-create` dialog body for the steps (which is why the steps EMIT
   `.em-admin-create` rather than a parallel class). No gradient, no shadow, no
   animation, and nothing here carries meaning by colour alone — every state
   below is also carried by a word in the markup.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the acts region on the record ────────────────────────────────────────── */
/* PINNED to the bottom of the pane, and CAPPED with its own scroll.
   The acts are why the record was opened, so they get a fixed home rather than a
   position determined by the length of the reference list above them. Capped because
   this region is NOT always short — a self-administered account carries four refusal
   blocks with explanations, and pinning that whole thing would leave no room for the
   record. Beyond the cap it scrolls itself.
   The owner asked for a draggable splitter; a fixed home makes the drag unnecessary,
   and a splitter would have been a control compensating for a layout, with a stored
   preference and a keyboard-resize obligation to match. */
.em-admin-acts {
  flex: 0 0 auto; max-height: 40%; overflow-y: auto;
  margin: 0; padding: var(--em-space-3);
  border-top: 1px solid var(--em-border-subtle);
  background: var(--em-surface-1);
  display: flex; flex-direction: column; gap: var(--em-space-2);
}
.em-admin-acts-lead { margin: 0; font-size: var(--em-fs-caption); max-width: 92ch;
  line-height: var(--em-lh-body); }
.em-admin-acts-row { display: flex; flex-wrap: wrap; gap: var(--em-space-2);
  align-items: flex-start; }
/* A refused control brings its reason with it, so it takes a full row rather than
   sitting shoulder-to-shoulder with a live one and wrapping its prose to 8ch. */
.em-admin-acts-row > .em-admin-writeact { flex: 1 1 100%; }
.em-admin-acts-self { margin: 0; }

/* ── a refusal ────────────────────────────────────────────────────────────── */
/* Plain English first, the rule id as a reference. The `✕ Refused` token is a WORD
   with a glyph — the left rule and the muted prose are reinforcement, never the
   signal, so this still reads in monochrome and with the stylesheet off. */
.em-admin-refusal {
  flex: 1 1 24ch; min-width: 0;
  border-left: 3px solid var(--em-tone-conflict);
  padding-left: var(--em-space-3);
  display: flex; flex-direction: column; gap: 2px;
}
.em-admin-refusal-head { font-weight: var(--em-fw-medium); }
.em-admin-refusal-why { margin: 0; font-size: var(--em-fs-caption);
  line-height: var(--em-lh-body); max-width: 92ch; color: var(--em-text-secondary); }
.em-admin-refusal-rule { margin: 0; font-size: var(--em-fs-caption);
  color: var(--em-text-muted); }

/* ── the role editor ──────────────────────────────────────────────────────── */
.em-admin-actrole {
  border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm);
  padding: var(--em-space-2) var(--em-space-3);
  display: flex; flex-direction: column; gap: var(--em-space-1);
}
.em-admin-actrole-on { border-color: var(--em-border-strong); }
/* CHANGED-BUT-NOT-SAVED. The rule is reinforcement; `.em-admin-actrole-pending`
   below is the carrier, and it is a sentence. */
.em-admin-actrole-changed { border-left: 3px solid var(--em-tone-rejected); }
/* A REFUSED ROLE ROW. Opacity alone was the only signal; the DASHED rule is the
   non-colour, non-opacity channel that survives monochrome, and it matches the inert
   button treatment so "present but not actionable" looks the same everywhere. The row
   keeps its tab stop and its refusal text — this is appearance, never enforcement. */
.em-admin-actrole-refused {
  opacity: 0.85;
  border-style: dashed;
  border-color: var(--em-border-strong);
}
.em-admin-actrole-refused .em-checkbox { cursor: default; }
.em-admin-actrole-why { display: flex; flex-direction: column; gap: var(--em-space-1);
  padding-left: calc(var(--em-space-4) + 2px); }
.em-admin-actrole-pending { margin: 0; font-size: var(--em-fs-caption);
  font-weight: var(--em-fw-medium); line-height: var(--em-lh-body); }

/* ── the dialog's own pieces ──────────────────────────────────────────────── */
.em-admin-act-points { margin: 0; padding-left: var(--em-space-4);
  display: flex; flex-direction: column; gap: var(--em-space-1);
  line-height: var(--em-lh-body); max-width: 92ch; }
.em-admin-act-count { margin: 0; font-variant-numeric: tabular-nums;
  line-height: var(--em-lh-body); }
/* The lede is the FIRST thing read on a credential act and it is what says which of
   the two acts this is. It is a token plus a sentence — never a colour. */
.em-admin-act-lede { display: flex; flex-direction: column; gap: var(--em-space-1); }
.em-admin-act-lede-text { font-weight: var(--em-fw-medium); }
.em-admin-act-evidence { display: flex; flex-direction: column; gap: var(--em-space-1);
  border-left: 3px solid var(--em-border-subtle); padding-left: var(--em-space-3); }
.em-admin-act-pending-lead { margin: 0; line-height: var(--em-lh-body); max-width: 92ch; }

/* ── A-4 reconciliation: reinstate, the four-eyes counts, the inbox ────────── */
/* Same tokens, same structural patterns. Nothing below carries meaning by colour:
   every state has a word in the markup beside it. */

/* The roles as they NOW are, on a version conflict — the fact the operator needs
   most, so it reads as body copy rather than as a footnote. */
.em-admin-roles-now { font-weight: var(--em-fw-medium); }

/* The reset trace on the record. A left rule + the token's own words; it is the
   thing that stops a credential revocation being secret, so it is not a footnote. */
.em-admin-reset-trace {
  display: flex; flex-direction: column; gap: var(--em-space-1);
  border-left: 3px solid var(--em-tone-conflict);
  padding-left: var(--em-space-3); margin-top: var(--em-space-2);
}

/* The awaiting-confirmation table. Add and remove are two ROWS with two verbs, not
   one list of tags distinguished by colour. */
.em-admin-req-dir { display: flex; align-items: baseline; gap: var(--em-space-2);
  flex-wrap: wrap; }
.em-admin-req-verb { font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-admin-req-tbl td { vertical-align: top; }
.em-admin-req-tbl .em-admin-writeact { flex-direction: column; align-items: flex-start;
  gap: var(--em-space-1); }

/* Repeated observations of ONE declared-type claim — grouped, never dropped: the count
   is stated on the claim and each observation stays individually openable in here. */
.em-type-claim-obs { margin-top: var(--em-space-1); font-size: var(--em-fs-caption); }
.em-type-claim-obs > summary { cursor: pointer; color: var(--em-text-secondary); }
.em-type-claim-obs ul { list-style: none; margin: var(--em-space-1) 0 0; padding: 0 0 0 var(--em-space-3); }
.em-type-claim-obs li { color: var(--em-text-muted); font-variant-numeric: tabular-nums; }

/* ── WHAT THIS DEPLOYMENT CAN SCAN (Part A item 3) ─────────────────────────────
   ABSENCE IS PRIMARY CONTENT. There is deliberately NO rule here that dims, shrinks,
   desaturates or reorders an unscannable kind: it reuses `.em-card` untouched, at the
   same size and weight as a scannable one. Someone arrives asking "can it discover
   OpenText?" and for them that card IS the answer — a design that makes the reachable
   things beautiful and the unreachable things a footnote answers the easy question well
   and the hard one badly. Dimming would also borrow a meaning this product already uses
   for something else (a CONTROL you may not use), and an unscannable kind is not a
   disabled control; it is a true statement of exactly the same standing.
   For the same reason there is no tone split between the two access paths: `read live`
   and `you supply an export` are two real ways to reach a real thing, and colouring one
   of them would rank them. The WORDS carry both distinctions. */
.em-scat { padding: var(--em-space-5); }
.em-scat-lead { margin: 0; line-height: 1.45; max-width: 78ch; }
.em-scat-list { grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); }
.em-scat-card { gap: var(--em-space-3); }
.em-scat-card .em-card-head { align-items: baseline; }

/* the wholly-empty deployment: WHY it can scan nothing. Stated here AND on every row —
   the two answer different questions ("why nothing at all?" vs "can it scan X?"), and a
   visitor who came for one kind must find the answer at that kind's row. */
.em-scat-empty {
  margin: 0; padding: var(--em-space-3);
  border: 1px solid var(--em-border-strong); border-radius: var(--em-radius-sm);
  background: var(--em-surface-1); line-height: 1.45;
}

/* one way a kind can be reached. Siblings are ALTERNATIVES (z/OS will be reachable both
   live and by a supplied export), so each is self-describing and none is "the" one. */
.em-scat-reaches { display: flex; flex-direction: column; gap: var(--em-space-4); }
.em-scat-reach { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-scat-reach + .em-scat-reach {
  border-top: 1px solid var(--em-border-subtle); padding-top: var(--em-space-4);
}
.em-scat-reach-head { display: flex; align-items: baseline; }
.em-scat-access { font-weight: var(--em-fw-bold); font-size: var(--em-fs-strong); }
.em-scat-summary { margin: 0; line-height: 1.45; color: var(--em-text-secondary); }

/* WHAT YOU WILL LEARN — the half that decides whether a scan is worth running, so it is
   a titled, visible list at body size. Never a chip row (which truncates the sentence)
   and never behind a disclosure. */
.em-scat-learn-h {
  margin: var(--em-space-1) 0 0; font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--em-text-muted);
}
.em-scat-learn {
  margin: 0; padding-left: var(--em-space-4); list-style: disc;
  display: flex; flex-direction: column; gap: var(--em-space-1); line-height: 1.45;
}
.em-scat-by { margin: 0; font-size: var(--em-fs-caption); }

/* the absence answer, in the SAME slot the reaches occupy and at the SAME size */
.em-scat-absence { margin: 0; line-height: 1.45; max-width: 68ch; }

.em-scat-error { display: flex; flex-direction: column; gap: var(--em-space-2);
  align-items: flex-start; }

/* Credential administration uses the Admin form and workbench control tokens. */
.em-vault .em-tables-head { justify-content: space-between; gap: var(--em-space-4); flex-wrap: wrap; }
.em-vault-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--em-space-2); }
.em-vault-form {
  display: flex; flex-direction: column; gap: var(--em-space-4);
  max-width: 780px; margin: var(--em-space-3) 0;
  padding: var(--em-space-4); border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-md); background: var(--em-surface-0);
}
.em-vault-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--em-space-3) var(--em-space-4); }
.em-vault-field { min-width: 0; font-weight: var(--em-fw-medium); }
.em-vault-field .em-input, .em-vault-field .em-select { width: 100%; box-sizing: border-box; font-weight: var(--em-fw-regular); }
.em-vault-hint, .em-vault-context { margin: 0; color: var(--em-text-secondary); font-size: var(--em-fs-caption); line-height: var(--em-lh-body); max-width: 72ch; }
.em-vault-context { grid-column: 1 / -1; }
.em-vault-capabilities { grid-column: 1 / -1; }
.em-vault-check { display: flex; align-items: center; gap: var(--em-space-2); padding: var(--em-space-1) 0; }
.em-vault-check input { accent-color: var(--em-accent); width: 16px; height: 16px; margin: 0; flex-shrink: 0; }
.em-vault-material { display: flex; flex-direction: column; gap: var(--em-space-2); }
.em-vault-material > .em-vault-field { max-width: calc(50% - var(--em-space-2)); }
.em-vault-form > .em-vault-actions { padding-top: var(--em-space-3); border-top: 1px solid var(--em-border-subtle); }
.em-vault-empty { padding: var(--em-space-5) var(--em-space-3); }
.em-vault-table { margin-bottom: var(--em-space-4); }
.em-vault-detail {
  display: flex; flex-direction: column; gap: var(--em-space-3);
  max-width: 820px; margin: 0 0 var(--em-space-5);
}
.em-vault-detail-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: var(--em-space-3); padding-top: var(--em-space-1);
}
.em-vault-facts {
  display: grid; grid-template-columns: minmax(150px, 1fr) 2fr;
  gap: var(--em-space-2) var(--em-space-4); margin: 0;
}
.em-vault-facts dt { color: var(--em-text-secondary); }
.em-vault-facts dd { margin: 0; overflow-wrap: anywhere; }
.em-vault-capability-summary {
  margin-top: var(--em-space-3); padding-top: var(--em-space-3);
  border-top: 1px solid var(--em-border-subtle);
}
.em-vault-capability-list {
  margin: var(--em-space-2) 0 0; padding-left: var(--em-space-4);
  display: flex; flex-direction: column; gap: var(--em-space-1);
}
.em-vault-acts {
  display: flex; flex-direction: column; gap: var(--em-space-2);
  padding: 0 var(--em-space-1);
}
.em-vault-acts > .em-admin-panel-title { margin: 0; }
@media (max-width: 760px) {
  .em-vault-grid { grid-template-columns: minmax(0, 1fr); }
  .em-vault-material > .em-vault-field { max-width: none; }
  .em-vault-form { padding: var(--em-space-3); }
  .em-vault-detail-head { flex-direction: column; }
  .em-vault-facts { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .em-vault-facts dd + dt { margin-top: var(--em-space-2); }
}

.em-vault-modal > .em-vault-form {
  max-width: none; margin: 0; border: 0; border-radius: 0;
  padding: var(--em-space-4); max-height: 75vh; overflow-y: auto;
}
.em-vault-modal > .em-vault-form > * { flex-shrink: 0; }

/* Frontier: one primary task, with details only when selected. */
.em-frontier-task-body { display:flex; flex-direction:column; flex:1; min-height:0; min-width:0; width:100%; background:var(--em-surface-0); }
.em-frontier-toolbar { display:flex; align-items:center; justify-content:space-between; gap:24px; padding:12px 24px; border-bottom:1px solid var(--em-border-subtle); flex-wrap:wrap; }
.em-frontier-tabs { display:flex; gap:8px; align-items:center; flex-wrap:wrap; min-width:0; }
.em-frontier-tabs .em-frontier-nav { padding:9px 14px; border-radius:6px; width:auto; white-space:nowrap; }
.em-frontier-task-content { flex:1; min-height:0; min-width:0; display:flex; overflow:auto; }
.em-frontier-primary { flex:1; min-width:0; min-height:0; overflow:auto; }
.em-frontier-context { flex:0 0 340px; min-width:0; overflow:auto; border-left:1px solid var(--em-border-subtle); background:var(--em-surface-1); }
.em-frontier-close-detail { margin:16px 20px 0; }
.em-frontier-setup { max-width:880px; margin:0 auto; padding:24px; }
.em-frontier-setup > * { height:auto; overflow:visible; }
.em-frontier-draft-banner { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:10px 24px; background:var(--em-accent-weak); border-bottom:1px solid var(--em-border-subtle); }
.em-frontier-draft-actions { display:flex; gap:8px; }
.em-frontier-task-body details > summary { cursor:pointer; }
.em-frontier-task-body p { max-width:80ch; }
.em-frontier-task-body .em-frontier-catalogue { height:auto; min-height:100%; overflow:visible; }
@media (max-width:800px) {
  .em-frontier-toolbar { padding:12px 16px; gap:12px; }
  .em-frontier-task-content.em-has-detail { flex-direction:column; }
  .em-frontier-context { flex:none; overflow:visible; border-left:0; border-top:1px solid var(--em-border-subtle); }
  .em-has-detail .em-frontier-primary { flex:none; overflow:visible; }
  .em-frontier-draft-banner { flex-wrap:wrap; padding:10px 16px; }
  .em-frontier-setup { padding:16px; }
}
.em-frontier-setup .em-insp-body { flex:none; overflow:visible; }
.em-frontier-setup .em-cred-stepper { padding:12px 0; gap:10px; }
.em-frontier-setup .em-cred-step { white-space:nowrap; }
.em-frontier-task-body .em-ds-map-interactive { flex:1; min-height:0; display:flex; flex-direction:column; }
.em-frontier-task-body .em-ds-map-controls { display:flex; align-items:center; gap:8px; padding:8px 12px; flex-wrap:wrap; }
.em-frontier-task-body .em-ds-mapstage { display:flex; flex-direction:column; min-height:420px; }
.em-frontier-task-body .em-ds-map-canvas { flex:1; min-height:320px; }
.em-frontier-task-body .em-ds-map-limit { padding:10px 16px; color:var(--em-text-secondary); border-top:1px solid var(--em-border-subtle); font-size:var(--em-fs-caption); }
.em-frontier-task-body .em-ds-evidence-details summary:focus-visible,
.em-frontier-task-body .em-ds-dsrc summary:focus-visible { outline:2px solid var(--em-accent); outline-offset:2px; }
.em-frontier-context .em-ds-covrow { grid-template-columns:1fr 1fr; }
.em-frontier-task-body .em-ds-question-row { display:block; width:100%; text-align:left; font:inherit; color:var(--em-text-primary); background:var(--em-surface-0); border:1px solid var(--em-border-subtle); border-radius:6px; padding:14px 16px; cursor:pointer; }
.em-frontier-task-body .em-ds-question-row:hover { background:var(--em-surface-1); border-color:var(--em-border-strong); }
.em-frontier-task-body .em-ds-question-row[aria-pressed="true"] { background:var(--em-accent-weak); border-color:var(--em-accent); }
.em-frontier-task-body .em-ds-question-row:focus-visible { outline:2px solid var(--em-accent); outline-offset:2px; }
.em-frontier-task-body .em-ds-question-action { display:block; color:var(--em-accent-strong); margin-top:6px; font-size:var(--em-fs-caption); }
.em-configuring-discovery .em-frontier-cat-grid { display:block; }
.em-configuring-discovery .em-frontier-cat-primary { display:none; }
.em-configuring-discovery .em-tgt-new-field > .em-visually-hidden { position:static; width:auto; height:auto; clip:auto; clip-path:none; overflow:visible; white-space:normal; display:block; margin-bottom:4px; }
.em-configuring-discovery .em-tgt-new-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:12px 16px; }
.em-configuring-discovery .em-tgt-new-field { display:flex; flex-direction:column; align-items:stretch; min-width:0; }
.em-configuring-discovery .em-tgt-new-field:nth-child(3) { grid-column:1 / -1; }
.em-configuring-discovery .em-tgt-new-field input,
.em-configuring-discovery .em-tgt-new-field select { width:100%; max-width:none; }

/* One programme selector, shared by every experience in the main bar. */
.em-context-bar { height:auto; min-height:var(--em-context-bar-h); flex-wrap:wrap !important; row-gap:8px !important; padding-top:6px !important; padding-bottom:6px !important; }
.em-context-bar > .em-scope-picker { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:0; padding:0; border:0; max-width:300px; }
.em-context-bar > .em-scope-picker .em-scope-picker-l { margin:0; white-space:nowrap; }
.em-context-bar > .em-scope-picker .em-scope-picker-s { width:auto; max-width:180px; }
.em-context-bar > .em-scope-picker .em-scope-picker-n { display:none; }
.em-context-bar > .em-scope-picker .em-scope-picker-n[role="status"] { display:block; flex-basis:100%; }

/* Programme management uses the shared scope bar and modal. */
.em-programmes-content { display: grid; gap: var(--em-space-4); min-height: 0; }
.em-programmes-content > .em-admin-state { margin: 0; }
.em-programmes-content p { margin: 0; max-width: 78ch; color: var(--em-text-secondary); }
.em-programmes-content h3 { margin: 0; font-size: var(--em-fs-strong); font-weight: var(--em-fw-bold); }
.em-programmes-list { list-style: none; padding: 0; margin: 0; max-height: 16rem; overflow-y: auto; }
.em-programmes-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--em-space-3); padding: var(--em-space-3) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-programmes-row:last-child { border-bottom: 0; }
.em-programmes-row > .em-btn { flex-shrink: 0; }
.em-programmes-row > div { min-width: 0; overflow-wrap: anywhere; }
.em-programmes-selected { color: var(--em-text-secondary); font-size: var(--em-fs-caption); font-weight: var(--em-fw-medium); }
.em-programme-id { display: block; margin-top: 0.25rem; color: var(--em-text-muted); overflow-wrap: anywhere; }
.em-programmes-create { display: grid; gap: var(--em-space-3); }
.em-programmes-create label { font-weight: var(--em-fw-medium); }
.em-programmes-create input { width: 100%; max-width: 36rem; box-sizing: border-box; }
.em-programmes-create > button { justify-self: start; }

.em-admin-programmes .em-programmes-content { padding: var(--em-space-3) 0; }
.em-programmes-modal .em-programmes-content { padding: var(--em-space-4); }

/* Selected-claim review: preserve the bounded graph while giving the task space. */
.em-review-layout { display: flex; flex-direction: column; flex: 1 1 0; min-width: 0; min-height: 0; }
.em-review-context-toggle { display: flex; justify-content: flex-end; padding: var(--em-space-2) var(--em-space-3); border-bottom: 1px solid var(--em-border-subtle); }
.em-review-focused > .em-split-p1 { display: none; }
.em-review-focused > .em-split-p2 { flex: 1 1 0; }
.em-review-layout-active .em-insp-tabpanel { display: block; overflow: auto; }
.em-review-layout-active .em-insp-body { overflow: visible; flex: none; }
.em-review-layout-active .em-insp-decision { position: static; flex: none; margin-top: var(--em-space-3); }
.em-review-layout-active .em-insp-header { padding-bottom: var(--em-space-2); }
.em-review-layout-active .em-insp-section { margin-bottom: var(--em-space-3); }
.em-review-layout-active .em-insp-learn { margin-top: var(--em-space-2); }
.em-review-next-step { margin: 0 0 var(--em-space-2); }
.em-review-other-actions > summary { margin-bottom: var(--em-space-2); }
.em-review-action-groups .em-action-list { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.em-review-layout-active .em-insp-task-claim { max-width: 80ch; }

.em-rq-source { display: grid; gap: 2px; font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-rq-selected-detail { margin: var(--em-space-2); }
.em-evidence-preview { margin-block: var(--em-space-3); }
.em-evidence-preview-text { max-height: 24rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; padding: var(--em-space-3); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); background: var(--em-surface-0); color: var(--em-text-primary); }

.em-evidence-preview button + button { margin-left: var(--em-space-2); }

/* Reviewed programme scope: existing form, border and typography tokens. */
.em-programme-scope { padding: var(--em-space-5); width: 100%; min-width: 0; box-sizing: border-box; }
.em-scope-header { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--em-space-3); padding-bottom: var(--em-space-4); margin-bottom: var(--em-space-4); border-bottom: 1px solid var(--em-border-subtle); }
.em-scope-header h2 { margin: 0 0 var(--em-space-2); }
.em-scope-header p { margin: 0; }
.em-scope-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--em-space-2); margin: var(--em-space-2) 0; }
.em-scope-form, .em-scope-task { min-width: 0; padding: var(--em-space-4); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); background: var(--em-surface-0); }
.em-scope-form h3, .em-scope-task h3 { margin-top: 0; }
.em-scope-form fieldset { border: 0; padding: 0; margin: var(--em-space-3) 0; min-width: 0; display: grid; gap: var(--em-space-3); }
.em-scope-form .em-scope-context { margin: 0; font-weight: var(--em-fw-medium); }
.em-scope-checkbox { display: flex; align-items: center; gap: var(--em-space-2); }
.em-scope-card { border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); padding: var(--em-space-3); margin: var(--em-space-2) 0; overflow-wrap: anywhere; }
.em-scope-card h4 { margin: 0; }
.em-scope-card p { margin: var(--em-space-2) 0; }
.em-scope-card details { margin-top: var(--em-space-2); }
.em-scope-group { padding: var(--em-space-3) 0; border-top: 1px solid var(--em-border-subtle); }
.em-scope-group h3 { margin: 0 0 var(--em-space-2); }
.em-scope-inspector { margin-top: var(--em-space-3); }
.em-scope-form .em-form-field { font-weight: var(--em-fw-medium); }
.em-scope-form .em-form-field textarea { font-weight: var(--em-fw-regular); }

/* Frontier workspaces use the available width; only explanatory prose is bounded. */
.em-runs-view { min-width: 0; overflow-wrap: anywhere; }
.em-run-detail-titlebar > .em-runs-refresh { margin-left: auto; }
.em-run-results { padding: var(--em-space-4); background: var(--em-surface-1); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); }
.em-run-next-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: var(--em-space-3); }
.em-run-next-actions > div { display: flex; flex-direction: column; align-items: flex-start; gap: var(--em-space-3); padding: var(--em-space-4); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); min-width: 0; }
.em-run-next-actions .em-run-followon-open { width: 100%; min-width: 0; }
.em-run-detail .em-technical-details { border-top: 1px solid var(--em-border-subtle); padding-top: var(--em-space-3); }
.em-run-detail p, .em-run-next-actions span.em-muted { max-width: 75ch; }
.em-programme-scope p { max-width: 75ch; }
.em-programme-scope .em-form-field,
.em-scope-form fieldset { width: 100%; max-width: none; min-width: 0; box-sizing: border-box; }
.em-programme-scope .em-input { box-sizing: border-box; }
.em-scope-task form { display: grid; gap: var(--em-space-3); }
.em-scope-form > .em-scope-actions { padding-top: var(--em-space-3); border-top: 1px solid var(--em-border-subtle); }
.em-scope-form > fieldset > details { padding: var(--em-space-3); background: var(--em-surface-1); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); }
.em-scope-form summary { cursor: pointer; font-weight: var(--em-fw-medium); }
.em-scope-form details .em-form-field { margin-top: var(--em-space-3); }
.em-programme-scope .em-scope-group { margin-bottom: var(--em-space-3); padding: var(--em-space-4); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); background: var(--em-surface-0); }
@media (max-width: 600px) {
  .em-programme-scope { padding: var(--em-space-3); }
  .em-scope-form, .em-scope-task, .em-programme-scope .em-scope-group { padding: var(--em-space-3); }
}
.em-scope-card h4 { display: flex; flex-wrap: wrap; align-items: center; gap: var(--em-space-2); }
@media (max-width: 600px) {
  .em-run-detail-title { flex-basis: 100%; order: 1; }
  .em-run-detail-titlebar > .em-run-id { flex-basis: 100%; order: 2; }
  .em-run-card-meta { grid-template-columns: 1fr; }
}

@media (min-width: 800px) {
  .em-programme-scope article.em-scope-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--em-space-4); align-items: start; }
  .em-programme-scope article.em-scope-card > :not(.em-scope-actions) { grid-column: 1; }
  .em-programme-scope article.em-scope-card > .em-scope-actions { grid-column: 2; grid-row: 1 / span 4; margin: 0; max-width: 320px; justify-content: flex-end; }
  .em-programme-scope article.em-scope-card > details { grid-column: 1 / -1; }
}

.em-scope-group:not(:has(.em-scope-card)) { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--em-space-2) var(--em-space-4); }
.em-scope-group:not(:has(.em-scope-card)) > h3, .em-scope-group:not(:has(.em-scope-card)) > p { margin: 0; }

.em-legend-row { display: flex; flex-direction: column; align-items: flex-start; gap: var(--em-space-2); margin-bottom: var(--em-space-3); }
.em-legend-row > .em-legend-label { font-weight: var(--em-fw-medium); }
.em-legend-key { background: transparent; border: 0; padding: 0; }
.em-legend-swatch--node { width: 18px; height: 18px; flex: none; }
.em-legend-swatch--edge { width: 28px; flex: none; }

/* Scan setup stays bounded above its originating workspace. */
.em-scan-setup-modal.em-modal-panel { width: min(960px, calc(100vw - 32px)); max-width: none; max-height: calc(100dvh - 48px); display: flex; flex-direction: column; overflow: hidden; padding-bottom: 0; }
.em-scan-setup-modal > .em-modal-head { flex: none; }
.em-scan-setup-target { margin: var(--em-space-3) var(--em-space-5); overflow-wrap: anywhere; color: var(--em-text-secondary); }
.em-scan-setup-modal .em-frontier-setup { width: 100%; min-height: 0; max-width: none; padding: 0 var(--em-space-5); overflow: auto; flex: 1 1 auto; }
.em-scan-setup-modal .em-insp-titlebar { display: none; }
.em-scan-setup-modal .em-insp-body { padding: var(--em-space-3) 0; }
.em-scan-setup-modal .em-cred-stepper { position: sticky; top: 0; z-index: 2; background: var(--em-surface-0); flex-wrap: wrap; }
.em-scan-setup-modal .em-cred-nocred-actions,
.em-scan-setup-modal .em-preflight-actions,
.em-scan-setup-modal .em-confirm-actions,
.em-scan-setup-modal .em-scope-actions { position: sticky; bottom: 0; background: var(--em-surface-0); padding: var(--em-space-3) 0; border-top: 1px solid var(--em-border-subtle); z-index: 1; }
.em-scan-setup-footer { flex: none; display: flex; justify-content: flex-end; padding: var(--em-space-3) var(--em-space-5); border-top: 1px solid var(--em-border-subtle); }

/* Sessions and audit filters use the shared form controls and pane spacing. */
.em-admin-activity-filters { display: flex; flex-direction: row; flex-wrap: wrap; align-items: end; gap: var(--em-space-3); }
.em-admin-activity-filters > .em-admin-state-title { flex-basis: 100%; }
.em-admin-activity-filters > .em-form-field { flex: 1 1 14rem; min-width: 12rem; }
.em-admin-activity-filters .em-input { width: 100%; box-sizing: border-box; }

/* Session administration: identify people first; protocol IDs stay in details. */
.em-sessions-table { table-layout: fixed; width: 100%; min-width: 1100px; }
.em-sessions-table th:nth-child(1) { width: 26%; }
.em-sessions-table th:nth-child(2), .em-sessions-table th:nth-child(3) { width: 17%; }
.em-sessions-table th:nth-child(4) { width: 24%; }
.em-sessions-table th:nth-child(5) { width: 16%; }
.em-sessions-table td { vertical-align: top; padding-top: var(--em-space-3); padding-bottom: var(--em-space-3); }
.em-session-person, .em-session-login { display: block; overflow-wrap: anywhere; }
.em-session-login, .em-session-clock { color: var(--em-text-secondary); }
.em-session-current-label { display: inline-block; margin-top: var(--em-space-2); padding: 2px 6px; color: var(--em-accent-strong); background: var(--em-accent-weak); border-radius: var(--em-radius-sm); font-size: var(--em-fs-caption); }
.em-session-current td:first-child { border-left: 3px solid var(--em-accent); }
.em-session-time { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 6px; row-gap: 2px; font-variant-numeric: tabular-nums; }
.em-session-relative { flex-basis: 100%; font-weight: var(--em-fw-medium); }
.em-session-time > span { white-space: nowrap; }
.em-session-limits { display: grid; grid-template-columns: max-content 1fr; align-items: baseline; gap: 6px 12px; margin: 0; }
.em-session-limits dt { font-size: var(--em-fs-caption); color: var(--em-text-muted); }
.em-session-limits dd { margin: 0; }
.em-session-details { margin-top: var(--em-space-2); font-size: var(--em-fs-caption); }
.em-session-details summary, .em-session-help summary { cursor: pointer; color: var(--em-text-secondary); }
.em-session-details dl { margin: var(--em-space-2) 0; }
.em-session-details dd { margin: 2px 0 var(--em-space-2); }
.em-session-details code { display: block; overflow-wrap: anywhere; user-select: all; }
.em-session-confirm-id { overflow-wrap: anywhere; }
.em-admin-pagination { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--em-space-3); padding: var(--em-space-3) 0; color: var(--em-text-secondary); }
.em-admin-pagination p { margin: 0; }
.em-admin-pagination nav { display: flex; gap: var(--em-space-2); }
.em-session-help { margin: var(--em-space-2) 0; max-width: 75ch; font-size: var(--em-fs-caption); }
.em-session-help p { line-height: var(--em-lh-body); }
@media (max-width: 1100px) {
  .em-sessions-table { table-layout: auto; }
  .em-sessions-table th { width: auto !important; }
  .em-sessions-table .em-btn { white-space: nowrap; }
}

.em-admin-activity-filters.em-session-search { border: 0; background: transparent; padding: 0; margin: var(--em-space-3) 0 var(--em-space-4); }
.em-session-search > .em-form-field { flex: 0 1 28rem; }
.em-session-details summary:focus-visible, .em-session-help summary:focus-visible { outline: 2px solid var(--em-accent); outline-offset: 3px; }

/* Audit prioritises the event; exact diagnostic identifiers stay in details. */
.em-admin-activity-filters.em-audit-filters { border: 0; background: transparent; padding: 0; margin: var(--em-space-3) 0 var(--em-space-4); }
.em-audit-filter-help { flex-basis: 100%; margin: 0; color: var(--em-text-secondary); font-size: var(--em-fs-caption); }
.em-audit-filters > .em-form-field { flex: 1 1 11rem; min-width: 9rem; }
.em-audit-table { width: 100%; table-layout: fixed; }
.em-audit-table th:nth-child(1) { width: 20%; }
.em-audit-table th:nth-child(2) { width: 43%; }
.em-audit-table th:nth-child(3) { width: 23%; }
.em-audit-table th:nth-child(4) { width: 14%; }
.em-audit-table caption { text-align: left; padding: var(--em-space-2); color: var(--em-text-secondary); font-size: var(--em-fs-caption); }
.em-audit-table td { padding: var(--em-space-3) var(--em-space-2); vertical-align: top; overflow-wrap: anywhere; }
.em-audit-event, .em-audit-actor { display: block; font-weight: var(--em-fw-medium); }
.em-audit-actor-id { display: block; margin-top: var(--em-space-1); font-size: var(--em-fs-caption); color: var(--em-text-secondary); }
.em-audit-outcome { display: inline-block; padding: 2px 7px; border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); font-size: var(--em-fs-caption); }
.em-audit-outcome-ok { color: var(--em-text-primary); }
.em-audit-outcome-refused { color: var(--em-text-primary); }
.em-audit-details { margin-top: var(--em-space-2); font-size: var(--em-fs-caption); }
.em-audit-details summary { cursor: pointer; color: var(--em-text-secondary); }
.em-audit-details summary:focus-visible { outline: 2px solid var(--em-accent); outline-offset: 3px; }
.em-audit-details dl { margin: var(--em-space-3) 0 0; padding-left: var(--em-space-3); border-left: 2px solid var(--em-border-subtle); }
.em-audit-details dt { color: var(--em-text-secondary); }
.em-audit-details dd { margin: 2px 0 var(--em-space-2); }
.em-audit-details code { display: block; overflow-wrap: anywhere; user-select: all; }
@media (max-width: 700px) {
  .em-audit-table { min-width: 36rem; }
}

.em-audit-actor-login { display: block; margin-top: var(--em-space-1); color: var(--em-text-secondary); font-size: var(--em-fs-caption); overflow-wrap: anywhere; }

/* Connector setup uses the Admin pane's spacing and keeps requirements readable. */
.em-connector-actions { display: flex; flex-wrap: wrap; gap: var(--em-space-2); }
.em-connector-version > h3, .em-connector-version > p { margin: 0; }
.em-connector-version > p { max-width: 78ch; }
.em-connector-version > dl { display: grid; grid-template-columns: minmax(10rem, 1fr) 3fr; gap: var(--em-space-3); margin: var(--em-space-2) 0; }
.em-connector-version dt { color: var(--em-text-secondary); }
.em-connector-version dd { margin: 0; overflow-wrap: anywhere; }
.em-connector-version ul { margin: 0; padding-left: 1.2em; }
@media (max-width: 700px) {
  .em-connector-version > dl { grid-template-columns: 1fr; gap: var(--em-space-2); }
}

/* Source readiness keeps untested access distinct from configuration checks. */
.em-source-readiness-modal > .em-modal-head { flex: none; }
.em-source-readiness-content { padding: var(--em-space-4); min-height: 0; overflow-y: auto; }
.em-source-readiness-checks { list-style: none; margin: 0; padding: 0; }
.em-source-readiness-check { padding: var(--em-space-4) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-source-readiness-check-heading { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: var(--em-space-2); }
.em-source-readiness-check h3 { margin: 0; font-size: var(--em-fs-strong); }
.em-source-readiness-check p { margin: var(--em-space-2) 0 0; max-width: 78ch; }
.em-source-readiness-status { padding: 2px var(--em-space-2); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); font-size: var(--em-fs-caption); }
.em-source-readiness-status[data-status="blocked"], .em-source-readiness-status[data-status="warning"] { border-color: var(--em-tone-conflict); }
.em-source-readiness-note { margin: var(--em-space-4) 0; color: var(--em-text-secondary); max-width: 78ch; }
.em-source-readiness-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--em-space-2); }
.em-frontier-compat-list > li { display: flex; flex-wrap: wrap; gap: var(--em-space-2); }

/* Passive identity records shared by review and the design harness. */
.em-idn-record { padding: var(--em-space-4); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-md); background: var(--em-surface-0); min-width: 0; }
.em-idn-record header { border-bottom: 1px solid var(--em-border-subtle); padding-bottom: var(--em-space-3); }
.em-idn-source { color: var(--em-text-secondary); }
.em-idn-fields { margin: var(--em-space-4) 0; padding: var(--em-space-3); background: var(--em-surface-1); border-radius: var(--em-radius-sm); }
.em-idn-fields > div { display: grid; grid-template-columns: minmax(7rem, 1fr) minmax(0, 2fr); gap: var(--em-space-3); padding: var(--em-space-2) 0; }
.em-idn-record section { margin-top: var(--em-space-4); }
.em-idn-related li { display: flex; flex-direction: column; align-items: start; gap: var(--em-space-1); padding-bottom: var(--em-space-2); }
.em-idn-group-meta { display: flex; flex-wrap: wrap; gap: var(--em-space-5); padding: var(--em-space-4); background: var(--em-surface-1); }
.em-idn-group-meta dd { margin-top: var(--em-space-2); }
.em-idn-record { overflow-wrap: anywhere; }
.em-idn-record h3 { font-size: var(--em-fs-strong); margin: 0 0 var(--em-space-3); }
.em-idn-record h4 { font-size: var(--em-fs-body); margin: 0 0 var(--em-space-2); }
.em-idn-fields dt, .em-idn-group-meta dt { color: var(--em-text-secondary); }
.em-idn-fields dd, .em-idn-group-meta dd { margin-left: 0; min-width: 0; }
@media (max-width: 400px) {
  .em-idn-fields > div { grid-template-columns: minmax(0, 1fr); gap: var(--em-space-1); }
}

/* Live identity review: shared EM surfaces and spacing; bounded source panels. */
.em-identity-live { min-width: 0; }
.em-identity-live h2, .em-identity-live h3 { margin-top: 0; }
.em-identity-live p { line-height: 1.5; }
.em-identity-heading { display: flex; align-items: start; justify-content: space-between; gap: 16px; }
.em-identity-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.em-identity-card { padding: 16px; border: 1px solid var(--em-border-subtle); border-radius: 6px; background: var(--em-surface-0); margin: 16px 0; min-width: 0; overflow-wrap: anywhere; }
.em-identity-card .em-input { width: 100%; margin: 8px 0; padding: 10px 12px; box-sizing: border-box; }
.em-identity-card .em-btn { margin: 4px 8px 4px 0; }
.em-identity-card dt { font-weight: 600; margin-top: 12px; }
.em-identity-card dd { margin: 4px 0 0; }
.em-identity-card code { white-space: normal; overflow-wrap: anywhere; }
@media (max-width: 800px) { .em-identity-pair { grid-template-columns: minmax(0, 1fr); } .em-identity-heading { flex-wrap: wrap; } }

/* Live review interactions use the same controls and neutral surfaces as Admin. */
.em-identity-live { box-sizing: border-box; container-type: inline-size; }
.em-identity-live h4 { font-size: var(--em-fs-body); margin: 0 0 var(--em-space-2); }
.em-identity-title { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--em-space-3); margin-top: var(--em-space-5); }
.em-identity-title h3 { margin: 0; }
.em-identity-search { display: flex; align-items: end; flex-wrap: wrap; gap: var(--em-space-2); }
.em-identity-search > .em-form-field { flex: 1 1 12rem; min-width: 0; margin: 0; }
.em-identity-live .em-form-field { min-width: 0; }
.em-identity-selected, .em-identity-decision, .em-identity-snapshot { margin-top: var(--em-space-4); padding: var(--em-space-4); background: var(--em-surface-1); border-radius: var(--em-radius-sm); }
.em-identity-selected p { margin: var(--em-space-2) 0; }
.em-identity-results, .em-identity-cases { list-style: none; padding: 0; margin: var(--em-space-3) 0 0; }
.em-identity-results > li, .em-identity-cases > li { padding: var(--em-space-2) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-identity-results .em-btn, .em-identity-cases .em-btn { width: 100%; text-align: left; display: flex; align-items: baseline; justify-content: space-between; gap: var(--em-space-3); flex-wrap: wrap; }
.em-identity-results details { color: var(--em-text-secondary); font-size: var(--em-fs-caption); }
.em-identity-live summary { cursor: pointer; }
.em-identity-live summary:focus-visible { outline: 2px solid var(--em-accent); outline-offset: 3px; }
.em-identity-context-group > button + button { margin-inline-start: var(--em-space-3); }
.em-identity-actions { display: flex; flex-wrap: wrap; gap: var(--em-space-3); }
.em-identity-actions > div { flex: 1 1 13rem; }
.em-identity-actions p { font-size: var(--em-fs-caption); margin: var(--em-space-1) 0; }
.em-identity-evidence { margin: var(--em-space-3) 0; padding: var(--em-space-3); border: 1px solid var(--em-border-subtle); }
.em-identity-evidence label { display: flex; align-items: start; gap: var(--em-space-2); margin: var(--em-space-2) 0; }
.em-identity-evidence code { min-width: 0; }
.em-identity-history { list-style: none; padding: 0; margin: 0; }
.em-identity-history li { padding: var(--em-space-4) 0; border-bottom: 1px solid var(--em-border-subtle); }
.em-identity-history li:last-child { border-bottom: 0; }
.em-identity-history p { margin: var(--em-space-2) 0 0; white-space: pre-wrap; }
.em-identity-notice { padding: var(--em-space-4); border-left: 3px solid var(--em-tone-candidate); background: var(--em-surface-1); }
.em-identity-group-history { margin-top: var(--em-space-4); }
@container (max-width: 680px) {
  .em-identity-pair { grid-template-columns: minmax(0, 1fr); }
}

/* Relationship proposals share the modal and form theme. */
.em-relationship-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
.em-relationship-endpoint { min-width: 0; padding: 1rem; border: 1px solid var(--em-border-subtle); border-radius: 6px; }
.em-relationship-selected, .em-relationship-sentence { padding: .75rem; background: var(--em-surface-1); margin-bottom: .75rem; }
.em-relationship-results { padding-left: 1.25rem; max-height: 14rem; overflow: auto; }
.em-relationship-results li { margin: .5rem 0; }
.em-relationship-endpoint code { overflow-wrap: anywhere; }
.em-relationship-endpoint form > .em-btn { margin-top: var(--em-space-2); }
/* Resource actions are a menu, with one comfortably spaced action per row. */
.em-graph-context {
  z-index: 80;
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: max-content;
  min-width: min(17rem, calc(100vw - 16px));
  max-width: calc(100vw - 16px);
  max-height: calc(100vh - 16px);
  overflow-y: auto;
  padding: 8px;
  background: var(--em-surface-0);
  border: 1px solid var(--em-border-subtle);
  border-radius: var(--em-radius-md);
  box-shadow: var(--em-elevation-overlay);
}
.em-graph-context > button {
  appearance: none;
  display: block;
  flex-shrink: 0;
  width: 100%;
  min-height: 42px;
  margin: 0;
  padding: 10px 14px;
  border: 0;
  border-radius: var(--em-radius-sm);
  background: transparent;
  box-shadow: none;
  color: var(--em-text-primary);
  font: inherit;
  line-height: 1.4;
  text-align: left;
  white-space: normal;
  overflow-wrap: anywhere;
  cursor: pointer;
}
.em-graph-context > button:hover,
.em-graph-context > button:focus-visible { background: var(--em-accent-weak); }
.em-graph-context > button:focus-visible {
  outline: 2px solid var(--em-accent);
  outline-offset: -2px;
}
@media (max-width: 720px) { .em-relationship-pair { grid-template-columns: 1fr; } }

.em-session-person-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.em-session-person-line .em-session-current-label { margin-top: 0; }

/* Keep readable panes when a three-column workbench no longer fits. These are
   presentation-only overrides: the graph stays mounted and saved splitter widths
   resume when the window widens. Hidden gutters cannot receive keyboard focus. */
.em-insp-titlebar { gap: var(--em-space-3); flex-wrap: wrap; }
.em-insp-title { overflow-wrap: anywhere; }
.em-insp-tabs { flex-wrap: wrap; }
.em-inspector-inner { min-width: 0; width: 100%; }
@media (max-width: 1200px) {
  .em-outer-split { overflow-y: auto; align-items: flex-start; }
  .em-outer-split > .em-split-p1 { flex: 0 0 240px !important; position: sticky; top: 0; height: 100%; }
  .em-outer-split > .em-split-p1:has(.em-rail-collapsed) { flex-basis: 36px !important; }
  .em-outer-split > .em-split-p2 { overflow: visible; }
  /* Administration's single-pane screens bypass the inner splitter. Give that
     direct workspace the same bounded height as a stacked inner workspace; an
     auto-height flex pane with zero-basis descendants otherwise collapses. */
  .em-admin-split > .em-split-p2:has(> .em-workspace) { height: max(640px, 80vh); }
  .em-outer-split > .em-split-gutter,
  .em-inner-split > .em-split-gutter { display: none; }
  .em-outer-split > .em-split-p2 > .em-review-layout { flex: none; width: 100%; }
  .em-inner-split { flex: none; flex-direction: column; gap: var(--em-space-3); }
  .em-inner-split > .em-split-p1 { flex: none !important; width: 100%; height: max(640px, 80vh); }
  .em-inner-split > .em-split-p2 { flex: none !important; width: 100%; height: 520px; }
  .em-inner-split > .em-split-p2:has(.em-inspector-collapsed) { height: 44px; }
  .em-inner-split .em-inspector-collapsed { width: 100% !important; }
  .em-review-focused > .em-split-p2 { height: max(640px, 80vh); }
}
@media (max-width: 800px) {
  .em-outer-split { flex-direction: column; }
  .em-outer-split > .em-split-p1 { position: static; flex: none !important; width: 100%; height: 220px; }
  .em-outer-split > .em-split-p1:has(.em-rail-collapsed) { height: 44px; }
  .em-outer-split .em-rail-collapsed { width: 100% !important; }
  .em-outer-split > .em-split-p2 { flex: none; width: 100%; }
  .em-context-bar { column-gap: 12px !important; }
  .em-field { flex-wrap: wrap; }
  .em-field-value { flex-basis: 180px; }
}

.em-relationship-claims { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--em-space-2); }
.em-relationship-claims > li { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--em-space-2); padding: var(--em-space-3); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); }
.em-relationship-claims > li:has([aria-pressed="true"]) { border-color: var(--em-accent); background: var(--em-accent-weak); }

.em-identity-evidence-choice { padding: var(--em-space-3); margin-top: var(--em-space-2); border: 1px solid var(--em-border-subtle); border-radius: var(--em-radius-sm); background: var(--em-surface-0); }
.em-identity-evidence-choice p { margin: var(--em-space-2) 0; }
.em-identity-evidence-choice details { margin-top: var(--em-space-2); }

.em-group-scope-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--em-space-3); }

.em-identity-history > li > span + button { margin-inline-start: var(--em-space-3); }

.em-reference-return { padding: var(--em-space-2) var(--em-space-3); }

/* Labeled search controls align with their action buttons, not with the label. */
.em-vault-search { align-items: end; margin-bottom: var(--em-space-3); }
.em-vault-search > .em-form-field { flex: 1 1 18rem; min-width: 0; }
.em-vault-search .em-input, .em-vault-search .em-btn { box-sizing: border-box; min-height: 2.5rem; margin: 0; }

/* Rule fields use a consistent vertical rhythm and the shared wrapping search row. */
.em-dynamic-rule-editor { display: flex; flex-direction: column; gap: var(--em-space-3); }
.em-dynamic-rule-editor > h3, .em-dynamic-rule-editor > h4,
.em-dynamic-rule-editor > p, .em-dynamic-rule-editor > ul { margin: 0; }
.em-dynamic-rule-editor > h4 { margin-top: var(--em-space-3); }
.em-dynamic-rule-editor .em-input, .em-dynamic-rule-editor .em-select,
.em-dynamic-rule-editor .em-btn { box-sizing: border-box; min-height: 2.5rem; margin: 0; }
.em-dynamic-rule-editor > .em-btn { align-self: start; }
.em-dynamic-rule-editor > label { display: flex; align-items: center; gap: var(--em-space-2); }
.em-dynamic-parent-selection { align-items: center; }
.em-dynamic-parent-selection details { flex: 1 1 14rem; min-width: 0; }

.em-group-categories { flex-wrap: wrap; margin-bottom: var(--em-space-3); }

.em-btn.em-multi-select-active { background: var(--em-accent-weak); border-color: var(--em-accent); color: var(--em-text-primary); font-weight: var(--em-fw-bold); }
