/* ============================================================
   Event Publishing Console — design tokens & base styles
   Drop-in at: src/main/resources/static/console/epc.css
   Source: claude-design handoff (retrace admin redesign)
   ============================================================ */

:root {
  --font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI Variable', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
  --font-ui: var(--font-sans);

  --accent-h: 200;
  --accent-c: 0.12;

  --d-pad: 16px;
  --d-row: 40px;
  --d-gap: 16px;
  --d-card-pad: 20px;
  --d-text: 14px;
  --d-text-sm: 12px;
  --d-radius: 10px;

  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;

  --shell-max: 1800px;

  /* Dialogs track the page work surface below the two-row app chrome on roomy viewports. */
  --dialog-work-surface-top: clamp(124px, 14vh, 180px);
  --dialog-viewport-gutter: 24px;

  --ease: cubic-bezier(.22, 1, .36, 1);
}

[data-theme="dark"] {
  /* Flat-surface palette from the pencil target (account review redesign): page #0B0E14,
     cards/topbar #10141B, raised controls #151A22, hairlines #1A212B, card/control strokes
     #232B37. */
  --bg: #0b0e14;
  --bg-elev: #10141b;
  --bg-elev-2: #151a22;
  --bg-hover: #1a212b;
  --bg-inset: #0b0e14;
  --border: #1a212b;
  --border-strong: #232b37;
  --border-subtle: #181e28;
  --text: #e7ecf2;
  --text-muted: #97a1b0;
  --text-dim: #5e6878;
  /* --accent* stay oklch: they read the runtime accent-hue picker (--accent-h / --accent-c, set
     from the persisted `accentHue` signal). Every other token below is static, so it's hex/rgba to
     match the flat-surface palette — one consistent system across the dark theme. */
  --accent: oklch(0.78 var(--accent-c) var(--accent-h));
  --accent-ink: #0d1216;
  --accent-soft: oklch(0.78 var(--accent-c) var(--accent-h) / 0.14);
  --accent-line: oklch(0.78 var(--accent-c) var(--accent-h) / 0.35);
  --success: #46c28a;
  --success-soft: rgba(70, 194, 138, 0.14);
  --warn: #e0b050;
  --warn-soft: rgba(224, 176, 80, 0.14);
  --danger: #ff6e70;
  --danger-soft: rgba(255, 110, 112, 0.14);
  --info: #4ac9ec;
  --info-soft: rgba(74, 201, 236, 0.14);
  --shadow-card: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 4px 14px -6px rgba(0, 0, 0, 0.5);
  --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5);
  --grid-line: rgba(255, 255, 255, 0.025);
  --json-key: #71bfff;
  --json-str: #89dd76;
  --json-num: #f8a052;
  --json-lit: #c689ff;
  /* AI-affordance palette (epc-ai-border): deliberately multi-hue and distinct from the
     single-accent chrome — these colors mean "an AI is behind this element". */
  --ai-1: #8b5cf6;
  --ai-2: #22d3ee;
  --ai-3: #e879f9;
}

[data-theme="light"] {
  --bg: oklch(0.985 0.004 240);
  --bg-elev: oklch(1 0 0);
  --bg-elev-2: oklch(0.97 0.005 240);
  --bg-hover: oklch(0.95 0.006 240);
  --bg-inset: oklch(0.965 0.005 240);
  --border: oklch(0.9 0.008 240);
  --border-strong: oklch(0.82 0.01 240);
  --border-subtle: oklch(0.94 0.006 240);
  --text: oklch(0.22 0.015 240);
  --text-muted: oklch(0.46 0.015 240);
  --text-dim: oklch(0.6 0.015 240);
  --accent: oklch(0.55 var(--accent-c) var(--accent-h));
  --accent-ink: oklch(1 0 0);
  --accent-soft: oklch(0.55 var(--accent-c) var(--accent-h) / 0.12);
  --accent-line: oklch(0.55 var(--accent-c) var(--accent-h) / 0.35);
  --success: oklch(0.55 0.16 155);
  --success-soft: oklch(0.55 0.16 155 / 0.12);
  --warn: oklch(0.6 0.15 70);
  --warn-soft: oklch(0.6 0.15 70 / 0.14);
  --danger: oklch(0.55 0.2 22);
  --danger-soft: oklch(0.55 0.2 22 / 0.12);
  --info: oklch(0.55 0.14 220);
  --info-soft: oklch(0.55 0.14 220 / 0.12);
  --shadow-card: 0 1px 0 oklch(1 0 0 / 0.8) inset, 0 1px 2px oklch(0 0 0 / 0.04), 0 8px 20px -12px oklch(0 0 0 / 0.08);
  --grid-line: oklch(0 0 0 / 0.04);
  /* AI-affordance palette — deeper variants for light surfaces. */
  --ai-1: #7c3aed;
  --ai-2: #0891b2;
  --ai-3: #c026d3;
}

[data-density="compact"] { --d-pad: 10px; --d-row: 32px; --d-gap: 10px; --d-card-pad: 14px; --d-text: 13px; --d-text-sm: 11px; --d-radius: 8px; }
[data-density="roomy"]   { --d-pad: 22px; --d-row: 48px; --d-gap: 22px; --d-card-pad: 26px; --d-text: 15px; --d-text-sm: 13px; --d-radius: 12px; }
[data-body-font="mono"]  { --font-ui: var(--font-mono); }

/* base -------------------------------------------------------- */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; min-height: 100vh; background: var(--bg); color: var(--text); font-family: var(--font-ui); font-size: var(--d-text); line-height: 1.5; -webkit-font-smoothing: antialiased; }
body[data-grid="on"]::before { content: ''; position: fixed; inset: 0; pointer-events: none; background-image: linear-gradient(to right, var(--grid-line) 1px, transparent 1px), linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px); background-size: 40px 40px; z-index: 0; mask-image: radial-gradient(ellipse 100% 80% at 50% 30%, black 40%, transparent 100%); }

/* Mouse-reactive grid glow: the SAME 40px grid repainted in the accent tint, revealed only
   inside a soft radius around the cursor. JS (epc-helpers "grid glow") lerps --grid-mx/--grid-my
   toward the pointer for a trailing, energy-like feel; the breathe keyframe gives the lit lines
   a slow pulse. Pure mask + gradient — GPU-composited, no canvas, no per-frame layout. Hidden on
   touch (no hover) and for reduced-motion users; follows the same data-grid toggle as the grid. */
body[data-grid="on"]::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: linear-gradient(to right, var(--accent-line) 1px, transparent 1px), linear-gradient(to bottom, var(--accent-line) 1px, transparent 1px);
  background-size: 40px 40px;
  opacity: 0;
  transition: opacity 0.5s ease;
  -webkit-mask-image: radial-gradient(260px circle at var(--grid-mx, 50%) var(--grid-my, -400px), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.28) 45%, transparent 72%);
  mask-image: radial-gradient(260px circle at var(--grid-mx, 50%) var(--grid-my, -400px), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.28) 45%, transparent 72%);
}
body[data-grid="on"].epc-grid-live::after { opacity: 1; animation: epc-grid-breathe 3.6s ease-in-out infinite; }
@keyframes epc-grid-breathe { 0%, 100% { opacity: 0.55; } 50% { opacity: 0.95; } }
@media (hover: none), (prefers-reduced-motion: reduce) {
  body[data-grid="on"]::after { display: none; }
}

/* ── AI-ish element affordance ─────────────────────────────────────────────────────────
   .epc-ai-border marks an element as AI-backed. Hover / focus-within lights a faint 2px
   multi-hue ring (violet→cyan→magenta, deliberately distinct from the single-accent
   chrome); every 6.6s — the same cadence as the background grid pulse — one bright comet
   fires a fast lap around it: the "discharge". Grid and card speak one visual language.
   Reusable prep for AI surfaces console-wide; the login card is the pilot adopter.
   Mechanics: the element's OWN ::before (static ring) and ::after (discharge comet) paint
   conic gradients as rings via padding-box xor masks, radius inherited — adopters must
   keep both pseudos free; any own border is simply covered while lit. GPU-only;
   reduced-motion gets the static ring with no discharge. */
@property --epc-ai-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}
.epc-ai-border { position: relative; }
.epc-ai-border::before,
.epc-ai-border::after {
  content: '';
  position: absolute;
  border-radius: inherit;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity 0.45s ease;
  pointer-events: none;
}
.epc-ai-border::before {
  inset: -2px;
  padding: 2px;
  background: conic-gradient(from 220deg,
    color-mix(in srgb, var(--ai-1) 35%, transparent),
    color-mix(in srgb, var(--ai-2) 35%, transparent),
    color-mix(in srgb, var(--ai-3) 35%, transparent),
    color-mix(in srgb, var(--ai-1) 35%, transparent));
}
.epc-ai-border::after {
  inset: -3px;
  padding: 4px;
  background: conic-gradient(from var(--epc-ai-angle), transparent 0turn 0.7turn, var(--ai-2) 0.82turn, var(--ai-1) 0.88turn, transparent 0.94turn);
  filter: blur(3px);
}
.epc-ai-border:hover::before,
.epc-ai-border:focus-within::before { opacity: 0.8; }
.epc-ai-border:hover::after,
.epc-ai-border:focus-within::after { animation: epc-ai-discharge 6.6s linear infinite; }
@keyframes epc-ai-discharge {
  0% { --epc-ai-angle: 0deg; opacity: 0; }
  4% { opacity: 0.95; }
  22% { --epc-ai-angle: 360deg; opacity: 0.95; }
  30% { --epc-ai-angle: 360deg; opacity: 0; }
  100% { --epc-ai-angle: 360deg; opacity: 0; }
}

/* .epc-ai-working — the loud tier: add to an .epc-ai-border element while the AI is
   actively doing something (generating, thinking, streaming). Two full-brightness comet
   trails chase around the ring and the under-glow becomes a wide halo that washes light
   onto the page — a spinner replacement that needs no hover: it is on while the class is
   present, and it overrides the hover discharge (declared after it on purpose). */
.epc-ai-border.epc-ai-working::before,
.epc-ai-border.epc-ai-working::after {
  background:
    conic-gradient(from var(--epc-ai-angle), transparent 0turn 0.58turn, var(--ai-2) 0.74turn, var(--ai-1) 0.82turn, transparent 0.9turn),
    conic-gradient(from calc(var(--epc-ai-angle) + 0.5turn), transparent 0turn 0.58turn, var(--ai-3) 0.74turn, var(--accent) 0.82turn, transparent 0.9turn),
    conic-gradient(from var(--epc-ai-angle),
      color-mix(in srgb, var(--ai-1) 30%, transparent),
      color-mix(in srgb, var(--ai-2) 30%, transparent),
      color-mix(in srgb, var(--ai-3) 30%, transparent),
      color-mix(in srgb, var(--ai-1) 30%, transparent));
  animation: epc-ai-spin 2.8s linear infinite;
}
.epc-ai-border.epc-ai-working::before { opacity: 0.9; }
.epc-ai-border.epc-ai-working::after {
  inset: -5px;
  padding: 7px;
  filter: blur(18px);
  opacity: 0.85;
}
@keyframes epc-ai-spin { to { --epc-ai-angle: 360deg; } }

@media (prefers-reduced-motion: reduce) {
  .epc-ai-border:hover::after,
  .epc-ai-border:focus-within::after,
  .epc-ai-border.epc-ai-working::before,
  .epc-ai-border.epc-ai-working::after { animation: none; }
}

/* Grid pulse: a thin ring of lit grid lines travelling outward from the pointer — the
   "electricity flowing away" half of the effect. #epc-grid-pulse (created by the grid-glow
   JS, sibling overlay to body::after) paints the accent grid masked to a ring whose radius
   is the typed custom property --pulse-r; the keyframe expands it 0→640px with an eased,
   dissipating fade. JS snapshots the ring's origin (--px/--py) only between iterations so a
   travelling ring never recenters mid-flight, and restarts the animation on click for an
   immediate pulse from the click point. Same GPU-only mask machinery as the glow. */
@property --pulse-r {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}
#epc-grid-pulse {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: linear-gradient(to right, var(--accent-line) 1px, transparent 1px), linear-gradient(to bottom, var(--accent-line) 1px, transparent 1px);
  background-size: 40px 40px;
  opacity: 0;
  -webkit-mask-image: radial-gradient(circle at var(--px, 50%) var(--py, 50%), transparent max(0px, calc(var(--pulse-r) - 34px)), rgba(0, 0, 0, 0.9) var(--pulse-r), transparent calc(var(--pulse-r) + 26px));
  mask-image: radial-gradient(circle at var(--px, 50%) var(--py, 50%), transparent max(0px, calc(var(--pulse-r) - 34px)), rgba(0, 0, 0, 0.9) var(--pulse-r), transparent calc(var(--pulse-r) + 26px));
}
#epc-grid-pulse.pulsing { animation: epc-grid-pulse 6.6s cubic-bezier(0.22, 0.61, 0.36, 1) infinite; }
@keyframes epc-grid-pulse {
  0% { --pulse-r: 0px; opacity: 0.45; }
  70% { opacity: 0.18; }
  100% { --pulse-r: 640px; opacity: 0; }
}
@media (hover: none), (prefers-reduced-motion: reduce) {
  #epc-grid-pulse { display: none; }
}
button { font-family: inherit; color: inherit; }
input, select, textarea { font-family: inherit; color: inherit; }
::selection { background: var(--accent-soft); color: var(--text); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 10px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--text-dim); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
a { color: inherit; text-decoration: none; }

/* typography -------------------------------------------------- */
h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.01em; }
.h1 { font-size: 22px; line-height: 1.2; letter-spacing: -0.02em; }
.h2 { font-size: 16px; line-height: 1.25; }
.h3 { font-size: 14px; line-height: 1.3; }
.cap { font-size: 10px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }
.text-2 { color: var(--text-muted); font-size: 13px; }
.text-3 { color: var(--text-dim); font-size: 12px; }
.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 500; }
.mono { font-family: var(--font-mono); font-feature-settings: 'zero'; }
.muted { color: var(--text-muted); }
.dim { color: var(--text-dim); }
.hairline { border-top: 1px solid var(--border); }
.label { font-size: 10px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }

kbd, .kbd { font-family: var(--font-mono); font-size: 10px; padding: 2px 5px; background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 4px; color: var(--text-muted); line-height: 1; }

/* layout utilities ------------------------------------------- */
/* Use these in place of inline `style="display:flex;…"`. Read like prose:
     <div class="row gap-sm">…</div>            row, items-center, 8px gap
     <div class="row-between gap-md">…</div>    row + space-between, 12px gap
     <div class="stack gap-lg">…</div>          column, 16px gap
     <div class="grid-2">…</div>                 2-col grid (uses --d-gap)
   Defaults: .row gap=12px, .stack gap=12px. Override with gap-* modifiers. */
.row           { display: flex; align-items: center; gap: 12px; }
.row-baseline  { display: flex; align-items: baseline; gap: 8px; }
.row-start     { display: flex; align-items: flex-start; gap: 12px; }
.row-end       { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
.row-between   { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.row-spread    { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.row-wrap      { flex-wrap: wrap; }
.stack         { display: flex; flex-direction: column; gap: 12px; }
.stack-h       { display: flex; flex-direction: column; }
/* A stacked cell in a right-aligned column: both lines flush right (flex items would otherwise stretch and the muted second line keep its own left edge). */
.tbl td.actions .stack-h { align-items: flex-end; text-align: right; }
/* "[<Field> not set]" line in stacked grid cells — keeps two-line rhythm when a field is null
   (GridColumn.stackedCell placeholders) while reading as a gap, not data. */
.stack-h .cell-missing { font-style: italic; opacity: .55; }
.items-center  { align-items: center; }
.items-start   { align-items: flex-start; }
.items-baseline { align-items: baseline; }
.items-end     { align-items: flex-end; }
.justify-between { justify-content: space-between; }
.justify-end     { justify-content: flex-end; }

/* gap modifiers (override default gap on .row / .stack / grids) */
.gap-0  { gap: 0; }
.gap-xs { gap: 4px; }
.gap-sm { gap: 8px; }
.gap-10 { gap: 10px; }
.gap-md { gap: 12px; }
.gap-lg { gap: 16px; }
.gap-xl { gap: 20px; }
.gap-2xl { gap: 24px; }

/* grid (always uses minmax(0,1fr) so children can truncate) */
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--d-gap); }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--d-gap); }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--d-gap); }

/* flex children */
.flex-1        { flex: 1 1 auto; min-width: 0; }
.flex-fill     { flex: 1; min-width: 0; }
.flex-0        { flex: 0 0 auto; }
.min-w-0       { min-width: 0; }
.full-w        { width: 100%; }

/* truncation / multiline ellipsis */
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.line-2   { overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }

/* Middle-truncate: head shrinks first (ellipsis), tail stays whole. Use for
   paths / fully-qualified ids where BOTH the prefix (root/feedId) AND the
   suffix (filename/leaf) carry meaning — only the middle is droppable.
   Markup:
     <span class="truncate-mid" title="${full}">
       <span class="head">${head}</span><span class="tail">${tail}</span>
     </span>
*/
.truncate-mid       { display: inline-flex; max-width: 100%; min-width: 0; vertical-align: bottom; }
.truncate-mid .head { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1 1 auto; min-width: 0; }
.truncate-mid .tail { white-space: nowrap; flex: 0 0 auto; }

/* spacing modifiers (use sparingly; prefer flex/grid gap) */
.mt-xs { margin-top: 4px; }   .mb-xs { margin-bottom: 4px; }
.mt-sm { margin-top: 8px; }   .mb-sm { margin-bottom: 8px; }
.mt-md { margin-top: 12px; }  .mb-md { margin-bottom: 12px; }
.mt-lg { margin-top: 16px; }  .mb-lg { margin-bottom: 16px; }
.mt-xl { margin-top: 20px; }  .mb-xl { margin-bottom: 20px; }

/* padding presets that match the design system's card/sub-zone density */
.p-card        { padding: var(--d-card-pad); }
.p-snug        { padding: 14px 16px; }
.p-tight       { padding: 12px 16px; }
.p-loose       { padding: 16px 18px; }

/* card sub-zones — header strip and footer strip */
.card-head { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.card-foot { padding: 10px 14px; border-top: 1px solid var(--border); background: var(--bg-inset); display: flex; align-items: center; justify-content: space-between; gap: 12px; }

/* font-size helpers — reach for these only when text-2/text-3/cap don't fit */
.sz-9  { font-size: 9px; }
.sz-10 { font-size: 10px; }
.sz-11 { font-size: 11px; }
.sz-12 { font-size: 12px; }
.sz-13 { font-size: 13px; }
.sz-14 { font-size: 14px; }
.sz-15 { font-size: 15px; }
.sz-22 { font-size: 22px; }

/* tabular numerics for stat-style numbers */
.tnum { font-variant-numeric: tabular-nums; }

/* icon tile — small/medium colored squares used as avatars next to row text */
.icon-tile { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; border-radius: var(--radius-sm); background: var(--bg-inset); color: var(--text-muted); }
.icon-tile.sm   { width: 24px; height: 24px; }
.icon-tile.md   { width: 36px; height: 36px; border-radius: 8px; }
.icon-tile.lg   { width: 42px; height: 42px; border-radius: 10px; }
.icon-tile.accent  { background: var(--accent-soft);  color: var(--accent); }
.icon-tile.success { background: var(--success-soft); color: var(--success); }
.icon-tile.warn    { background: var(--warn-soft);    color: var(--warn); }
.icon-tile.danger  { background: var(--danger-soft);  color: var(--danger); }
.icon-tile.info    { background: var(--info-soft);    color: var(--info); }

/* visual modifiers */
.italic { font-style: italic; }
.color-accent  { color: var(--accent); }
.color-success { color: var(--success); }
.color-warn    { color: var(--warn); }
.color-danger  { color: var(--danger); }
.overflow-hidden { overflow: hidden; }

/* schema-tree indent levels (replaces inline padding-left).
   Step is 16px per level. JSON:API hierarchy reaches lvl-4 today; add lvl-5+
   on demand if a deeper schema needs to render — keep the 16px step. */
.schema-row.lvl-1 { padding-left: 16px; }
.schema-row.lvl-2 { padding-left: 32px; }
.schema-row.lvl-3 { padding-left: 48px; }
.schema-row.lvl-4 { padding-left: 64px; }

/* chip / dot -------------------------------------------------- */
.chip { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; height: 20px; border-radius: 999px; font-size: 11px; font-weight: 500; letter-spacing: 0.02em; font-family: var(--font-mono); border: 1px solid var(--border); background: var(--bg-elev-2); color: var(--text-muted); white-space: nowrap; }
.chip.sm { height: 18px; font-size: 10px; padding: 0 6px; }
/* Enum glyphs inside chips (grid + review): icon leads the label; tighten the left padding and
   the inherited 6px gap so the icon sits snug. The svg inherits chip colour via currentColor. */
.chip-icon { gap: 4px; padding-left: 6px; }
.chip svg { flex: 0 0 auto; }
/* Contained set rendered as wrapping chips (review page): enum sets reuse .chip(.chip-icon),
   string sets use .tag-chip — a quiet mono tag. */
/* Shared count cell (GridColumns.countChipHtml): a .chip.sm number; the navigable variant wraps it
   in a link with a trailing arrow — the only difference between an in-row size and a child count. */
.count-nav { display: inline-flex; align-items: center; gap: 3px; text-decoration: none; }
.count-nav::after { content: "\203A"; color: var(--text-dim); font-size: 14px; line-height: 1; transition: color 120ms var(--ease); }
.count-nav:hover .chip { border-color: var(--border-strong); color: var(--text); }
.count-nav:hover::after { color: var(--text-muted); }
.chip-set { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tag-chip { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 5px; background: var(--bg-elev); border: 1px solid var(--border-subtle); color: var(--text-muted); font-size: 11.5px; line-height: 1.5; white-space: nowrap; }
.chip.warn, .chip.status-warn { color: var(--warn); background: var(--warn-soft); border-color: transparent; }
.chip.err, .chip.status-err { color: var(--danger); background: var(--danger-soft); border-color: transparent; }
.chip.ok, .chip.status-ok { color: var(--success); background: var(--success-soft); border-color: transparent; }
/* Two-axis feed-delivery chip (overview Delivery column). */
.chip.litefeed { color: var(--success); background: var(--success-soft); border-color: transparent; }
.chip.json_ingest { color: var(--warn); background: var(--warn-soft); border-color: transparent; }
.chip.projection { color: var(--info); background: var(--info-soft); border-color: transparent; }
.chip.proxy { color: var(--info); background: var(--info-soft); border-color: transparent; }
.chip.mixed { color: var(--accent); background: var(--accent-soft); border-color: transparent; }
.chip.none { color: var(--text-muted); }
.chip.info, .chip.status-info { color: var(--info); background: var(--info-soft); border-color: transparent; }
.chip.accent { color: var(--accent); background: var(--accent-soft); border-color: transparent; }
.chip.status-muted { color: var(--text-muted); }
.chip.method-get { color: var(--info); background: var(--info-soft); border-color: transparent; }
.chip.method-post { color: var(--success); background: var(--success-soft); border-color: transparent; }
.chip.method-put, .chip.method-patch { color: var(--warn); background: var(--warn-soft); border-color: transparent; }
.chip.method-delete { color: var(--danger); background: var(--danger-soft); border-color: transparent; }
.chip.method-sse, .chip.method-head { color: var(--accent); background: var(--accent-soft); border-color: transparent; }

.dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.dot.ok { background: var(--success); box-shadow: 0 0 0 3px var(--success-soft); }
.dot.warn { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-soft); }
.dot.err { background: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft); animation: epc-pulse 1.8s var(--ease) infinite; }
.dot.idle { background: var(--text-dim); }
@keyframes epc-pulse { 0%, 100% { box-shadow: 0 0 0 3px var(--danger-soft); } 50% { box-shadow: 0 0 0 6px color-mix(in oklab, var(--danger) 5%, transparent); } }

/* card -------------------------------------------------------- */
.card { background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: var(--d-radius); box-shadow: var(--shadow-card); }
.card:hover { border-color: var(--border-strong); }

/* buttons ----------------------------------------------------- */
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 0 14px; height: 34px; border-radius: var(--radius-sm); border: 1px solid var(--border-strong); background: var(--bg-elev); color: var(--text); font-size: 13px; font-weight: 500; cursor: pointer; transition: all 120ms var(--ease); text-decoration: none; white-space: nowrap; }
.btn:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.btn.primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; font-weight: 600; }
.btn.primary:hover { filter: brightness(1.08); }
.btn.ghost { background: transparent; border-color: transparent; }
.btn.ghost:hover { background: var(--bg-hover); }
/* outlined ghost: transparent fill + a faint border so it reads as a button while staying calm
   (used for inline field actions, e.g. Rename). Border brightens on hover. */
.btn.ghost.outlined { border-color: var(--border-subtle); color: var(--text-muted); }
.btn.ghost.outlined:hover { border-color: var(--border-strong); color: var(--text); }
.btn.small, .btn.sm { height: 26px; padding: 0 10px; font-size: 12px; }
.btn.danger { color: var(--danger); }
.btn.danger:hover { background: var(--danger-soft); border-color: var(--danger); }
.btn.danger.primary { background: var(--danger); color: white; }
.btn.danger.primary:hover { filter: brightness(1.08); }
.btn.icon { width: 34px; padding: 0; justify-content: center; }
.btn.small.icon { width: 26px; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.5; pointer-events: none; cursor: not-allowed; }

/* truncated id (UUID) cell ------------------------------------ */
/* Click-to-copy via window.epcCopyId; full value lives in title= and data-full.
   Hover decorates with a copy-icon glyph so the click affordance is obvious;
   click flashes a green halo + toast (epcToast) for ~900ms. */
.trunc-id { cursor: pointer; user-select: none; border-bottom: 1px dashed var(--border);
            border-radius: 3px; padding: 1px 4px; margin: -1px -4px;
            white-space: nowrap; display: inline-block;
            transition: background-color 120ms var(--ease), color 120ms var(--ease),
                        border-color 120ms var(--ease), box-shadow 120ms var(--ease); }
.trunc-id:hover { color: var(--accent); border-bottom-color: var(--accent);
                  background: var(--bg-hover); }
.trunc-id:hover::after { content: "⧉"; opacity: 0.8; font-size: 11px; margin-left: 4px; }
.trunc-id.copied { color: var(--success); border-bottom-color: var(--success);
                   background: var(--success-soft);
                   box-shadow: 0 0 0 3px var(--success-soft);
                   animation: epc-copy-pulse 900ms var(--ease); }
.trunc-id.copied::after { content: "✓"; color: var(--success); opacity: 1; margin-left: 4px; }
@keyframes epc-copy-pulse {
  0%   { box-shadow: 0 0 0 0   oklch(0.7 0.18 145 / 0.5); }
  40%  { box-shadow: 0 0 0 8px oklch(0.7 0.18 145 / 0.0); }
  100% { box-shadow: 0 0 0 0   oklch(0.7 0.18 145 / 0.0); }
}

/* semantic email value — used in grid cells (EmailCellRenderer) and review detail rows */
.value-email-wrap { display: inline-flex; align-items: center; gap: 6px; }
.value-email { display: inline-flex; align-items: center; gap: 3px; color: inherit; text-decoration: none; }
.value-email:hover { color: var(--accent); text-decoration: underline; }
.value-email-icon { opacity: 0.55; font-size: 11px; }
/* inline copy trigger (⧉) for copyable values */
.value-copy { cursor: pointer; opacity: 0.35; font-size: 11px; user-select: none;
              transition: opacity 120ms, color 120ms; }
.value-copy:hover { opacity: 1; color: var(--accent); }
.value-copy.copied { color: var(--success); opacity: 1; }

/* per-row ops overflow ⋯ menu --------------------------------- */
/* JS-toggled (window.epcToggleOpsOverflow). Menu uses position: fixed +
   JS-computed coords so it escapes table-cell overflow clipping. */

/* inputs ------------------------------------------------------ */
.input, .select, textarea.input { height: 32px; padding: 0 10px; background: var(--bg-inset); border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text); font-size: 13px; transition: border-color 120ms var(--ease); width: 100%; font-family: inherit; }
.select { appearance: none; -webkit-appearance: none; -moz-appearance: none; padding-right: 30px; background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a0a8b0' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 8px center; background-size: 14px 14px; cursor: pointer; }
.select:hover { border-color: var(--border-strong); }
.select-wrap { position: relative; }
textarea.input { height: auto; padding: 8px 10px; line-height: 1.5; resize: vertical; }
.input:focus, .select:focus, textarea.input:focus { outline: none; border-color: var(--accent-line); background: var(--bg-elev); }
.input::placeholder { color: var(--text-dim); }
.input.sm { height: 28px; font-size: 12px; padding: 0 8px; }
[data-theme="dark"] .epc-tryit-param[type="date"],
[data-theme="dark"] .epc-tryit-param[type="datetime-local"] {
  color-scheme: dark;
  padding-right: 5px;
}
[data-theme="dark"] .epc-tryit-param[type="date"]::-webkit-calendar-picker-indicator,
[data-theme="dark"] .epc-tryit-param[type="datetime-local"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
  filter: invert(1);
  opacity: 0.9;
  padding: 3px;
}

.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.field.row { flex-direction: row; align-items: center; gap: 10px; }
.field > legend { padding: 0; }

/* form-field abstraction (used by form_field.jte) ------------- */
.form-field { display: flex; flex-direction: column; gap: 6px; }
.form-field-label { font-size: 13px; font-weight: 500; color: var(--text); }
/* Sits between the label and the control (see form_field.jte): tucked up against the label it
   belongs to, with the control below it, so proximity groups it with the right field. */
.form-field-hint { font-size: 12px; color: var(--text-dim); margin: -2px 0 6px; }
/* The resolved outcome under an input (an op parameter's `preview:`) — the literal line a customer
   will call. Mono and accented because it is an address, not prose: it should read as the thing
   about to be published, distinct from the hint that explains the field. */
.form-field-preview { font-family: var(--font-mono); font-size: 11px; color: var(--accent); word-break: break-all; }
.form-field-error { font-size: 12px; color: var(--danger); }
.form-field .input[disabled], .form-field .input.is-disabled { opacity: 0.7; }
/* Read-only display for a context-pinned op param (opSheet.jte pinned branch): looks like a
   disabled input — boxed + muted — so it reads as a fixed value, not editable text. */
.form-field-static {
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface-2, rgba(127,127,127,0.08)); color: var(--text); opacity: 0.85;
}
/* Live unique-field check status (set by epcInstallUniqueChecks). Empty
   container by default; gets a class + text when a check completes. */
.form-field-unique-status { font-size: 12px; color: var(--text-dim); min-height: 14px; }
.form-field-unique-status.is-ok { color: var(--success, #2c8a3a); }
.form-field-unique-status.is-err { color: var(--danger); }
.form-field-unique-status.is-pending { color: var(--text-dim); font-style: italic; }
/* Footer row: hint/error on the left, live validation message on the right (same line). */
.form-field-footer { display: flex; align-items: baseline; gap: 8px; min-height: 14px; }
/* Live HTML5 validation status (set by epcInstallFieldValidation). */
.form-field-validity { font-size: 12px; color: var(--danger); }

/* boolean toggle switch (form_field type="checkbox") -------- */
/* Distinct from the older .toggle button-toggler (used in tweaks_panel,
   epcToggleRow): named .toggle-switch to avoid cascade collision. The
   native <input> is visually hidden but stays in flex flow so the wrapping
   <label> reliably toggles it. State styles use sibling combinators
   (:checked + .track, :checked ~ .state) for universal browser support. */
.toggle-switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; align-self: flex-start; user-select: none; position: relative; }
.toggle-switch-input { position: absolute; opacity: 0; width: 36px; height: 20px; margin: 0; cursor: pointer; }
/* The track + state are purely decorative (aria-hidden=true); take them out
   of the pointer-event chain so clicks fall through to the wrapping <label>,
   which forwards them to the input. Without this, Playwright (and any other
   precision pointer that targets the topmost element) keeps hitting the
   track and never flips the checkbox. */
.toggle-switch-track { position: relative; width: 36px; height: 20px; background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 999px; transition: background 150ms var(--ease), border-color 150ms var(--ease); flex-shrink: 0; pointer-events: none; }
.toggle-switch-thumb { position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: var(--text-dim); transition: transform 150ms var(--ease), background 150ms var(--ease); }
.toggle-switch-state { pointer-events: none; }
.toggle-switch-input:checked + .toggle-switch-track { background: var(--accent); border-color: var(--accent); }
.toggle-switch-input:checked + .toggle-switch-track .toggle-switch-thumb { transform: translateX(16px); background: var(--accent-ink); }
.toggle-switch .toggle-switch-state { font-size: 13px; min-width: 28px; }
.toggle-switch .toggle-switch-state::before { content: "Off"; color: var(--text-dim); }
.toggle-switch-input:checked ~ .toggle-switch-state::before { content: "On"; color: var(--text); font-weight: 500; }
.toggle-switch-input:focus-visible + .toggle-switch-track { box-shadow: 0 0 0 2px var(--accent-soft); }
.toggle-switch-input[disabled] ~ .toggle-switch-track { opacity: 0.6; cursor: not-allowed; }

/* tristate / segmented control (form_field type="tristate") -- */
/* Nullable Boolean as a chip-row segmented control. The radio inputs are
   visually hidden but kept in flow; chip styling on each label switches
   via the descendant :checked combinator (no :has() required). */
.seg-toggle { display: inline-flex; gap: 0; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; align-self: flex-start; }
.seg-toggle label { position: relative; padding: 6px 14px; font-size: 13px; cursor: pointer; background: transparent; border-right: 1px solid var(--border); color: var(--text-dim); transition: background 120ms var(--ease), color 120ms var(--ease); }
.seg-toggle label:last-child { border-right: 0; }
.seg-toggle label:hover { background: var(--bg-hover); color: var(--text); }
.seg-toggle input { position: absolute; opacity: 0; width: 100%; height: 100%; top: 0; left: 0; margin: 0; cursor: pointer; }
.seg-toggle input:checked + span { color: var(--accent-ink); font-weight: 500; }
.seg-toggle label:has(input:checked) { background: var(--accent); color: var(--accent-ink); font-weight: 500; }
.seg-toggle:focus-within { box-shadow: 0 0 0 2px var(--accent-soft); }
.form-field-validity.is-err { color: var(--danger); }
.form-field-validity.is-ok  { color: var(--success); font-weight: 500; }
.form-field .input.is-invalid { border-color: var(--danger); }

/* form layouts: vertical stack of fields, used in sheets and pages */
.form-stack { display: flex; flex-direction: column; gap: 16px; }

/* T3#19 inlineCreate: New/Existing toggle wrapper — the toggle sits above whichever arm is
   active; .inline-create-arm reuses .form-vo (new arm) / entity_ref_picker's own markup
   (existing arm) for its own internal styling, this block only owns the vertical rhythm. */
.inline-create-toggle { display: flex; flex-direction: column; gap: 10px; }
.inline-create-arm.form-vo { margin-top: 0; }

/* VO param card shell (Option C: thin border + shadow) — single and list VO */
.form-vo { border: 1px solid var(--border-strong); border-radius: var(--radius-sm); box-shadow: 0 1px 3px rgb(0 0 0 / 0.06); padding: 0 12px 10px; margin: 0; min-width: 0; }
.form-vo-legend { padding: 0 4px; font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); line-height: 1; }
.form-vo-hint { font-size: 12px; color: var(--text-dim); margin: 4px 0 10px; }
.form-vo-list-item { position: relative; padding: 6px 30px 4px 0; }
.form-vo-list-item + .form-vo-list-item { border-top: 1px solid var(--border-subtle); padding-top: 8px; }
/* Inline × remove button — always visible, top-right of the item */
.btn-vo-remove { position: absolute; top: 8px; right: 0; color: var(--text-muted); }
.btn-vo-remove:hover { background: var(--danger-soft); color: var(--danger); }
/* Compact text-link add button */
.btn-vo-add { display: inline-flex; align-items: center; gap: 4px; margin-top: 8px; padding: 0; background: transparent; border: none; cursor: pointer; font-size: 12px; color: var(--text-muted); transition: color 100ms var(--ease); }
.btn-vo-add:hover { color: var(--accent); }

/* fe-accordion — feed-entity grant tree ----------------------- */
.fe-accordion { display: flex; flex-direction: column; gap: 6px; }
.fe-head { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; }
.fe-feed { border: none; }
.fe-feed-summary { display: flex; align-items: center; gap: 6px; cursor: pointer; font-size: 13px; font-weight: 500; color: var(--text); padding: 5px 4px; border-radius: 6px; list-style: none; }
.fe-feed-summary::-webkit-details-marker { display: none; }
.fe-feed-summary::before { content: "▸"; font-size: 10px; color: var(--text-dim); flex-shrink: 0; transition: transform 120ms var(--ease); }
details[open] > .fe-feed-summary::before { transform: rotate(90deg); }
.fe-feed-summary:hover { background: var(--bg-hover); }
.fe-feed-body { padding: 4px 0 4px 6px; }
.fe-entities { display: flex; flex-direction: column; gap: 2px; padding: 3px 0 3px 16px; border-left: 2px solid var(--border-subtle); margin-left: 8px; }
.fe-grant, .fe-decl, .fe-everything { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 6px; font-size: 12.5px; color: var(--text); cursor: pointer; user-select: none; }
.fe-grant:hover, .fe-decl:hover, .fe-everything:hover { background: var(--bg-hover); }
.fe-grant input[type=checkbox], .fe-decl input[type=checkbox], .fe-everything input[type=checkbox], .fe-grant input[type=radio] { flex-shrink: 0; accent-color: var(--accent); width: 14px; height: 14px; cursor: pointer; }
.fe-everything { margin-bottom: 4px; }
.fe-decl { color: var(--text-muted); font-style: italic; }

/* status-toggle box (used inline in sheets) ------------------- */
.status-box { display: flex; align-items: center; gap: 10px; padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.status-box .status-text { font-size: 13px; }
.status-box .status-detail { font-size: 12px; color: var(--text-dim); }

/* segmented / toggle ----------------------------------------- */
.seg { display: inline-flex; padding: 3px; background: var(--bg-inset); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.seg button { padding: 4px 10px; border: 0; background: transparent; color: var(--text-muted); font-size: 12px; border-radius: calc(var(--radius-sm) - 2px); cursor: pointer; transition: all 120ms var(--ease); }
.seg button.active { background: var(--bg-elev); color: var(--text); box-shadow: 0 1px 2px oklch(0 0 0 / 0.15); }
/* Disabled segments read as disabled everywhere — the try-it response tabs, the pager on a
   first/last page, any future seg. */
.seg button:disabled { opacity: 0.38; cursor: not-allowed; }
.seg a.seg-link { padding: 4px 10px; color: var(--text-muted); font-size: 12px; border-radius: calc(var(--radius-sm) - 2px); transition: all 120ms var(--ease); text-decoration: none; }
.seg a.seg-link:hover { color: var(--text); }
.seg a.seg-link.active { background: var(--bg-elev); color: var(--text); box-shadow: 0 1px 2px oklch(0 0 0 / 0.15); }

.toggle { width: 32px; height: 18px; background: var(--border-strong); border-radius: 999px; position: relative; cursor: pointer; transition: background 160ms var(--ease); flex-shrink: 0; border: 0; padding: 0; }
.toggle::after { content: ''; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; background: white; border-radius: 50%; transition: transform 160ms var(--ease); }
.toggle.on { background: var(--accent); }
.toggle.on::after { transform: translateX(14px); }

/* tables ------------------------------------------------------ */
.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th { text-align: left; padding: 10px 14px; color: var(--text-dim); font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; border-bottom: 1px solid var(--border); background: var(--bg-inset); position: sticky; top: 0; vertical-align: top; }
/* Top-aligned cells: rows with a two-line cell (display-name over subtitle) keep their one-line
   neighbours anchored to the first text line instead of floating mid-row. */
.tbl td { padding: 11px 14px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; }
/* Row hover: a translucent, blurred veil — the animated backdrop shows through the HOVERED row
   only (user verdict 2026-08-05). The mechanism inverts the usual layering: a child can't punch
   a hole through an opaque parent, so the grid CARD is translucent, every row paints its own
   opaque bg-elev by default, and hover swaps that for the veil — the card's translucency then
   surfaces through exactly one row. */
.tbl tr:hover td { background: color-mix(in srgb, var(--bg-hover) 30%, transparent); backdrop-filter: blur(2.5px); -webkit-backdrop-filter: blur(2.5px); }
.card.tbl-wrap { background: color-mix(in srgb, var(--bg-elev) 55%, transparent); }
.card.tbl-wrap .tbl td { background: var(--bg-elev); }
.card.tbl-wrap .tbl tr:hover td { background: color-mix(in srgb, var(--bg-hover) 30%, transparent); }

/* Semantic grid bands — compact ledger dividers, not selected rows or alert slabs.
   Neutral surfaces preserve the table's scan rhythm without decorative rails or rules. Band <th>s
   live in tbody and must not inherit the column header's sticky offset; grouped page grids opt
   into their own offset below. */
.tbl .grid-band th {
  position: static;
  top: auto;
  padding: 0 14px;
  color: var(--text-muted);
  /* Barely-there vertical falloff rather than a flat fill: the band reads as a surface the rows
     sit under, without becoming a stripe that competes with the rows for attention. Both stops
     derive from the same token, so it follows the theme (and degrades to the flat fill it
     replaced wherever color-mix is unsupported). */
  background: linear-gradient(
    var(--bg-elev-2),
    color-mix(in srgb, var(--bg-elev-2) 60%, transparent)
  );
  border-top: 4px solid var(--bg);
  border-bottom: 1px solid var(--border);
  text-transform: none;
  letter-spacing: normal;
  box-shadow: none;
}
/* Grouped grids keep both levels of context in view while the page scrolls: column labels sit
   below the 56px topbar + 44px subnav, and the current group band sits immediately beneath them.
   `overflow: clip` preserves the card's rounded clipping without creating the scroll container
   that prevents viewport-sticky descendants. Scoped through :has() so ordinary/embedded tables
   retain their existing overflow and header behaviour; unsupported browsers degrade to static
   headers rather than changing layout. */
.tbl-wrap:has(.grid-band) {
  --grid-sticky-chrome-height: 100px;
  --grid-sticky-column-height: 37px;
  overflow: clip;
}
.tbl-wrap:has(.grid-band) .tbl > thead th {
  top: var(--grid-sticky-chrome-height);
  z-index: 4;
}
.tbl-wrap:has(.grid-band) .tbl .grid-band th {
  position: sticky;
  top: calc(var(--grid-sticky-chrome-height) + var(--grid-sticky-column-height));
  z-index: 3;
  box-shadow: 0 1px 0 var(--border), 0 8px 14px -14px oklch(0 0 0 / 0.8);
}
.tbl .grid-band--collapsible th { cursor: pointer; }
.tbl .grid-band__inner {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  /* A band separates groups; it needs more air than the rows it separates, or the eye reads the
     whole table as one list. Height here is independent of --grid-sticky-column-height, which
     offsets the band below the sticky COLUMN header (thead), not below itself. */
  padding: 11px 0 12px;
}
.tbl .grid-band__toggle {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 22px;
  flex: 0 0 18px;
  padding: 0;
  color: var(--text-dim);
  background: transparent;
  border: 0;
  border-radius: 3px;
  cursor: pointer;
}
.tbl .grid-band--collapsible:hover .grid-band__value { color: var(--accent); }
.tbl .grid-band__toggle:focus-visible {
  outline-offset: 3px;
}
.tbl .grid-band__chevron {
  width: 6px;
  height: 6px;
  flex: 0 0 6px;
  border-right: 1.5px solid var(--text-dim);
  border-bottom: 1.5px solid var(--text-dim);
  transform: rotate(45deg) translate(-1px, -1px);
  transform-origin: center;
  transition: transform 140ms var(--ease), border-color 140ms var(--ease);
}
.tbl .grid-band--collapsible:hover .grid-band__chevron {
  border-color: var(--accent);
}
.tbl .grid-band__toggle[aria-expanded="false"] .grid-band__chevron {
  transform: rotate(-45deg);
}
.tbl .grid-band__select {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Centers the group checkbox over the child rows' 36px bulk column (.epc-bulk-cell
     centers its 13px input in the td) so all checkboxes form one straight column — the
     margin absorbs the centering slack the td has and the band's flex row hasn't. */
  padding: 4px;
  margin-left: 4px;
  height: 22px;
  flex: 0 0 auto;
  cursor: pointer;
}
.tbl .grid-band__select input {
  width: 13px;
  height: 13px;
  margin: 0;
  cursor: pointer;
  accent-color: var(--accent);
}
.tbl .grid-band__label {
  flex: 0 0 auto;
  color: var(--text-dim);
  font-size: 9px;
  font-weight: 650;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.tbl .grid-band__value {
  /* Pulls the feed name onto the same left edge as the column header and the nested rows beneath
     it — all three read as one column now. Taken off the name rather than the band's own inset,
     because the caret's position is load-bearing: the nested rule is aligned to its centre. */
  margin-left: -4px;
  min-width: 0;
  color: var(--text);
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: none;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color 140ms var(--ease);
}
.tbl .grid-band__value .stack-h { gap: 1px; }
/* The relative-time line is client-filled after the row lands in the DOM; an empty span has no
   line box, so a late fill grows every datetime row at once and shifts the whole grid under the
   cursor (the Product-mapper "screen jumps on paging" find). Reserve the line up front — but only
   where a fill is actually coming (data-ts present); null-timestamp cells stay single-line. */
.epc-rel-time[data-ts]:empty::before { content: "\00a0"; }
.tbl .grid-band__summaries {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  margin-left: 5px;
}
.tbl .grid-band__summary {
  color: var(--text-dim);
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 500;
  white-space: nowrap;
}
.tbl .grid-band__summary .num {
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.tbl .grid-band__summary .chip {
  padding: 1px 6px;
  font-size: 9px;
  line-height: 15px;
}
/* "3 selected" — per-group selection count stamped by epcSyncBandSelectionCounts; accent so it
   reads as live selection state, not another data count. */
.tbl .grid-band__selected { font-size: 11px; white-space: nowrap; }
.epc-bulk-count:not(:empty) { margin-left: 5px; opacity: .75; }
.tbl .grid-band__count {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  margin-left: auto;
  padding-left: 16px;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  white-space: nowrap;
}
.tbl .grid-band__count-label {
  color: var(--text-dim);
  font-family: var(--font-body);
  font-size: 10px;
}
.tbl .grid-band__actions {
  position: relative;
  flex: 0 0 auto;
}
.tbl .grid-band__actions > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 24px;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 1px;
  background: transparent;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
  list-style: none;
}
.tbl .grid-band__actions > summary::-webkit-details-marker { display: none; }
.tbl .grid-band__actions > summary:hover,
.tbl .grid-band__actions[open] > summary {
  color: var(--text);
  background: var(--bg-hover);
}
.tbl .grid-band__actions > summary:focus-visible { outline-offset: 2px; }
.tbl .grid-band__action-menu {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  right: 0;
  display: flex;
  flex-direction: column;
  min-width: 170px;
  padding: 5px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 7px;
  box-shadow: var(--shadow-modal);
}
.tbl .grid-band__action-menu button {
  width: 100%;
  padding: 7px 9px;
  color: var(--text-muted);
  font: 500 11px/1.25 var(--font-body);
  text-align: left;
  background: transparent;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
}
.tbl .grid-band__action-menu button:hover,
.tbl .grid-band__action-menu button:focus-visible {
  color: var(--text);
  background: var(--bg-hover);
}
.tbl .grid-band-group--loading .grid-band__value,
.tbl .grid-band-group--loading .grid-band__count,
.tbl .grid-band-group--loading .grid-band__summaries {
  opacity: 0.55;
}
.tbl .grid-band-group--loading .grid-band__chevron {
  animation: grid-band-loading 700ms ease-in-out infinite alternate;
}
.tbl .grid-band__pager td {
  padding: 7px 14px 8px;
  background: var(--bg-inset);
  border-bottom: 1px solid var(--border);
}
.tbl .grid-band__pager:hover td { background: var(--bg-inset); }
.tbl .grid-band__pager-inner,
.tbl .grid-band__pager-actions {
  display: flex;
  align-items: center;
}
.tbl .grid-band__pager-inner {
  justify-content: space-between;
  gap: 16px;
}
.tbl .grid-band__pager-label {
  color: var(--text-dim);
  font-size: 10px;
  letter-spacing: 0.04em;
}
.tbl .grid-band__pager-actions { gap: 12px; }
.tbl .grid-band__pager-button {
  appearance: none;
  padding: 0;
  color: var(--text-muted);
  background: transparent;
  border: 0;
  font: inherit;
  font-size: 11px;
  cursor: pointer;
}
.tbl .grid-band__pager-button:hover:not(:disabled) { color: var(--accent); }
.tbl .grid-band__pager-button:disabled {
  color: var(--text-dim);
  cursor: default;
  opacity: 0.45;
}
.tbl tbody > tr[hidden] { display: none; }
@keyframes grid-band-loading {
  from { opacity: 0.35; }
  to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .tbl .grid-band__chevron,
  .tbl .grid-band__value { transition: none; }
  .tbl .grid-band-group--loading .grid-band__chevron { animation: none; }
}
@media (max-width: 860px) {
  .tbl .grid-band__summaries { display: none; }
  .tbl .grid-band__count-label { display: none; }
}

/* Parameter table (API Docs) — each parameter is two rows: a main row with the
   name/in/type/required/value columns, and a description row that spans the full width
   underneath it. The row divider sits below the description so the pair reads as one unit. */
.param-tbl tr.param-main td { border-bottom: 0; padding-bottom: 5px; vertical-align: middle; }
.param-tbl tr.param-desc td { border-bottom: 1px solid var(--border-subtle); padding-top: 0; padding-bottom: 12px; color: var(--text-dim); font-size: 12px; line-height: 1.45; }
.param-tbl tr.param-desc:last-child td { border-bottom: 0; }
.param-tbl tr:hover td { background: transparent; }
/* Required parameters carry a bold marker inline in the description line (the dedicated
   Required column was removed); optional parameters simply omit it. Muted tone — the bold
   weight carries the emphasis without an alarm-red. */
.param-tbl .req-flag { font-weight: 700; color: var(--text-muted); }
/* Keep the type cell (e.g. "integer (int32)") on a single line. */
.param-tbl tr.param-main td:nth-child(3) { white-space: nowrap; }
/* Fixed height for the Parameters card-head so it is identical with or without the Try-it
   "Reset" button — otherwise flipping Explore <-> Try it shifts the whole table vertically.
   Tracks the density row token (+6px clears the Reset button) rather than a hardcoded pixel. */
/* Grows with its content and keeps symmetric breathing room around the primary Send button.
   Compound selector: a later plain .card-head rule (12px 16px 4px) would otherwise win the
   cascade and pinch the bottom padding to 4px. */
.card-head.param-head { min-height: calc(var(--d-row) + 6px); padding: 12px 16px; }
.tbl tr.row-link { cursor: pointer; }
.tbl tr:last-child td { border-bottom: 0; }
/* The Access tab's entity-cardinality popover: the "x/N" summary expands a small floating card
   with the covered / not-covered breakdown. details/summary = zero JS, keyboard-accessible. */
.net-ents { position: relative; display: inline-block; }
.net-ents summary { cursor: pointer; list-style: none; color: var(--accent); text-decoration: underline dotted; }
.net-ents summary::-webkit-details-marker { display: none; }
.net-ents[open] summary { color: var(--text); }
.net-ents-pop { position: absolute; left: 0; top: calc(100% + 6px); z-index: 60; min-width: 260px; max-width: 420px; padding: 10px 12px; background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow-card); white-space: normal; }

/* The Net access pivot: axis pickers + the traffic-light cells. Green = exercisable and nothing
   in the folded dimensions constrains the combo; yellow = exercisable but partial; ✕ (warn) =
   granted on paper but no granted protocol can carry the operation. */
/* Both pickers dense on ONE line: Rows [Feed|Operation|Protocol]  Columns [Operation|Protocol]. */
.pv-axes { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin: 4px 0 14px; }
.pv-axes .seg-radio a { text-decoration: none; }
.pv-axes .seg-radio { gap: 2px; padding: 2px; border-radius: 7px; background: var(--bg); border: 1px solid var(--border); }
.pv-axes .seg-opt { height: 22px; padding: 0 10px; border-radius: 5px; font-size: 11.5px; }
.pv-axes .seg-opt.active { background: var(--bg-elev-2); box-shadow: inset 0 0 0 1px var(--border-strong); }
/* Pivot cell values render as plain colored mono text (the popover affordance is the cursor). */
.net-ents summary.pv-green, .net-ents summary.pv-yellow { text-decoration: none; font-family: var(--font-mono); font-size: 12px; }
.net-ents summary.pv-green { color: var(--success); }
.net-ents summary.pv-yellow { color: var(--warn); }
.net-ents summary.pv-x { color: var(--warn); text-decoration: none; }
/* The pivot table sits in its own rounded frame; the header band drops to the page tone. */
/* overflow must stay visible so a cell's <details> .net-ents-pop popover can escape the frame
   (it's an absolutely-positioned dropdown; overflow:hidden here clipped it — the corners still
   read rounded since the header bg matches the page). */
.pivot-frame { border: 1px solid var(--border); border-radius: 8px; overflow: visible; }
.pivot-frame .tbl th { background: var(--bg); }
.pivot-frame .tbl tr:last-child td { border-bottom: 0; }

.tbl th.num, .tbl td.num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl th.actions, .tbl td.actions { text-align: right; white-space: nowrap; }
.tbl td.actions .btn + .btn { margin-left: 6px; }
.tbl-wrap { overflow: hidden; }
/* Grid, not flex space-between: the nav links must stay put across page navigations even though
   the count label changes width ("N rows · first page" vs "N rows") — with space-between the
   centered middle child shifts by half the label delta, sliding the Next button out from under
   the cursor mid-paging. 1fr/auto/1fr pins the links to the true center of the bar. */
.tbl-pager { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px; padding: 10px 14px; border-top: 1px solid var(--border); background: var(--bg-inset); font-size: 12px; color: var(--text-muted); }
.tbl-pager .links { display: inline-flex; gap: 4px; }
.tbl-pager .links a { padding: 2px 8px; border-radius: var(--radius-sm); color: var(--text-muted); }
.tbl-pager .links a:hover { background: var(--bg-hover); color: var(--text); }
.tbl-pager .links a[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }
/* T3#20 follow-up: .input's form-field full-width convention balloons the page-size select across
   the whole row, crowding the count/nav controls — pin it to its content width, right-aligned in
   its grid column (grid items otherwise stretch to the column width). */
.tbl-pager .pager-page-size { width: auto; justify-self: end; min-width: 0; padding-right: 26px; }
.tbl th.sortable { cursor: pointer; user-select: none; }
.tbl th.sortable:hover { color: var(--text-muted); }
.tbl th.sortable .sort-arrow { display: inline-block; margin-left: 4px; opacity: 0.5; font-size: 9px; }
.tbl th.sortable.sorted-asc .sort-arrow, .tbl th.sortable.sorted-desc .sort-arrow { opacity: 1; color: var(--accent); }

/* layout ------------------------------------------------------ */
.app-shell { min-height: 100vh; display: flex; flex-direction: column; position: relative; z-index: 1; }
.page { max-width: var(--shell-max); width: 100%; margin: 0 auto; padding: 24px 28px 48px; }

@keyframes epc-fadein { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* data-show visibility helper (Datastar) --------------------- */
[data-show="false"], [data-hidden] { display: none !important; }
.epc-hidden { display: none !important; }

/* Theme-toggle icons: pick the right one before JS runs to avoid a flash. */
[data-theme="light"] #epc-moon-icon { display: none; }
[data-theme="dark"] #epc-sun-icon { display: none; }

/* sparkline + health strip ----------------------------------- */
.sparkline { display: block; overflow: visible; max-width: 100%; }
/* Fluid sparklines where the svg fills a block container (stat cards, the events strip): the
   viewBox + preserveAspectRatio=none stretch horizontally to the card, so narrow windows shrink
   the chart instead of overflowing. Inline/sized usages (feed rows) keep their width attribute. */
.stat-card-spark .sparkline, .events-spark .sparkline { width: 100%; }
.digest-chart { overflow: hidden; }
.digest-chart-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 16px 10px; border-bottom: 1px solid var(--border-subtle); }
.digest-chart-controls { flex-wrap: wrap; justify-content: flex-end; }
.digest-chart-controls .input { block-size: 32px; min-inline-size: 130px; padding: 0 28px 0 10px; font-size: 12px; }
/* isolation traps the stage's internal z-layers (empty pill 2, scrim 3, tooltip 5) in their own
   stacking context — without it they escape into the app-shell arena and paint over page-local
   modals/backdrops (observed: the empty pill floating above the MCP preview modal). */
.digest-chart-stage { position: relative; block-size: 260px; min-inline-size: 0; isolation: isolate; }
.digest-chart-stage::after { content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none; opacity: 0; background: color-mix(in oklch, var(--bg-elev) 72%, transparent); backdrop-filter: blur(2px); transition: opacity var(--ease); }
.digest-chart.is-loading .digest-chart-stage::after { opacity: 1; }
.digest-chart-svg { display: block; overflow: visible; }
.digest-chart-threshold { stroke: var(--warn, #e0a83a); stroke-width: 1.5; stroke-dasharray: 6 4; }
.digest-chart-threshold-label { fill: var(--warn, #e0a83a); font-family: var(--font-mono); font-size: 10px; }
.digest-chart-axis { color: var(--text-dim); font-family: var(--font-mono); font-size: 10px; }
.digest-chart-axis .domain { stroke: var(--border-strong); }
.digest-chart-axis .tick line { stroke: var(--border); }
.digest-chart-grid { color: var(--border-subtle); }
.digest-chart-grid .domain { display: none; }
.digest-chart-grid .tick line { stroke: currentColor; }
.digest-chart-bars .bar { transition: filter 120ms ease, opacity 120ms ease; }
.digest-chart-bars .bar[data-accent="true"] { filter: brightness(1.15); }
.digest-chart-bars .bar[data-selected="true"] { filter: brightness(1.2); stroke: var(--accent); stroke-width: 2; }
.digest-chart-hitboxes rect { cursor: pointer; outline: none; }
.digest-chart-hitboxes rect:focus { stroke: var(--accent); stroke-width: 1; }
.digest-chart-tooltip { position: absolute; inset: 0 auto auto 0; z-index: 5; display: flex; flex-direction: column; gap: 2px; min-inline-size: max-content; padding: 7px 9px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--bg-elev-2); box-shadow: var(--shadow-card); pointer-events: none; font-size: 11px; }
.digest-chart-tooltip[hidden] { display: none; }
.digest-chart-tooltip .label { color: var(--text-muted); }
.digest-chart-tooltip .value { color: var(--text); font-family: var(--font-mono); font-weight: 600; }
.digest-chart-empty { position: absolute; inset: 50% auto auto 50%; z-index: 2; translate: -50% -50%; padding: 6px 9px; border-radius: var(--radius-sm); background: color-mix(in oklch, var(--bg-elev) 88%, transparent); color: var(--text-dim); font-size: 12px; pointer-events: none; }
.digest-chart-empty[hidden] { display: none; }
.digest-chart-foot { min-block-size: 30px; padding: 7px 16px; border-top: 1px solid var(--border-subtle); text-align: end; }
@media (max-width: 680px) {
  .digest-chart-head { align-items: flex-start; flex-direction: column; }
  .digest-chart-controls { inline-size: 100%; justify-content: stretch; }
  .digest-chart-controls label { flex: 1; }
  .digest-chart-controls .input { inline-size: 100%; min-inline-size: 0; }
  .digest-chart-stage { block-size: 220px; }
}
.health-strip { display: flex; gap: 2px; height: 16px; align-items: stretch; }
.health-strip > span, .health-cell { flex: 1; min-width: 3px; border-radius: 1px; opacity: 0.85; }
.health-strip > span.idle, .health-cell.idle { opacity: 0.4; background: var(--border-strong); }
.health-strip > span.ok, .health-cell.ok { background: var(--success); }
.health-strip > span.warn, .health-cell.warn { background: var(--warn); }
.health-strip > span.err, .health-cell.err { background: var(--danger); }

/* ---------- impersonation banner -------------------------------- */
.imp-banner { display: flex; align-items: center; gap: 10px; padding: 0 28px; height: 36px; background: color-mix(in oklch, #f59e0b 12%, var(--bg)); border-bottom: 1px solid color-mix(in oklch, #f59e0b 35%, var(--border)); font-size: 12px; font-weight: 500; color: var(--text); }
.imp-banner-icon { color: #d97706; flex-shrink: 0; }
.imp-banner-text { flex: 1; }
.imp-banner-actor { color: var(--text-muted); margin-left: 6px; }
.imp-banner-exit { margin-left: auto; flex-shrink: 0; }
/* Tier-1 #5: countdown states layered on the base banner above — warn from T-5min, expired
   (terminal, red) at T-0. .imp-banner-countdown is empty/invisible until epc-helpers.js's
   countdown IIFE starts writing to it (only once inside the warn window). */
.imp-banner-countdown { margin-left: 8px; font-weight: 600; }
.imp-banner-warn { background: color-mix(in oklch, #f59e0b 24%, var(--bg)); border-bottom-color: color-mix(in oklch, #f59e0b 55%, var(--border)); }
.imp-banner-expired { background: var(--danger-soft); border-bottom-color: var(--danger); color: var(--danger); }
.imp-banner-expired .imp-banner-icon, .imp-banner-expired .imp-banner-actor { color: var(--danger); }

/* ---------- top bar ----------------------------------------- */
.topbar { position: sticky; top: 0; z-index: 20; background: var(--bg); border-bottom: 1px solid var(--border); }
.topbar .inner { max-width: var(--shell-max); margin: 0 auto; padding: 0 28px; height: 56px; display: flex; align-items: center; gap: 24px; }
.topbar .brand { display: flex; align-items: center; gap: 10px; color: inherit; }
.topbar .brand-name { font-weight: 600; font-size: 14px; letter-spacing: -0.01em; }
.topbar .brand-sub { color: var(--text-muted); font-weight: 400; }
.topbar .brand-version { margin-left: 8px; font-size: 10px; height: 18px; padding: 0 6px; }
.topbar .topnav { display: flex; gap: 4px; margin-left: 8px; }
.topbar .topnav .tab { height: 34px; padding: 0 12px; display: inline-flex; align-items: center; color: var(--text-muted); background: transparent; border: 0; border-radius: var(--radius-sm); font-size: 13px; font-weight: 500; cursor: pointer; }
.topbar .topnav .tab:hover { background: var(--bg-hover); }
.topbar .topnav .tab.active { color: var(--text); font-weight: 600; background: var(--bg-hover); }
.topbar .spacer { flex: 1; }
.topbar-actions { display: flex; align-items: center; gap: 8px; }
.topbar-search { height: 32px; padding: 0 10px; font-size: 12px; color: var(--text-muted); min-width: 220px; justify-content: space-between; background: var(--bg-inset); border: 1px solid var(--border); border-radius: var(--radius-sm); display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.topbar-search:hover { background: var(--bg-hover); }
.topbar-divider { width: 1px; height: 22px; background: var(--border); }
.topbar-user { display: flex; align-items: center; gap: 10px; }
.topbar-user .avatar { width: 28px; height: 28px; border-radius: 50%; background: linear-gradient(135deg, var(--accent) 0%, color-mix(in oklch, var(--accent), black 30%) 100%); color: var(--accent-ink); display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 11px; }
.topbar-user .name { font-weight: 500; font-size: 12px; line-height: 1.2; }
.topbar-user .role { color: var(--text-dim); font-size: 11px; }

.topbar-user-menu { position: relative; }
.topbar-user-menu > summary { list-style: none; cursor: pointer; }
.topbar-user-menu > summary::-webkit-details-marker { display: none; }
.topbar-user-menu > summary::marker { content: ''; }
.topbar-user-menu > summary:hover { background: var(--bg-hover); border-radius: var(--radius-sm); }
.topbar-user-menu .topbar-user { padding: 4px 6px; }
.topbar-user-dropdown { position: absolute; top: calc(100% + 6px); right: 0; min-width: 180px; padding: 6px; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow-modal); z-index: 30; }
.topbar-user-dropdown .btn { width: 100%; justify-content: flex-start; }

/* ---------- sub nav ----------------------------------------- */
.subnav { border-bottom: 1px solid var(--border); background: var(--bg); position: sticky; top: 56px; z-index: 19; }
.subnav .inner { max-width: var(--shell-max); margin: 0 auto; padding: 0 28px; display: flex; gap: 2px; height: 44px; align-items: center; }
/* Pill tabs: the active entry keeps the hover surface (bg-hover) as a persistent pill — same
   affordance as the topbar's primary nav. No underline; that style stays with the view-switcher. */
.subnav .subtab, .subnav a.tab { height: 28px; padding: 0 12px; font-size: 13px; color: var(--text-muted); font-weight: 500; background: transparent; border: 0; border-radius: var(--radius-sm); display: inline-flex; align-items: center; gap: 6px; text-decoration: none; cursor: pointer; }
.subnav .subtab:hover, .subnav a.tab:hover { color: var(--text); background: var(--bg-hover); }
.subnav .subtab.active, .subnav a.tab.active { color: var(--text); font-weight: 600; background: var(--bg-hover); }
.subnav .subtab-divider { display: inline-flex; align-items: center; }
/* View selector + detail-view chooser: underlined tabs (active tab underlined in the accent). */
.view-switcher, .detail-chooser { display: inline-flex; align-items: center; gap: 2px; border-bottom: 1px solid var(--border); }
.view-switcher, .detail-chooser { margin: 0; }
.view-switcher .subtab, .detail-chooser .subtab { height: 32px; padding: 0 12px; color: var(--text-muted); font-family: inherit; font-size: 13px; font-weight: 500; text-decoration: none; display: inline-flex; align-items: center; gap: 6px; cursor: pointer; appearance: none; background: transparent; border: 0; border-bottom: 2px solid transparent; border-radius: 0; margin-bottom: -1px; }
.view-switcher .subtab:hover, .detail-chooser .subtab:hover { color: var(--text); }
.view-switcher .subtab.active, .detail-chooser .subtab.active { color: var(--text); font-weight: 600; border-bottom-color: var(--accent); }
.subnav .subtab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 16px; padding: 0 5px; background: var(--bg-inset); color: var(--text-muted); font-size: 10px; font-weight: 600; border-radius: 4px; font-family: var(--font-mono); }
.subnav .subtab.active .subtab-badge { background: var(--accent); color: var(--accent-ink); }

/* ---------- page head --------------------------------------- */
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 24px; flex-wrap: wrap; }
.page-head h1 { font-size: 19px; font-weight: 650; letter-spacing: -0.01em; }
.page-head .text-2 { margin-top: 4px; }
.page-head-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* Page-level purpose banner (UX guidance study item 3) — sits between the page-head band and
   the view switcher/grid. */
.page-shell > .review-sheet-guidance { margin: -12px 0 20px; }

/* generic page-shell: the standard "fade-in column with gap" wrapper -- */
.page-shell { display: flex; flex-direction: column; gap: 18px; }
/* Inside the shell, the flex gap is the single source of vertical rhythm — the components'
   own bottom margins would stack on top of it (e.g. page-head 24px + gap 18px = 42px voids). */
.page-shell > .page-head { margin-bottom: 0; }
.page-shell > .param-bar { margin-bottom: 0; }
.page-shell > .grid-search { margin-bottom: 0; }
.page-shell > .bulk-bar { margin-bottom: 0; }

/* toolbar: filters/search row, typically between page-head and a table - */
.toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.toolbar-left, .toolbar-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.section-head { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-bottom: 10px; }
.section-head .section-title { display: flex; align-items: center; gap: 10px; }
.section-head h2 { font-size: 14px; }
.section-filters { display: flex; align-items: center; gap: 8px; }

.chip-group { display: inline-flex; gap: 6px; }

/* ---------- stat cards / stat row --------------------------- */
/* minmax(0,1fr) (not bare 1fr) so columns may shrink BELOW their content's min width — the
   fixed-width sparkline SVGs inside would otherwise set a min-content floor that overflows and
   right-clips the whole layout on narrow windows. */
.stat-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--d-gap); margin-bottom: 24px; }
.stat-card { padding: var(--d-card-pad); background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--d-radius); box-shadow: var(--shadow-card); text-align: left; width: 100%; cursor: default; transition: all 160ms var(--ease); display: block; }
button.stat-card { cursor: pointer; }
.stat-card:hover { border-color: var(--border-strong); }
.stat-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.stat-card .delta { font-size: 11px; font-weight: 600; letter-spacing: 0.04em; color: var(--success); font-family: var(--font-mono); }
.stat-card .delta.down { color: var(--danger); }
.stat-card .num { font-size: 30px; font-weight: 600; letter-spacing: -0.02em; margin-top: 6px; font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text); }
.stat-card.tone-warn .num { color: var(--warn); }
.stat-card.tone-err  .num { color: var(--danger); }
.stat-card.tone-ok   .num { color: var(--success); }
.stat-card-spark { margin-top: 10px; }

/* ---------- events strip ------------------------------------ */
.events-strip { padding: var(--d-card-pad); display: flex; align-items: center; gap: 24px; margin-bottom: 24px; }
.events-strip .num.h1 { font-size: 26px; color: var(--accent); }
.events-spark { flex: 1; }
.events-spark .tick-row { display: flex; justify-content: space-between; margin-top: 6px; font-size: 10px; color: var(--text-dim); font-family: var(--font-mono); }
.events-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.events-meta > div { display: flex; gap: 8px; align-items: center; font-size: 11px; color: var(--text-muted); }

/* ---------- attention list --------------------------------- */
.attention { margin-bottom: 24px; }
.card-head { padding: 12px 16px 4px; display: flex; align-items: center; gap: 10px; }
.attention-list { list-style: none; margin: 0; padding: 0; }
.attention-row { display: flex; align-items: center; gap: 14px; padding: 12px 16px; border-bottom: 1px solid var(--border-subtle); }
.attention-row:last-child { border-bottom: 0; }
.attention-icon { width: 28px; height: 28px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; background: var(--accent-soft); color: var(--accent); }
.attention-row.warn .attention-icon { background: var(--warn-soft); color: var(--warn); }
.attention-row.err  .attention-icon { background: var(--danger-soft); color: var(--danger); }
.attention-row.ok   .attention-icon { background: var(--success-soft); color: var(--success); }
.attention-main { flex: 1; min-width: 0; }
.attention-title { font-size: 13px; font-weight: 500; color: var(--text); }
.attention-meta { display: flex; gap: 10px; align-items: center; }

/* ---------- feed grid / feed card -------------------------- */
.feed-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--d-gap); }
.feed-card { padding: var(--d-card-pad); cursor: pointer; display: block; position: relative; color: inherit; min-width: 0; }
.feed-card:hover { border-color: var(--border-strong); }
.feed-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 10px; min-width: 0; }
.feed-card-title { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
.feed-card .feed-name { font-family: var(--font-mono); font-size: 13px; font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.feed-card .feed-desc { font-size: 12px; color: var(--text-muted); margin-top: 4px; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; max-width: 100%; }
.feed-card .provider { font-family: var(--font-mono); font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; }
.feed-card-health { display: flex; gap: 2px; height: 16px; align-items: stretch; margin-bottom: 10px; }
.feed-card-stats { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin: 0; font-size: 11px; color: var(--text-muted); }
.feed-card-stats dl { margin: 0; }
.feed-card-stats > div { display: grid; gap: 2px; }
.feed-card-stats dt { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600; margin: 0; }
.feed-card-stats dd { margin: 0; font-size: 12px; color: var(--text); }
.feed-card-foot { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.feed-card-foot .chip-list { display: flex; gap: 4px; flex-wrap: wrap; }
.feed-card-foot .metrics { display: flex; gap: 14px; align-items: center; font-size: 11px; color: var(--text-muted); }
.feed-card-health-row { margin: 10px 0; }
.throughput-ok   { color: var(--success); }
.throughput-warn { color: var(--warn); }
.throughput-err  { color: var(--danger); }
.throughput-idle { color: var(--text-dim); }

.stat-row-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.section-row { margin-top: 24px; }
.audit-search-input { width: 320px; }

/* limit usage bar tones (pair with .limit-bar-fill width inline) */
.limit-bar-fill.tone-ok   { background: var(--success); }
.limit-bar-fill.tone-warn { background: var(--warn); }
.limit-bar-fill.tone-err  { background: var(--danger); }
.limit-fraction { margin-left: auto; }

.review-callout { padding: 12px; background: var(--bg-inset); border-radius: var(--radius-sm); border-left: 2px solid var(--warn); }
.review-actions { border-top: 1px solid var(--border); padding-top: 14px; display: flex; justify-content: flex-end; gap: 8px; }

/* ---------- tweaks panel ----------------------------------- */
.tweaks { position: fixed; right: 20px; bottom: 20px; width: 320px; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: 0 20px 60px -20px oklch(0 0 0 / 0.5); z-index: 100; padding: 14px 18px; animation: epc-fadein 180ms var(--ease); }
.tweaks-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.tweaks-head .h3 { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.tweaks-body { display: flex; flex-direction: column; }
.tweak { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border-subtle); gap: 12px; }
.tweak:last-child { border-bottom: 0; }
.tweak > .cap { font-size: 13px; color: var(--text-muted); letter-spacing: 0; text-transform: none; font-weight: 500; }
.tweak .cap-row { display: flex; align-items: center; gap: 8px; }
.tweak .row { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-muted); cursor: pointer; }
.tweak .hue-slider { width: 150px; }
.swatch { width: 22px; height: 22px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; padding: 0; }
.swatch.active { border-color: var(--text); }
.swatch-row { display: flex; gap: 6px; }

/* ---------- command palette -------------------------------- */
.palette-backdrop { position: fixed; inset: 0; background: oklch(0 0 0 / 0.45); z-index: 80; animation: epc-fadein 120ms var(--ease); }
.palette { position: fixed; top: 15vh; left: 50%; transform: translateX(-50%); width: 680px; max-width: calc(100vw - 40px); background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; box-shadow: 0 30px 80px -20px oklch(0 0 0 / 0.6); z-index: 81; animation: epc-opacity 180ms var(--ease); }
.palette-input-row { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
.palette-input { flex: 1; background: transparent; border: 0; outline: none; color: var(--text); font-size: 14px; }
/* Scope pills (task #45): plain .chip (dim = the page's ambient default scope) or .chip.accent
   (a scope the operator narrowed via `in:`) — the pills ride the chip family's tokens/themes
   instead of forking them. Only the container sizing is palette-specific. */
.palette-scopes { display: inline-flex; gap: 4px; flex: 0 1 auto; min-width: 0; max-width: 45%; overflow: hidden; }
.palette-scopes:empty { display: none; }
.palette-scopes .chip { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette-results { max-height: 400px; overflow: auto; padding: 6px; margin: 0; list-style: none; }
.palette-group { padding: 10px 12px 4px; font-size: 10px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }
.palette-item-row { list-style: none; padding: 0; margin: 0; position: relative; }
.palette-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 13px; color: var(--text); cursor: pointer; text-decoration: none; }
.palette-item-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.palette-item-main { display: inline-flex; align-items: center; gap: 10px; }
.palette-item-main svg { color: var(--text-muted); }
.palette-item-detail { margin-left: 25px; color: var(--text-muted); font-size: 11px; line-height: 1.35; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.palette-item-row.is-active > .palette-item,
.palette-item:hover { background: var(--bg-elev-2); }
.palette-item-row.is-active > .palette-item .palette-item-main svg { color: var(--accent); }
/* Accent strip is a separate sibling so the row's rounded background can't
   round the strip's corners. Sits flush against the palette gutter. */
.palette-item-row.is-active::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: var(--accent); }
.palette-item .section-hint { flex: 0 0 auto; max-width: 42%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.06em; }
.palette-empty { padding: 24px; text-align: center; color: var(--text-muted); font-size: 13px; }
.palette-footer { display: flex; gap: 16px; padding: 8px 16px; border-top: 1px solid var(--border); background: var(--bg-elev-2); font-size: 11px; color: var(--text-dim); }
.palette-footer kbd { font-family: var(--font-mono); font-size: 10px; padding: 1px 4px; border: 1px solid var(--border); border-radius: 3px; background: var(--bg-elev); margin-right: 2px; color: var(--text-muted); }

/* Search-result deep links land on the exact schema field and leave enough room above the row for
   the sticky console chrome. The target wash makes the jump legible in both table and tree views. */
.schema-field-target { scroll-margin-top: 110px; }
.schema-field-target:target,
.schema-field-target:target > * { background: color-mix(in oklch, var(--accent) 14%, transparent); }

/* ---------- side sheet ------------------------------------- */
.sheet-backdrop { position: fixed; inset: 0; background: oklch(0 0 0 / 0.4); z-index: 60; animation: epc-fadein 180ms var(--ease); }
/* The op dialog. Historically a right-hand drawer with a "modal" flavor variant; the drawer
   was retired 2026-08-24 (UX-16) — every op now opens centered, from the first frame, so the
   old skeleton-opens-as-drawer-then-morphs flash cannot happen. */
.sheet {
  position: fixed; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 520px; max-width: calc(100vw - 40px); max-height: calc(100vh - 80px);
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 30px 80px -20px oklch(0 0 0 / 0.6);
  display: flex; flex-direction: column; z-index: 61;
  animation: epc-modal-pop 180ms var(--ease);
}
.sheet-head { padding: 18px 22px; border-bottom: 1px solid var(--border); display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
/* Op description block inside the form body — same layout as .sheet-head but no horizontal
   padding (already provided by .sheet-body/.modal-body), so op name aligns with field labels. */
/* UX-15 header shape: step train (wizards) or one op line (single-step), then a ruled
   description band. Replaces the old squeezed two-column .op-head. */
.op-head-line { padding-bottom: 14px; border-bottom: 1px solid var(--border); }
.op-line { display: flex; gap: 8px; align-items: baseline; font-size: 14px; }
.op-line-op { font-weight: 600; }
.op-line-sep { color: var(--text-dim); }
.op-line-noun { color: var(--text-muted); }
.op-train { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.op-train-step { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; }
.op-train-dot { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--border-strong);
  display: inline-flex; align-items: center; justify-content: center; font-size: 10px;
  font-style: normal; color: var(--bg); flex: none; }
.op-train-step.current .op-train-dot { border-color: var(--accent);
  background: radial-gradient(circle at center, var(--accent) 0 4px, transparent 5px); }
.op-train-step.done .op-train-dot { border-color: var(--accent); background: var(--accent); }
.op-train-step.current .op-train-name { color: var(--text); font-weight: 600; }
.op-train-step.done .op-train-name { color: var(--text-muted); }
.op-train-step.todo { color: var(--text-dim); }
.op-train-link { flex: none; width: 26px; height: 1px; background: var(--border-strong); }
.op-desc { padding: 12px 0 14px; border-bottom: 1px solid var(--border); }
.op-desc .text-2 p { margin: 0 0 6px; }
.op-desc .text-2 p:last-child { margin-bottom: 0; }
/* op-level guidance: banner (UX guidance study item 1) — reuses reviewFragment.jte's guidance
   class outside a .review-sheet ancestor, so restate the spacing here. */
.op-desc .review-sheet-guidance { margin: 0 0 6px; }
.op-preview { padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-inset); display: flex; flex-direction: column; gap: 12px; }
.op-preview-title { font-size: 12px; font-weight: 600; color: var(--text); text-transform: uppercase; letter-spacing: 0.06em; }
.op-preview-section { display: flex; flex-direction: column; gap: 6px; }
.op-preview-section-title { font-size: 12px; color: var(--text-dim); }
.op-preview-row { display: grid; grid-template-columns: minmax(120px, 0.36fr) minmax(0, 1fr); gap: 10px; align-items: baseline; font-size: 13px; }
.op-preview-label { color: var(--text-dim); }
.op-preview-value { color: var(--text); overflow-wrap: anywhere; }
.op-preview-warnings { margin: 0; padding-left: 18px; color: var(--warn); font-size: 12px; line-height: 1.45; }
.op-preview-warnings li + li { margin-top: 4px; }
.sheet-head h2 { font-size: 16px; }
.sheet-head .subtitle { color: var(--text-muted); font-size: 13px; margin-top: 2px; }
.sheet-body { flex: 1; overflow: auto; padding: 22px; }
.sheet-foot { padding: 14px 22px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; }
.sheet-form { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.sheet-form .sheet-body { flex: 1; overflow: auto; }

@keyframes epc-modal-pop {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.97); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* ---------- Customer Explorer (UX-19) ---------- */
.explorer-picker { min-width: 320px; }
/* The strict-cascade subject pickers sit in one row, account → principal → credential. */
#explorer-pickers { display: flex; gap: 12px; min-width: 0; }
#explorer-pickers .form-field { min-width: 200px; margin: 0; }
/* One row for all six controls (drive feedback 2026-08-24): subject cascade, then a hairline
   gap, then the data filters — the gap alone marks the two groups, no label. */
.explorer-controls { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; margin-top: 14px; }
.explorer-filters { display: flex; gap: 12px; align-items: flex-end;
  padding-left: 12px; border-left: 1px solid var(--border); }
.explorer-filters .form-field { min-width: 190px; margin: 0; }
.explorer-wrap { display: flex; gap: 14px; align-items: stretch; }
.explorer-wrap .explorer-canvas { flex: 1; min-width: 0; }
.explorer-rail { width: 300px; flex: none; margin-top: 16px; border: 1px solid var(--border);
  border-radius: var(--radius-lg); background: var(--bg-elev-1, var(--bg-inset));
  padding: 16px 18px; align-self: flex-start; position: sticky; top: 70px;
  max-height: calc(100vh - 90px); overflow-y: auto; }
.rail-title { font-weight: 600; font-size: 15px; }
.rail-sub { color: var(--text-dim); font-size: 12px; margin-bottom: 4px; }
.rail-section { margin-top: 12px; font-size: 12.5px; line-height: 1.5; }
.rail-head { font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 4px; }
.rail-note { color: var(--text-dim); font-size: 11.5px; margin-bottom: 6px; }
.rail-list { list-style: none; margin: 0; padding: 0; }
.rail-list li { padding: 3px 0; border-bottom: 1px solid var(--border); font-family: var(--font-mono); font-size: 11.5px; }
.rail-list li:last-child { border-bottom: none; }
.rail-clipped { color: var(--danger, #e2685c); }
.explorer-canvas { margin-top: 16px; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--bg-elev); overflow-x: auto; min-height: 320px; }
.explorer-empty { padding: 60px; text-align: center; }
.explorer-svg { display: block; }
.explorer-colhead { font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  fill: var(--text-dim); font-weight: 600; }
.explorer-idbox { fill: var(--bg-elev-2); stroke: var(--border-strong); }
.explorer-grantbox { fill: var(--bg-elev-2); stroke: var(--border); }
.explorer-node:hover rect { stroke: var(--accent); }
.explorer-idlabel { fill: var(--text); font-size: 13px; font-weight: 600; }
.explorer-idsub { fill: var(--text-dim); font-size: 11px; }
.explorer-boundary { fill: var(--bg-inset); stroke: var(--border); stroke-dasharray: none; }
.explorer-boundary-product { stroke: color-mix(in oklch, var(--accent) 45%, transparent); }
.explorer-boundary-title { fill: var(--text-muted); font-size: 12px; font-weight: 600; }
.explorer-rowlabel { fill: var(--text); font-size: 12px; font-family: var(--font-mono); }
.explorer-row:hover .explorer-rowlabel { fill: var(--accent); }
.explorer-dot-terminal { fill: var(--accent); stroke: color-mix(in oklch, var(--accent) 45%, transparent); stroke-width: 3; }
.explorer-dot-sealed { fill: none; stroke: var(--border-strong); stroke-width: 1.5; }
.explorer-aggbox { fill: var(--bg-elev-2); stroke: var(--border); }
/* Customer view: the doors a name answers on when not all selected; a name whose target differs by door. */
.explorer-chip { fill: var(--text-dim); font-size: 10px; letter-spacing: .04em; }
.explorer-chip-warn { fill: var(--warn); }
.explorer-collapse { fill: var(--text-dim); font-size: 10px; }
.explorer-edge { fill: none; stroke: var(--border-strong); stroke-width: 1.2; }
/* "Built from storage" edges are dashed at every zoom — collapsed boundary-to-boundary and per-name alike — so a solid line always means a grant. */
.explorer-edge-mapping { stroke: color-mix(in oklch, var(--accent) 55%, transparent); stroke-width: 1.6; stroke-dasharray: 5 3; }
.explorer-arrow { stroke: none; }
.explorer-arrow-grant { fill: var(--border-strong); }
.explorer-arrow-mapping { fill: color-mix(in oklch, var(--accent) 55%, transparent); }
.explorer-arrow-name { fill: color-mix(in oklch, var(--accent) 75%, transparent); }
.explorer-edge-name { stroke: color-mix(in oklch, var(--accent) 75%, transparent); stroke-width: 1.6; stroke-dasharray: 5 3; }
.explorer-edge-label { fill: var(--accent); font-size: 10px; }
.explorer-card { position: fixed; z-index: 95; max-width: 340px; background: var(--bg-elev-2);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 10px 13px;
  font-size: 12.5px; line-height: 1.55; box-shadow: 0 12px 32px -12px rgba(0,0,0,.55);
  pointer-events: none; }
.explorer-card-dim { color: var(--text-dim); font-family: var(--font-mono); font-size: 11.5px; }
.explorer-footnote { margin-top: 10px; }
/* Legend (drive feedback 2026-08-24: the dot semantics needed asking — now they're printed). */
.explorer-legend { display: flex; flex-wrap: wrap; gap: 18px; align-items: center;
  margin-top: 10px; font-size: 12px; color: var(--text-dim); }
.explorer-legend span { display: inline-flex; align-items: center; gap: 6px; }
.explorer-legend .lg-dot { width: 10px; height: 10px; border-radius: 50%; }
.explorer-legend .lg-dot-terminal { background: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 45%, transparent); }
.explorer-legend .lg-dot-sealed { background: none; border: 1.5px solid var(--border-strong); }
.explorer-legend .lg-line { width: 26px; height: 0; border-top: 1.6px solid; }
.explorer-legend .lg-line-grant { border-color: var(--border-strong); }
.explorer-legend .lg-line-name { border-color: color-mix(in oklch, var(--accent) 75%, transparent); border-top-style: dashed; }
/* Flow tracing (UX-23): plain click on a node/boundary lights its end-to-end path and dims
   the rest; the traced edges thicken slightly so the path reads at a glance. Dimmed groups
   keep pointer events — clicking a dimmed node refocuses the trace onto it. */
.explorer-flow-dim { opacity: .14; }
.explorer-edge.explorer-flow-hot { stroke-width: 2; }
.explorer-edge-grant.explorer-flow-hot { stroke: var(--text-muted); }
.explorer-edge-mapping.explorer-flow-hot { stroke: color-mix(in oklch, var(--accent) 80%, transparent); }
.explorer-focused > rect { stroke: var(--accent); stroke-width: 1.6; }

/* ---------- pair-picker (UX-2): one component, eight configurable styles ---------- */
.sheet.sheet-wide { width: 800px; }
.fe-pickbar { display: flex; gap: 8px; align-items: center; margin: 0 0 8px; }
.fe-search-toggle { background: var(--bg-elev); border: 1px solid var(--border); color: var(--text-muted);
  border-radius: var(--radius-sm); width: 30px; height: 30px; flex: none;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.fe-search-toggle:hover { color: var(--text); border-color: var(--border-strong); }
.fe-search { flex: 1; }
.fe-search.fe-search-hidden { display: none; }
/* shared row semantics */
.fe-hidden-search, .fe-hidden-pane, details.fe-hidden-search { display: none !important; }
.fe-row { cursor: pointer; }
.fe-row input[type=radio] { position: absolute; opacity: 0; pointer-events: none; }
.fe-row > span { display: flex; align-items: baseline; gap: 10px; flex: 1; min-width: 0; }
.fe-dot { width: 13px; height: 13px; border-radius: 50%; border: 1.5px solid var(--border-strong);
  flex: none; align-self: center; }
.fe-row:has(input:checked) .fe-dot { border-color: var(--accent);
  background: radial-gradient(circle at center, var(--accent) 0 3.5px, transparent 4.5px); }
.fe-cell-name { font-family: var(--mono, ui-monospace, monospace); font-size: 12.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.fe-cell-num { width: 86px; text-align: right; color: var(--text-muted); font-size: 12px;
  font-variant-numeric: tabular-nums; flex: none; }
.fe-colhead { display: flex; gap: 10px; padding: 2px 12px 6px 35px; font-size: 10px;
  letter-spacing: .09em; text-transform: uppercase; color: var(--text-dim); font-weight: 600; }
.fe-colhead > span:first-child { flex: 1; }
.fe-colhead > span + span { width: 86px; text-align: right; flex: none; }

/* two-pane (single-select default) */
/* The row track carries the upper bound itself: a max-height on the grid container alone does not
   bound an auto row in every engine, and where it does not, each pane grows to its full content,
   the container's overflow:hidden clips it, and neither list can be scrolled. fit-content keeps a
   short catalog short (the panes' min-height is the lower bound); the container's min/max-height
   stay as the outer bounds (border included). */
.fe-style-twopane { display: grid; grid-template-columns: 230px 1fr;
  grid-template-rows: fit-content(418px); border: 1px solid var(--border);
  border-radius: var(--radius-sm); overflow: hidden; min-height: 260px; max-height: 420px; }
.fe-pane-feeds { background: var(--bg-inset); border-right: 1px solid var(--border); padding: 0 6px 6px;
  overflow-y: auto; min-height: 258px; }
/* One header band across both panes: identical height, padding and rule, sticky so the
   alignment survives scrolling either side. */
.fe-pane-head { position: sticky; top: 0; z-index: 1; display: flex; gap: 10px; align-items: center;
  height: 30px; margin: 0 -6px 4px; padding: 0 16px; border-bottom: 1px solid var(--border);
  font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--text-dim);
  font-weight: 600; }
.fe-pane-feeds .fe-pane-head { background: var(--bg-inset); }
.fe-pane-ents .fe-pane-head { background: var(--bg-elev); margin: 0 -8px 4px; }
.fe-pane-head > span:first-child { flex: 1; }
.fe-pane-head-num { width: 86px; text-align: right; flex: none; }
.fe-pane-feeds .fe-pane-head-num { width: auto; }
.fe-pane-feed { display: flex; justify-content: space-between; align-items: center; width: 100%;
  background: none; border: none; color: var(--text-muted); font-size: 12.5px; text-align: left;
  padding: 7px 10px; border-radius: 7px; cursor: pointer;
  font-family: var(--mono, ui-monospace, monospace); }
.fe-pane-feed:hover { background: var(--bg-elev); }
.fe-pane-feed.on { background: var(--bg-elev-2); color: var(--text); }
.fe-pane-feed-cnt { color: var(--text-dim); font-size: 11px; }
.fe-pane-feed-empty { opacity: .38; }
.fe-pane-ents { padding: 0 8px 8px; overflow-y: auto; min-height: 258px; }
/* Search-term highlight in the picker's feed and entity names: a soft accent wash, no glow. */
mark.fe-hit { background: var(--accent-line); color: inherit; border-radius: 3px; padding: 0 1px; }
.fe-style-twopane .fe-row { display: flex; align-items: center; padding: 7px 10px; border-radius: 7px; }
.fe-style-twopane .fe-row:hover { background: var(--bg-elev); }
.fe-style-twopane .fe-row:has(input:checked) { background: var(--accent-soft, rgba(200,150,60,.12)); }

/* dense-grid */
.fe-style-dense-grid { border: 1px solid var(--border); border-radius: var(--radius-sm);
  overflow: hidden auto; max-height: 420px; font-size: 12px; }
.fe-colhead-flat { background: var(--bg-elev-2); padding: 5px 10px; margin: 0; display: flex; }
.fe-colhead-flat > span:first-child { width: 170px; flex: none; }
.fe-colhead-flat > span:nth-child(2) { flex: 1; }
.fe-style-dense-grid .fe-row { display: flex; align-items: center; border-top: 1px solid var(--border);
  min-height: 28px; padding: 0 10px; }
.fe-style-dense-grid .fe-cell-feed { width: 160px; flex: none; color: var(--text-dim);
  font-family: var(--mono, ui-monospace, monospace); font-size: 11px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.fe-style-dense-grid .fe-row:hover { background: var(--bg-elev); }
.fe-style-dense-grid .fe-row:has(input:checked) { background: var(--accent-soft, rgba(200,150,60,.12));
  box-shadow: inset 2px 0 0 var(--accent); }

/* palette */
.fe-style-palette { border: 1px solid var(--border); border-radius: var(--radius-sm);
  overflow: hidden auto; max-height: 420px; }
.fe-style-palette .fe-row { display: flex; align-items: baseline; padding: 9px 14px;
  border-top: 1px solid var(--border); font-size: 13px; }
.fe-style-palette .fe-row:first-child { border-top: none; }
.fe-style-palette .fe-cell-feed { color: var(--text-dim);
  font-family: var(--mono, ui-monospace, monospace); font-size: 12px; flex: none; }
.fe-style-palette .fe-cell-sep { color: var(--text-dim); flex: none; margin: 0 2px; }
.fe-style-palette .fe-cell-inline { color: var(--text-muted); font-size: 12px; white-space: nowrap; flex: none; }
.fe-style-palette .fe-row:hover { background: var(--bg-elev); }
.fe-style-palette .fe-row:has(input:checked) { background: var(--accent-soft, rgba(200,150,60,.12));
  box-shadow: inset 3px 0 0 var(--accent); }

/* banded shared */
.fe-style-tree-meters, .fe-style-radio-less, .fe-style-row-cards, .fe-style-chip-flow,
.fe-style-mapper-clone { max-height: 420px; overflow-y: auto; }
.fe-style-tree-meters details, .fe-style-radio-less details { border-top: 1px solid var(--border); }
.fe-style-tree-meters .fe-row, .fe-style-radio-less .fe-row {
  display: flex; align-items: center; padding: 7px 12px; border-radius: 7px; margin: 1px 6px; }
.fe-style-tree-meters .fe-row:hover, .fe-style-radio-less .fe-row:hover { background: var(--bg-elev); }
.fe-style-tree-meters .fe-row:has(input:checked),
.fe-style-radio-less .fe-row:has(input:checked) { background: var(--accent-soft, rgba(200,150,60,.12)); }

/* tree-meters extras */
.fe-meter { flex: 1; height: 5px; border-radius: 3px; background: var(--bg-elev-2);
  overflow: hidden; position: relative; align-self: center; }
.fe-meter i { position: absolute; inset: 0 auto 0 0; border-radius: 3px;
  background: linear-gradient(90deg, color-mix(in oklch, var(--accent) 45%, transparent), var(--accent)); }
.fe-style-tree-meters .fe-cell-name { flex: 0 0 200px; }
.fe-style-tree-meters .fe-cell-num { width: auto; white-space: nowrap; }

/* radio-less: no dot, solid selected row */
.fe-style-radio-less .fe-dot { display: none; }
.fe-style-radio-less .fe-row:has(input:checked) { background: var(--accent); }
.fe-style-radio-less .fe-row:has(input:checked) .fe-cell-name { color: oklch(0.22 0.05 80); }
.fe-style-radio-less .fe-row:has(input:checked) .fe-cell-num { color: oklch(0.35 0.06 80); }

/* row-cards */
.fe-style-row-cards details { border: none; }
.fe-style-row-cards summary { font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-dim); font-weight: 600; padding: 12px 2px 6px; list-style: none; cursor: pointer; }
.fe-style-row-cards summary::before { content: none; }
.fe-style-row-cards .fe-row { display: flex; align-items: center; background: var(--bg-elev);
  border: 1px solid var(--border); border-radius: 10px; padding: 10px 14px; margin-bottom: 6px; }
.fe-style-row-cards .fe-dot { display: none; }
.fe-style-row-cards .fe-row:hover { border-color: var(--border-strong); }
.fe-style-row-cards .fe-row:has(input:checked) { border-color: var(--accent);
  background: var(--accent-soft, rgba(200,150,60,.12)); }
.fe-style-row-cards .fe-cell-meta { color: var(--text-muted); font-size: 12px; flex: none; }
.fe-style-row-cards .fe-cell-meta b { color: var(--text); font-weight: 500;
  font-variant-numeric: tabular-nums; }

/* chip-flow */
.fe-style-chip-flow details { border: none; margin-bottom: 6px; }
.fe-style-chip-flow summary { color: var(--text-muted); font-size: 12px; font-weight: 600;
  margin-bottom: 7px; list-style: none; cursor: pointer; }
.fe-style-chip-flow summary::before { content: none; }
.fe-style-chip-flow .fe-entities { display: flex; flex-wrap: wrap; gap: 7px; }
.fe-style-chip-flow .fe-row { display: inline-flex; border: 1px solid var(--border);
  background: var(--bg-elev); border-radius: 99px; padding: 5px 12px; margin: 0; }
.fe-style-chip-flow .fe-row > span { gap: 7px; }
.fe-style-chip-flow .fe-dot { display: none; }
.fe-style-chip-flow .fe-cell-name { flex: none; font-size: 12px; }
.fe-style-chip-flow .fe-sup { color: var(--text-dim); font-size: 10.5px; }
.fe-style-chip-flow .fe-row:hover { border-color: var(--border-strong); }
.fe-style-chip-flow .fe-row:has(input:checked) { border-color: var(--accent);
  background: var(--accent-soft, rgba(200,150,60,.12)); }
.fe-style-chip-flow .fe-row:has(input:checked) .fe-sup { color: var(--accent); }

/* mapper-clone (also the multi accordion's chrome) */
.fe-style-mapper-clone details { background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: 9px; margin-bottom: 8px; overflow: hidden; }
.fe-style-mapper-clone summary { padding: 9px 14px; font-size: 13px; font-weight: 600;
  list-style: none; cursor: pointer; }
.fe-style-mapper-clone summary::before { content: none; }
.fe-style-mapper-clone summary .text-3 { font-weight: 400; }
.fe-style-mapper-clone .fe-row, .fe-style-mapper-clone .fe-grant {
  display: flex; align-items: center; gap: 12px; padding: 8px 14px;
  border-top: 1px solid var(--border); margin: 0; border-radius: 0; }
.fe-style-mapper-clone .fe-row:hover, .fe-style-mapper-clone .fe-grant:hover { background: rgba(255,255,255,.02); }
.fe-style-mapper-clone .fe-row:has(input:checked) { background: var(--accent-soft, rgba(200,150,60,.12)); }
.fe-chip { font-size: 11px; color: var(--text-muted); border: 1px solid var(--border);
  border-radius: 99px; padding: 2px 9px; white-space: nowrap; flex: none; }
.fe-chip-warm { color: var(--accent); border-color: color-mix(in oklch, var(--accent) 45%, transparent); }

.divider { height: 1px; background: var(--border); margin: 18px 0; }
.danger-zone { border: 1px solid var(--danger); background: var(--danger-soft); border-radius: var(--radius-sm); padding: 14px; }
.danger-zone h3 { color: var(--danger); margin-bottom: 4px; }

/* ---------- toast (Tier-2 #15.1: FIFO stack, max 3 visible) ---------- */
/* column-reverse: appending a new toast as the last DOM child places it visually AT the
   anchor corner, pushing older ones upward — no manual position math needed per toast. */
#epc-toast-stack {
  position: fixed; right: 20px; bottom: 20px; z-index: 90;
  display: flex; flex-direction: column-reverse; align-items: flex-end; gap: 8px;
  pointer-events: none;
}
.epc-toast {
  padding: 10px 16px; background: var(--bg-elev-2); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px -10px oklch(0 0 0 / 0.5);
  font-size: 13px; opacity: 0; transform: translateY(8px);
  transition: opacity 180ms var(--ease), transform 180ms var(--ease);
}
.epc-toast.show { opacity: 1; transform: none; }
.epc-toast.tone-err { border-color: var(--danger); color: var(--danger); background: var(--danger-soft); }
.epc-toast.tone-ok { border-color: var(--ok, var(--success, var(--accent))); }

/* ---------- live-stream connection indicator (Tier-2 #13) --------
   Bottom-LEFT (not the toast family's bottom-right, see epcRenderStreamChip in epc-helpers.js):
   the toast stack (Tier-2 #15) can grow to 3 deep in that corner, and a degraded stream is
   exactly the kind of moment an op is also likely to fail/toast — keeping them in separate
   corners avoids the chip fighting a toast for the same screen real estate right when both are
   likely to be visible together. */
.epc-stream-chip {
  position: fixed; left: 20px; bottom: 20px; z-index: 90;
  padding: 7px 14px; background: var(--bg-elev-2); color: var(--text-muted);
  border: 1px solid var(--border-strong); border-radius: 999px;
  box-shadow: 0 8px 24px -10px oklch(0 0 0 / 0.5);
  font-size: 12.5px; display: flex; align-items: center; gap: 7px;
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity 180ms var(--ease), transform 180ms var(--ease);
}
.epc-stream-chip:not(.epc-hidden) { opacity: 1; transform: none; }
.epc-stream-chip::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0;
  background: var(--warn); animation: epc-stream-pulse 1.4s ease-in-out infinite;
}
.epc-stream-chip.epc-stream-chip-paused { color: var(--danger); border-color: var(--danger); }
.epc-stream-chip.epc-stream-chip-paused::before { background: var(--danger); animation: none; }
@keyframes epc-stream-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.form-field-required { color: var(--danger); margin-left: 4px; font-weight: 600; }
.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; }

/* ---------- confirm modal ---------------------------------- */
.modal-backdrop { position: fixed; inset: 0; background: oklch(0 0 0 / 0.45); z-index: 70; display: flex; align-items: center; justify-content: center; animation: epc-fadein 180ms var(--ease); }
.modal { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 420px; max-width: calc(100vw - 40px); max-height: calc(100vh - 80px); display: flex; flex-direction: column; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; box-shadow: 0 30px 80px -20px oklch(0 0 0 / 0.6); z-index: 71; animation: epc-opacity 180ms var(--ease); }
@keyframes epc-opacity { from { opacity: 0; } to { opacity: 1; } }
.modal-head { padding: 22px 22px 0; display: flex; align-items: center; gap: 12px; flex: none; }
.modal-head .h2 { font-size: 15px; }
.modal-body { min-height: 0; overflow: auto; padding: 10px 22px 22px; }
/* Refusal dialogs join multiple server messages with blank lines (epcShowOpRefusal);
   ordinary confirm bodies are single-line strings, for which pre-line is a no-op. */
#epc-modal-body { white-space: pre-line; }
.modal-body .text-2 { line-height: 1.6; }
.modal-foot { padding: 12px 22px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; flex: none; background: var(--bg-inset); }
.type-to-confirm { margin-top: 16px; }
.type-to-confirm .cap { display: block; margin-bottom: 6px; }

/* ---------- one-shot S3 customer handoff ------------------ */
.modal.epc-secret-manifest {
  width: min(920px, calc(100vw - 40px));
  min-height: 0;
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
}
.secret-manifest-head {
  flex: none;
  justify-content: space-between;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.secret-manifest-head .h4 { margin-top: 3px; font-size: 17px; }
.secret-manifest-eyebrow { color: var(--accent); }
.secret-once-badge {
  padding: 4px 7px;
  border: 1px solid color-mix(in oklch, var(--warn) 48%, var(--border));
  border-radius: 999px;
  color: var(--warn);
  background: color-mix(in oklch, var(--warn) 9%, transparent);
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .11em;
}
.secret-manifest-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-top: 16px; }
.secret-manifest-intro { margin: 0 0 12px; max-width: 720px; }
.secret-once-warning {
  display: flex;
  gap: 9px;
  align-items: center;
  margin-bottom: 14px;
  padding: 9px 11px;
  border-left: 2px solid var(--warn);
  background: color-mix(in oklch, var(--warn) 7%, var(--bg-inset));
}
.secret-warning-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex: none;
  border-radius: 50%;
  background: var(--warn);
  color: var(--bg);
  font: 700 11px/1 var(--font-mono);
}
.secret-credentials-grid,
.secret-door-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.secret-credential-field {
  min-width: 0;
  padding: 11px 12px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-inset);
}
.secret-field-head,
.secret-door-heading,
.secret-section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.secret-field-actions { display: flex; gap: 3px; }
.secret-field-actions .btn { min-height: 24px; padding: 2px 7px; font-size: 10.5px; }
.secret-field-value {
  display: block;
  margin-top: 7px;
  color: var(--text);
  font-size: 12px;
  line-height: 1.45;
  overflow-wrap: anywhere;
  user-select: all;
}
.secret-section-head { margin: 19px 0 8px; }
.secret-door-card {
  min-width: 0;
  padding: 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: linear-gradient(145deg, var(--bg-elev), var(--bg-inset));
}
.secret-door-card[data-door="direct"] {
  border-color: color-mix(in oklch, var(--accent) 35%, var(--border));
}
.secret-door-card.unavailable { opacity: .68; border-style: dashed; }
.secret-door-card.unavailable p { margin: 18px 0 5px; }
.secret-door-title { font-size: 13px; font-weight: 650; color: var(--text); }
.secret-door-client {
  display: block;
  margin-top: 2px;
  color: var(--text-dim);
  font: 10.5px/1.35 var(--font-mono);
  font-weight: 400;
}
.secret-door-cost {
  flex: none;
  color: var(--accent);
  font-size: 10px;
  letter-spacing: .03em;
}
.secret-route-flow {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  gap: 6px;
  align-items: center;
  margin: 13px 0 11px;
}
.secret-route-step { min-width: 0; text-align: center; }
.secret-route-node {
  display: block;
  padding: 5px 4px;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  background: var(--bg-elev-2);
  color: var(--text);
  font: 10.5px/1 var(--font-mono);
}
.secret-route-detail { display: block; margin-top: 4px; color: var(--text-dim); font-size: 9.5px; }
.secret-route-arrow { color: var(--accent); font-family: var(--font-mono); }
.secret-door-endpoint {
  display: block;
  padding: 7px 8px;
  border-radius: 4px;
  background: var(--bg-elev-2);
  color: var(--text-muted);
  font-size: 10.5px;
  overflow-wrap: anywhere;
  user-select: all;
}
.secret-door-note { margin: 8px 0 0; line-height: 1.45; }
.secret-paths,
.secret-package-exports {
  display: grid;
  grid-template-columns: minmax(185px, .72fr) minmax(0, 1.45fr) auto;
  gap: 12px;
  align-items: center;
  margin-top: 12px;
  padding: 11px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.secret-paths p,
.secret-package-exports p { margin: 3px 0 0; line-height: 1.4; }
.secret-path-command {
  min-width: 0;
  padding: 7px 8px;
  border-radius: 4px;
  background: var(--bg-inset);
  color: var(--text-muted);
  font-size: 10.5px;
  overflow-wrap: anywhere;
}
.secret-package-exports { grid-template-columns: minmax(185px, .72fr) minmax(0, 2fr); }
.secret-export-actions { display: flex; flex-wrap: wrap; gap: 7px; justify-content: flex-end; }
.secret-export-actions .btn { white-space: nowrap; }
.secret-manifest-foot { flex: none; justify-content: space-between; align-items: center; }

@media (max-width: 760px) {
  .modal.epc-secret-manifest {
    width: calc(100vw - 24px);
    height: calc(100vh - 24px);
    max-height: calc(100vh - 24px);
  }
  .secret-credentials-grid,
  .secret-door-grid { grid-template-columns: 1fr; }
  .secret-paths,
  .secret-package-exports { grid-template-columns: 1fr; align-items: start; }
  .secret-export-actions { justify-content: flex-start; }
  .secret-manifest-foot { gap: 12px; align-items: flex-end; }
}

@media (prefers-reduced-motion: reduce) {
  .modal.epc-secret-manifest { animation: none; }
}

/* Tier-1 #3: bulk/group-bulk failure breakdown swapped into the still-open op sheet — every
   failed row's display + reason, plus optional matched-vs-snapshot drift notes. */
.bulk-results-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; }
.bulk-results-list li { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 8px 10px; background: var(--bg-inset); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.bulk-results-row { font-family: var(--font-mono); font-size: 12px; flex-shrink: 0; }
.bulk-results-msg { font-size: 12px; color: var(--text-muted); text-align: right; }

/* ---------- fragment modal (centered, body = fetched HTML) ----- */
.fmodal-backdrop { position: fixed; inset: 0; background: oklch(0 0 0 / 0.45); z-index: 70; animation: epc-fadein 180ms var(--ease); }
.fmodal { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 520px; max-width: calc(100vw - 40px); max-height: calc(100vh - 80px); display: flex; flex-direction: column; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; box-shadow: 0 30px 80px -20px oklch(0 0 0 / 0.6); z-index: 71; animation: epc-opacity 180ms var(--ease); }
/* A gradient across the panel's face, so an overlay reads as a lit surface rather than a flat
   rectangle raised only by its drop shadow. Deliberately the whole of the effect: two richer
   ideas were tried and dropped — a wide white box-shadow "bloom", which reads as haze rather
   than light, and a radial-gradient on the backdrop, which draws an ellipse whose edge ignores
   the panel's shape entirely. Neither survived being looked at.

   Must come after .sheet, .modal and .fmodal: each sets `background` as a shorthand, which
   resets background-image to none, so an earlier declaration of this would be wiped.

   Applied to the three panels and not to .palette, whose own surface treatment is its own. */
.sheet,
.modal,
.fmodal {
  background-image: linear-gradient(to bottom, oklch(1 0 0 / 0.055) 0%, transparent 48%);
}

.fmodal.fmodal-wide { width: 640px; }
.fmodal-head { padding: 18px 22px; border-bottom: 1px solid var(--border); display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; flex-shrink: 0; }
.fmodal-head h2 { font-size: 16px; }
.fmodal-head-text { min-width: 0; flex: 1; }
.fmodal-subtitle { color: var(--text-muted); font-size: 13px; margin-top: 2px; }
.fmodal-subtitle:empty { display: none; }
.fmodal-body { flex: 1; min-height: 0; overflow: auto; padding: 20px 22px; }
.fmodal-foot { margin: 18px -22px -20px; padding: 14px 22px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; background: var(--bg-inset); }

/* ---------- wizard (full page ceremony) -------------------- */
.wizard { display: flex; flex-direction: column; gap: 20px; animation: epc-fadein 180ms var(--ease); }
.wizard-head { display: flex; flex-direction: column; gap: 8px; }
.wizard-head .crumb { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; color: var(--text-muted); padding: 2px 6px; border-radius: var(--radius-sm); align-self: flex-start; }
.wizard-head .crumb:hover { background: var(--bg-hover); }
.wizard-head h1 { font-size: 22px; }
.wizard-head .subtitle { color: var(--text-muted); font-size: 13px; }

.stepper { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(var(--steps, 5), 1fr); gap: 4px; }
.stepper li { display: flex; flex-direction: column; gap: 2px; padding: 10px 0 0; border-top: 3px solid var(--bg-inset); border-radius: 0; font-size: 13px; color: var(--text-muted); transition: all 240ms var(--ease); }
.stepper li.done, .stepper li.active { border-top-color: var(--accent); color: var(--text); }
.stepper li.active { font-weight: 600; }
.stepper .step-num { font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); font-weight: 600; }
.stepper li.done .step-num, .stepper li.active .step-num { color: var(--accent); }

.wizard-head-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.wizard-head-row .text-2 { margin-top: 4px; }

.wizard-body { display: grid; grid-template-columns: 1fr 300px; gap: var(--d-gap); align-items: flex-start; }
.wizard-steps { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--d-radius); padding: 28px; box-shadow: var(--shadow-card); }
.wizard-step { display: flex; flex-direction: column; gap: 8px; }
.wizard-step h2 { margin-bottom: 8px; }
.wizard-summary { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--d-radius); padding: var(--d-card-pad); position: sticky; top: 120px; box-shadow: var(--shadow-card); }
.wizard-summary .cap { margin-bottom: 10px; display: block; }
.wizard-summary dl { display: flex; flex-direction: column; gap: 10px; font-size: 13px; margin: 0; }
.wizard-summary dt { color: var(--text-muted); font-size: 11px; margin: 0; }
.wizard-summary dd { margin: 0; }
.wizard-foot { display: flex; justify-content: space-between; border-top: 1px solid var(--border); padding-top: 18px; }
.wizard-foot-right { display: flex; gap: 8px; }

.review-grid { display: grid; grid-template-columns: 140px 1fr; row-gap: 10px; column-gap: 16px; font-size: 13px; margin: 0; }
.review-grid dt { color: var(--text-muted); margin: 0; }
.review-grid dd { margin: 0; }

/* Review page: single-column body — the per-record actions live in the header band (see
   .entity-band), section-attached ops in their section's head. */
.review-main { margin-top: 6px; }
.page-shell > .review-main, .page-shell > * > .review-main { margin-top: 0; }
/* On the record review page the tab strip and the content share ONE shell child, so the shell's
   18px flex gap never separates them — restore the same rhythm for exactly that adjacency (must
   sit after the zeroing rule above; equal specificity, later wins). The review sheet's own
   `.review-sheet .review-main { margin: 0 }` still beats this further down. */
.detail-chooser + .review-main { margin-top: 18px; }
.review-main .review-section + .review-section { margin-top: 16px; }
.rail-title { display: flex; align-items: center; gap: 7px; font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); margin: 0 0 10px; }
/* Panel-header icon (UX-17): one glyph per panel NAME, mapped once in ConsoleIcons.panelGlyph
   so Lifecycle (etc.) looks the same on every page. Muted so the label stays the anchor. */
.rail-title .rail-ico { display: inline-flex; flex: none; opacity: 0.55; }

/* Review header: entity band (status chip, mono identifier + display name, subtitle, actions).
   The list/back affordance lives below the review body to keep the title area compact. */
.review-head { display: flex; flex-direction: column; gap: 12px; }
.review-back { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; font-size: 12px; color: var(--text-dim); }
.review-back:hover { color: var(--text); }
.review-foot { display: flex; align-items: center; justify-content: flex-start; margin-top: 4px; padding: 0 0 24px; }
.entity-band { display: flex; align-items: center; gap: 14px; }
.band-chip { width: 43px; height: 43px; border-radius: 7px; display: flex; align-items: center; justify-content: center; font-size: 34px; font-weight: 700; flex: 0 0 auto; }
.band-chip.tone-ok { background: var(--success-soft); color: var(--success); }
.band-chip.tone-info { background: var(--info-soft); color: var(--info); }
.band-chip.tone-warn { background: var(--warn-soft); color: var(--warn); }
.band-chip.tone-err { background: var(--danger-soft); color: var(--danger); }
.band-col { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.band-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
.band-slug { font-family: var(--font-mono); font-size: 17px; font-weight: 600; letter-spacing: -0.01em; }
.band-name { color: var(--text-muted); font-size: 14px; }
.band-sub { color: var(--text-dim); font-size: 12px; }
.record-browser-inline { display: inline-flex; align-items: center; gap: 7px; min-width: 0; max-width: 100%; }
.record-browser-prefix { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.record-browser-current { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; }
.record-browser-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.record-browser-btn { width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--border); border-radius: 5px; background: var(--bg-elev); color: var(--text-muted); text-decoration: none; font-size: 15px; line-height: 1; flex: 0 0 auto; }
.record-browser-btn:hover { color: var(--text); border-color: var(--border-strong); background: var(--bg-hover); }
.record-browser-btn.disabled { opacity: .35; pointer-events: none; }
.band-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.band-actions .btn { height: 30px; border-radius: 7px; font-size: 12.5px; }
.band-actions .btn.icon { width: 30px; }
.band-actions .btn svg, .lifecycle-pop .btn svg { width: 13px; height: 13px; flex: 0 0 auto; }
/* The … overflow: a details-dropdown of .act items, same idiom as the topbar user menu. */
.band-menu { position: relative; }
.band-menu > summary { list-style: none; cursor: pointer; }
.band-menu > summary::-webkit-details-marker { display: none; }
.band-menu > summary::marker { content: ''; }
.band-menu-pop { position: absolute; top: calc(100% + 6px); right: 0; min-width: 210px; padding: 6px; background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: 8px; box-shadow: var(--shadow-modal); z-index: 30; }
/* Section-attached actions (the op `section:` directive) — a compact header band per section:
   one primary + one secondary button, the rest packed into the section's own … menu. */
.sec-actions { display: flex; align-items: center; justify-content: flex-end; gap: 12px; }
.sec-actions .btn { height: 24px; padding: 0 9px; font-size: 11.5px; border-radius: 6px; }
/* Section-head ops render as quiet TEXT LINKS (no fill/border/box, muted; accent on hover) so the
   head stays calm — amber is reserved for the page header band. The … overflow keeps its subtle
   icon-button look; a middot separates adjacent links. */
.sec-actions .btn:not(.icon) { height: auto; padding: 0; border: 0; background: none; border-radius: 0; color: var(--text-muted); font-weight: 500; }
.sec-actions .btn:not(.icon):hover { background: none; color: var(--accent); }
.sec-actions .btn:not(.icon):disabled, .sec-actions .btn:not(.icon):disabled:hover { color: var(--text-dim); cursor: default; }
.sec-actions .btn:not(.icon) + .btn:not(.icon)::before { content: "·"; color: var(--text-dim); margin-right: 12px; }
.sec-actions .btn svg { width: 12px; height: 12px; }
.sec-actions .btn.icon { width: 26px; padding: 0; }

/* Containment: every review/Access section is a CARD — panel surface, hairline border, a real
   header row — instead of a small-caps label floating in the void. The h3 keeps the .rail-title
   class (templates + tests unchanged); inside a card it renders as the card's title. */
/* Shared card frame for the review deck. */
.review-section { background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: var(--radius); }
.review-section { padding: 0 16px 12px; margin-bottom: 4px; }
/* The zero top padding above assumes the section opens with a .rail-title/.card-head (whose own
   12px padding supplies the top space). A headerless section (History's bare timeline, specView's
   set-headed blocks) must bring the space itself or its first row sits on the border. */
.review-section.no-head { padding-top: 12px; }
.review-section > .rail-title, .review-section > .card-head { margin: 0 -16px 12px; padding: 12px 16px; border-bottom: 1px solid var(--border-subtle); }
.review-section > .rail-title, .card-head .rail-title { font-size: 13px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--text); }
.card-head .rail-title { margin: 0; padding: 0; border: 0; }
.card-head .card-cap { color: var(--text-dim); font-size: 11.5px; text-align: right; }
/* Lifecycle presentations (LifecycleMode, per-entity `review.lifecycle:`).
   CHIP — the state chip IS the control: a <details> under the record header whose summary is the
   chip (+ caret when actions exist) and whose panel stacks the transition buttons. Exclusivity
   and outside-click close are delegated in epc-helpers.js. */
.lifecycle-chip { position: relative; display: inline-block; }
.lifecycle-chip > summary { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; list-style: none; border-radius: 999px; }
.lifecycle-chip > summary::-webkit-details-marker { display: none; }
.lifecycle-chip > summary::marker { content: ''; }
.lifecycle-chip > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Same 6px border-corner caret as .tbl .grid-band__chevron — kept separate only because that
   rule is .tbl-scoped; keep the two in step. */
.lifecycle-chip-caret { width: 6px; height: 6px; border-right: 1.5px solid var(--text-dim); border-bottom: 1.5px solid var(--text-dim); transform: rotate(45deg) translateY(-2px); transition: transform 140ms var(--ease); }
.lifecycle-chip[open] .lifecycle-chip-caret { transform: rotate(-135deg) translateY(-2px); }
.lifecycle-pop { position: absolute; top: calc(100% + 6px); left: 0; z-index: 40; display: flex; flex-direction: column; align-items: stretch; gap: 4px; min-width: 180px; padding: 6px; background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-modal); }
.lifecycle-pop .btn { justify-content: flex-start; }
.lifecycle-pop-divider { height: 1px; margin: 2px 4px; background: var(--border); }
/* CARD — a "Lifecycle" section card leading the Overview deck: chip + buttons, danger after the
   spacer (the retired bar's layout, housed in a deck card). */
/* Context-scoped related-block actions ("+ New …") — pushed to the head's right edge, beside the
   count chip, so each related list carries its own create affordance (see RelatedActions). */
.card-head-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* Embedded (compact) grids are card-less — the wrapper sheds .card (entity_grid.jte) so the table
   sits flush in whatever frames it: a single review-section card (inline deck block / review tab),
   or fully bare on a fragment modal / count-drill panel that already provides the boundary. Either
   way the grid never nests a box in a box. The header drops the inset band to read as plain column
   labels — painted with the surface colour (every embedder is bg-elev) rather than `transparent`,
   so it stays opaque: the sticky header (.tbl th is position:sticky) still clips rows scrolling
   under it inside a fmodal-body / page scroll, instead of letting them bleed through the labels.
   See docs/design/console2-detail-blocks.md. */
.tbl-compact .tbl th { background: var(--bg-elev); }

/* ---------- review detail GRID deck (blocks with explicit spans) ----------
   The hybrid alternative to the JS column-balanced masonry (.deck-row/.deck-col), used only when a
   layout declares a partial span or a related block (docs/design/console2-detail-blocks.md). Each
   block is a direct grid child carrying inline `grid-column: span k`; row-flow + dense backfill.
   --deck-cols is the layout's column count. Collapses to one column on a narrow surface. */
.review-deck-grid { display: grid; grid-template-columns: repeat(var(--deck-cols, 3), minmax(0, 1fr)); grid-auto-flow: row dense; gap: 16px; margin: 24px 0; }
.review-deck-grid > .review-section { margin: 0; }
@media (max-width: 760px) {
  .review-deck-grid { grid-template-columns: 1fr; }
  .review-deck-grid > .review-section { grid-column: 1 / -1 !important; }
}

/* ---------- review popup (rowAction sheet) ----------
   The full review page's deck/grid layout is page-scoped (specView.jte's <style>), so the popup
   fragment — injected into #epc-sheet — has no deck CSS and falls back to the global 2-col
   .review-grid, which wraps the markup's third (row-act) cell. Give the popup its own compact
   rendering: a flat single-column key/value list (no bordered section cards) with a 3-col grid
   that matches the dt/dd/row-act markup, sized for the ~520px sheet. */
.review-sheet .review-sheet-guidance { color: var(--text-2); margin: 0 0 14px; }
.review-sheet .review-sheet-actions { margin: 0 0 16px; }
.review-sheet .review-main { margin: 0; }
.review-sheet .review-deck { display: flex; flex-direction: column; gap: 16px; margin: 0; }
.review-sheet .review-section { background: transparent; border: 0; border-radius: 0; padding: 0; margin: 0; }
/* Beats the global `.review-main .review-section + .review-section` 16px (the deck's flex gap owns
   inter-section spacing here); needed for any multi-section popup. */
.review-sheet .review-main .review-section + .review-section { margin-top: 0; }
.review-sheet .review-section > .rail-title { margin: 0 0 10px; padding: 0 0 8px; border-bottom: 1px solid var(--border-subtle); }
.review-sheet .review-grid { grid-template-columns: minmax(88px, max-content) 1fr auto; gap: 8px 14px; }
.review-sheet .review-grid dt { color: var(--text-2); font-size: 12px; }
.review-sheet .review-grid .row-act { display: flex; align-items: center; justify-content: flex-end; }

/* Access-tab scope rows: name | operations via protocols | feeds + entity coverage. Plain text
   columns (the .text-2/.text-3 utilities) — chips are reserved for status (chip overload was the
   old look's biggest noise). overflow-wrap keeps long mono scope names inside the fixed track. */
.access-scope { display: grid; grid-template-columns: 200px 270px minmax(0, 1fr); gap: 16px; align-items: start; padding: 12px 0; border-bottom: 1px solid var(--border); }
.access-scope:last-child { border-bottom: 0; }
.access-scope > div { min-width: 0; overflow-wrap: anywhere; }
.access-scope > div > * + * { margin-top: 2px; }
/* Provenance rows ("Where it comes from"): one line per attachment, hairline-separated. */
.access-assignment { padding: 12px 0; border-bottom: 1px solid var(--border); }
.access-assignment:last-child { border-bottom: 0; }
/* Square tag (the attachment-kind label) — Inter, boxed; distinct from the mono status .chip. */
.tag { display: inline-flex; align-items: center; padding: 3px 9px; border-radius: 5px; background: var(--bg-elev-2); border: 1px solid var(--border-strong); color: var(--text-muted); font-size: 11px; font-weight: 600; white-space: nowrap; }
/* Resolved FK on the review's Overview: the target's display label, linking to its review. */
.ref-link { border-bottom: 1px dashed var(--border-strong); }
.ref-link:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* Shared action system, borderless menu-list form (icon + label). One base `.act` with three intent
   tiers carried by COLOR, not fills — default (neutral), primary (create-style, accent text), danger
   (destructive, red). The whole row tints on hover; no boxes, so a stack of actions stays light. */
.act { display: flex; align-items: center; gap: 10px; width: 100%; justify-content: flex-start; height: 32px; padding: 0 8px; border-radius: var(--radius-sm); border: 0; background: transparent; color: var(--text); font-size: 13px; font-weight: 500; font-family: inherit; text-align: left; text-decoration: none; cursor: pointer; transition: background 120ms var(--ease), color 120ms var(--ease); }
.act svg { width: 15px; height: 15px; flex: 0 0 auto; opacity: 0.55; }
.act:hover { background: var(--bg-hover); }
.act--primary { color: var(--accent); }
.act--primary svg { opacity: 0.9; }
.act--danger { color: var(--danger); }
.act--danger svg { opacity: 0.9; }
.act--danger:hover { background: var(--danger-soft); }
.act[disabled], .act[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }

/* Related: declarative reverse-ref child-counts on the review page — small stat chips that drill
   into the child's filtered grid. */
.related-counts { display: flex; flex-wrap: wrap; gap: 10px; }
.related-count { position: relative; display: inline-flex; flex-direction: column; gap: 3px; min-width: 96px; padding: 10px 28px 10px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-inset); color: var(--text); text-decoration: none; transition: background 120ms var(--ease), border-color 120ms var(--ease); }
a.related-count:hover { background: var(--bg-hover); border-color: var(--border-strong); }
/* Drill-down affordance: a faint arrow on linked chips, brightening on hover (non-linked render as a div, no arrow). */
a.related-count::after { content: "\2192"; position: absolute; top: 9px; right: 11px; font-size: 12px; color: var(--text-dim); opacity: 0.4; transition: opacity 120ms var(--ease); }
a.related-count:hover::after { opacity: 0.95; }
.related-count-n { font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.05; }
.related-count-label { font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-dim); }
/* Inline-related lazy-expander (present: inline). display:contents dissolves the wrapper so the
   button stays a normal flex tile while its panel (flex-basis:100%) wraps to its own full-width line
   below the tiles. The caret rotates on expand; the button reuses the .related-count chrome. */
.related-inline { display: contents; }
.related-expander { cursor: pointer; font: inherit; text-align: left; }
.related-expander:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.related-expander-caret { position: absolute; top: 9px; right: 11px; font-size: 12px; color: var(--text-dim); opacity: 0.5; transition: transform 120ms var(--ease), opacity 120ms var(--ease); }
.related-expander[aria-expanded="true"] .related-expander-caret { transform: rotate(90deg); opacity: 0.95; }
.related-inline-panel { flex-basis: 100%; width: 100%; margin-top: 2px; }
.related-inline-panel[hidden] { display: none; }
/* Resolved VO-list ref chips (elementRef) on the review page: a stack of linked name + uuid-copy
   rows, one per bundled reference (e.g. scope_collection3.scopes → each scope3). */
.ref-list { display: flex; flex-direction: column; gap: 4px; }
.ref-list .ref-row { display: flex; align-items: baseline; gap: 7px; }
/* Grid count cell (the @relation column): a subtle clickable number that drills into the child grid. */
.related-cell { color: var(--text); text-decoration: none; font-variant-numeric: tabular-nums; border-bottom: 1px solid transparent; }
.related-cell:hover { color: var(--accent); border-bottom-color: var(--accent-line); }

/* Segmented radio (the param-bar's enum filters: By Status / By Type) — a fully-rounded pill
   group for fixed low-cardinality choices. The checked segment is a NEUTRAL elevated thumb
   (raised surface + bright text + soft shadow, the iOS/Linear segmented-control idiom) — no
   accent at all, so the strip never competes with the page's primary CTA. Distinct from `.seg`
   (the button/link segment strip) — same family, different children + checked model. */
/* Index param-bar: compact label-over-control filter groups that wrap, plus an applied-filter
   summary row (removable chips + Clear). Replaces the old one-filter-per-row two-column grid. */
.param-bar { margin: 0 0 16px; }

/* Inline grid search (2026-08-24): one input + submit above the grid, rendered only for
   entities with `render: [searchable]` string fields. Sits flush with the param bar's rhythm. */
.grid-search { display: flex; align-items: center; gap: 8px; margin: 0 0 16px; }
.grid-search-input { max-width: 340px; }

/* Bulk bar (view-level `bulk:` opt-in): checkbox column + batch buttons. */
.bulk-bar { align-items: center; margin: 0 0 12px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface-2, transparent); }
.bulk-bar .bulk-select-page { align-items: center; cursor: pointer; }
.epc-bulk-cell { display: flex; align-items: center; justify-content: center; padding: 4px; cursor: pointer; }
.epc-bulk-cb { cursor: pointer; }
.pb-controls { display: flex; flex-wrap: wrap; gap: 12px 22px; align-items: flex-start; }
.pb-group { display: flex; flex-direction: column; gap: 5px; }
.pb-group .form-field { margin: 0; }
.pb-label { font-size: 10px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); }
/* pinToCaller: a locked, preset filter value (scoped to the signed-in caller) — non-interactive. */
.pb-pinned { display: inline-flex; align-items: center; gap: 6px; padding: 7px 11px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg); color: var(--text-2); font-size: 12.5px; }
.pb-pinned svg { color: var(--text-dim); }
.pb-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border-subtle); }
.pb-summary-label { font-size: 11px; color: var(--text-dim); margin-right: 2px; }
.filter-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 7px 3px 9px; border-radius: 6px; background: var(--bg-elev); border: 1px solid var(--border); color: var(--text-muted); font-size: 11.5px; text-decoration: none; transition: color 120ms var(--ease), border-color 120ms var(--ease); }
.filter-chip:hover { color: var(--text); border-color: var(--border-strong); }
.filter-chip svg { color: var(--text-dim); flex: 0 0 auto; }
.filter-chip:hover svg { color: var(--danger); }
.pb-clear { display: inline-flex; align-items: center; gap: 5px; color: var(--accent); font-size: 12px; font-weight: 500; text-decoration: none; margin-left: 4px; }
.pb-clear:hover { filter: brightness(1.12); }

.seg-radio { display: inline-flex; gap: 3px; padding: 3px; background: var(--bg-inset); border: 1px solid var(--border-subtle); border-radius: 999px; }
/* Form variant (op-sheet enum params/subfields): wraps when the symbol set is wide, hugs left. */
.seg-radio-form { flex-wrap: wrap; align-self: flex-start; border-radius: 12px; }
.seg-opt { display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 12px; border-radius: 999px; font-size: 12px; letter-spacing: 0.02em; color: var(--text-muted); cursor: pointer; user-select: none; transition: background 120ms var(--ease), color 120ms var(--ease), box-shadow 120ms var(--ease); }
/* Enum glyph inside a segmented option (filters + op-sheet radios): fixed size, inherits colour. */
.seg-opt svg { flex: 0 0 auto; }
.seg-opt input { position: absolute; width: 0; height: 0; opacity: 0; pointer-events: none; }
.seg-opt:hover { color: var(--text); background: var(--bg-hover); }
.seg-opt:has(input:checked), .seg-opt.active { background: var(--bg-elev-2); color: var(--text); font-weight: 600; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45), inset 0 0 0 1px var(--border); }

.radio-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.radio-card { display: flex; align-items: center; gap: 10px; padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; background: transparent; transition: all 120ms var(--ease); }
.radio-card input { accent-color: var(--accent); }
.radio-card.selected { border-color: var(--accent-line); background: var(--accent-soft); }
.radio-card .h3 { font-size: 13px; }

.scope-card { display: flex; align-items: center; gap: 12px; padding: 12px; border: 1px solid var(--border); border-radius: 8px; margin-bottom: 8px; cursor: pointer; background: transparent; transition: all 120ms var(--ease); }
.scope-card.selected { border-color: var(--accent-line); background: var(--accent-soft); }
.scope-card input { accent-color: var(--accent); }
.scope-card .mono { font-size: 13px; }

/* ---------- breadcrumb ------------------------------------- */
/* Redesign (2026-08-04 session): ancestors-only, one quiet 12px line — plain text links, no
   padded boxes (the boxed 13px links read as a third nav layer). Tight to whatever sits below
   (the H1 / the review band). */
.crumbs { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
.crumbs .sep { opacity: 0.45; flex: 0 0 auto; }
/* T3#21: same ellipsis-on-flex-item technique as .record-browser-label — min-width: 0 lets a
   crumb segment shrink below its content size inside the flex row so max-width + overflow can
   actually clip it, rather than the segment refusing to shrink and blowing out the layout. */
.crumbs a, .crumbs .current {
  min-width: 0;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.crumbs a { color: var(--text-muted); }
.crumbs a:hover { color: var(--text); text-decoration: underline; }
.crumbs .current { color: var(--text); font-weight: 500; }
/* Grid-page scope chip: the bound parent as a filter badge in the count line, not a trail. */
.scope-chip-lead { color: var(--text-muted); }
a.scope-chip { text-decoration: none; }
a.scope-chip:hover { border-color: var(--accent); color: var(--text); }

/* ---------- settings overview (operator homepage) ------------ */
.attention-strip { display: flex; flex-wrap: wrap; gap: 10px; }
.attention-chip { font-size: 13px; padding: 8px 14px; text-decoration: none; background: var(--accent-soft); border-color: transparent; color: var(--text); }
.attention-chip strong { font-weight: 650; font-family: var(--font-mono); }
.attention-chip.warn { background: var(--warn-soft); color: var(--warn); }
.attention-chip:hover { border-color: var(--accent); }
.stat-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.stat-tile { display: flex; flex-direction: column; gap: 8px; padding: 14px 16px; background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: var(--radius); text-decoration: none; }
.stat-tile .stat-head { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 500; color: var(--text-muted); }
.stat-tile .stat-head svg { flex: 0 0 auto; opacity: 0.75; }
.stat-tile .num { font-size: 24px; font-weight: 600; color: var(--text); line-height: 1; }
.stat-tile:hover { border-color: var(--accent); }
.stat-tile:hover .stat-head { color: var(--text); }
/* ov- prefix: .activity-row is TAKEN by the Recent-activity drawer's grid rows further down —
   the un-prefixed name silently lost the cascade to it (2026-08-04 smoke find). */
.ov-activity-row { display: flex; align-items: center; gap: 12px; padding: 8px 6px; margin: 0 -6px; border-radius: var(--radius-sm); white-space: nowrap; transition: background 120ms var(--ease); }
.ov-activity-row:hover { background: var(--bg-hover); }
.ov-activity-action { font-size: 13px; font-weight: 550; color: var(--text); flex: 0 0 auto; }
.ov-activity-target { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ov-activity-when { flex: 0 0 auto; }

/* ---------- empty state ------------------------------------ */
.empty { padding: 40px 20px; text-align: center; color: var(--text-muted); border: 1px dashed var(--border); border-radius: var(--d-radius); background: var(--bg-inset); }
.empty .title { color: var(--text); font-weight: 500; margin-bottom: 4px; }
.empty .icon { opacity: 0.6; margin-bottom: 10px; }
.empty.empty-flat { border: 0; background: transparent; }

/* ---------- chip row helper -------------------------------- */
.chip-row { display: flex; flex-wrap: wrap; gap: 6px; }

/* ---------- sheet footer with optional left-actions split -- */
.sheet-foot.sheet-foot-split { justify-content: space-between; flex-wrap: wrap; }
.sheet-foot-left, .sheet-foot-right { display: flex; gap: 8px; flex-wrap: wrap; }
.sheet-foot-right { margin-left: auto; }

/* ---------- responsive nudges ------------------------------ */
@media (max-width: 1100px) {
  .stat-row { grid-template-columns: repeat(2, 1fr); }
  .feed-grid { grid-template-columns: repeat(2, 1fr); }
  .wizard-body { grid-template-columns: 1fr; }
  .wizard-summary { position: static; }
}
@media (max-width: 680px) {
  .stat-row, .feed-grid { grid-template-columns: 1fr; }
  .page { padding: 16px; }
  .topbar .inner, .subnav .inner { padding: 0 16px; }
}

/* ---------- inner tabs (feed detail, principals) ---------- */
.inner-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); }
.inner-tab { height: 36px; padding: 0 14px; font-size: 13px; color: var(--text-muted); font-weight: 500; display: inline-flex; align-items: center; gap: 8px; text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer; border-radius: 0; }
.inner-tab:hover { color: var(--text); }
.inner-tab.active { color: var(--text); font-weight: 600; border-bottom-color: var(--accent); }

/* ---------- watermark bars -------------------------------- */
.wm-bars { display: flex; flex-direction: column; gap: 12px; }
.wm-row { display: grid; grid-template-columns: 110px 1fr 60px; align-items: center; gap: 12px; }
.wm-row > .cap { color: var(--text-muted); letter-spacing: 0.06em; }
.wm-bar { height: 10px; background: var(--bg-inset); border-radius: 999px; overflow: hidden; position: relative; }
.wm-fill { height: 100%; border-radius: 999px; transition: width 600ms var(--ease); }
.wm-row .mono { text-align: right; font-size: 13px; color: var(--text); font-weight: 500; }

/* ---------- phase tracker (active imports) ---------------- */
.imp-phase-grid { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px 14px; }
.imp-phase { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.imp-phase .cap { color: var(--text-dim); letter-spacing: 0.06em; }
.imp-bar { height: 6px; background: var(--bg-inset); border-radius: 999px; overflow: hidden; }
.imp-bar-fill { height: 100%; border-radius: 999px; transition: width 600ms var(--ease), background 200ms var(--ease); }
.imp-phase .mono { font-size: 12px; color: var(--text); font-variant-numeric: tabular-nums; }

/* ---------- limit usage bar ------------------------------- */
.limit-bar { width: 180px; height: 6px; background: var(--bg-inset); border-radius: 999px; overflow: hidden; }
.limit-bar-fill { height: 100%; border-radius: 999px; }

/* ---------- feed detail (kpi strip + tool tiles + schema picker) ----- */
.kpi-strip { padding: 0; display: grid; grid-template-columns: repeat(4, 1fr); }
.kpi-cell  { padding: var(--d-card-pad); border-right: 1px solid var(--border); }
.kpi-cell-last { border-right: 0; }

.tool-tile { padding: var(--d-card-pad); display: flex; gap: 14px; align-items: flex-start; text-decoration: none; color: inherit; }
.tool-tile.is-disabled { opacity: 0.6; cursor: not-allowed; }

.entity-btn { width: 100%; justify-content: space-between; font-family: var(--font-mono); font-size: 13px; }
.entity-btn.is-active { background: var(--accent-soft); color: var(--accent); }

/* Schema-tab entities pane: hug content (don't stretch to the tall schema table)
   and stick below the topbar+subnav while a long schema scrolls — mirrors the
   .wizard-summary sticky sidebar idiom (top offset clears the 56px topbar + 44px subnav). */
.entities-pane { align-self: flex-start; position: sticky; top: 120px; }

/* Schema tab layout: two viewport-height panes, each with its OWN internal scrollbar
   instead of growing the page (mirrors the .api-docs-grid / .entity-tree idiom below).
   Both panes stick at top:120px (clearing the 56px topbar + 44px subnav) and cap their
   height to the viewport, so a 200-entity list and a long field table each stay one
   screen tall and scroll independently. min-height:0 lets a grid child shrink below its
   content height so overflow-y can engage (grid/flex children default to min-height:auto,
   which would otherwise force the pane to full content height and defeat the scroll). */
.schema-split { display: grid; grid-template-columns: 320px 1fr; gap: var(--d-gap); align-items: start; }
.schema-split > .entities-pane,
.schema-split > .p-card {
  position: sticky;
  top: 120px;
  align-self: start;
  max-height: calc(100vh - 140px);
  min-height: 0;
  overflow-y: auto;
}

/* Two-line entity row (Schema-tab pane): line 1 = name (ellipsis) + field-count
   badge in a space-between flex; line 2 = muted facts. Overrides the single-line
   .entity-btn used by the Files/Snowflake panes. */
.entity-row-2l { flex-direction: column; align-items: stretch; height: auto; min-height: 34px; padding-top: 7px; padding-bottom: 7px; gap: 2px; }
.entity-row-2l .entity-row-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; }
/* Truncate entity names from the FRONT, not the back. Successor entities differ only by a
   trailing digit — insider_trades_global vs insider_trades_global3 — so an end-ellipsis renders
   two different entities as the same string, one of them live with 15M records. On 2026-08-08 a
   seed was launched against the wrong one of exactly that pair. direction:rtl moves the ellipsis
   to the start; unicode-bidi:plaintext keeps the name itself reading left-to-right. */
.entity-row-2l .entity-row-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  direction: rtl;
  text-align: left;
  unicode-bidi: plaintext;
}
.entity-row-2l .entity-row-sub { font-family: var(--font-ui); letter-spacing: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.provider-grid { display: grid; grid-template-columns: 160px 1fr; row-gap: 10px; column-gap: 20px; }

/* ---------- entity doc banner ------------------------------ */
.entity-doc { padding: 12px 16px; background: var(--bg-elev); border: 1px solid var(--border); }
.entity-doc-text { color: var(--text-muted); font-style: italic; font-size: 13px; }

/* ---------- responses accordion --------------------------- */
.resp-row { border-top: 1px solid var(--border-subtle); }
.resp-row:first-child { border-top: 0; }
details > summary { list-style: none; }
details > summary::-webkit-details-marker { display: none; }
details > summary::marker { content: ""; }
.resp-row > summary { display: flex; align-items: center; gap: 12px; padding: 12px 16px; cursor: pointer; }
.resp-row > summary .chev { color: var(--text-dim); transition: transform 160ms var(--ease); }
.resp-row[open] > summary .chev { transform: rotate(90deg); color: var(--text-muted); }
.resp-row .resp-sum { flex: 1; font-size: 13px; }
.resp-row .resp-body { padding: 4px 16px 16px 40px; }

.schema-tree { padding: 4px 0; font-family: var(--font-mono); font-size: 12px; line-height: 1.7; }
.schema-row { display: grid; grid-template-columns: 14px auto auto auto 1fr; align-items: baseline; gap: 1px 8px; padding: 2px 0; }
.schema-row.root { grid-template-columns: 14px auto auto auto; color: var(--text-muted); }
.schema-row .tree-disclose { width: 14px; color: var(--text-dim); }
.schema-row .schema-name { color: var(--text); font-weight: 500; }
.schema-row.root .schema-name { font-style: italic; }
.schema-row .schema-req { font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 600; color: var(--danger); background: var(--danger-soft); padding: 1px 6px; border-radius: 3px; font-family: var(--font-sans); }
/* Consistent optional marker — same shape as required, muted tone (no field is left unlabelled). */
.schema-row .schema-req.opt { color: var(--text-dim); background: var(--bg-elev-2); }
/* Description drops to its own full-width line beneath the name/type, left-aligned and indented
   under the field name (matches the parameter table's colspanned description row). */
.schema-row .schema-desc { color: var(--text-dim); font-family: var(--font-sans); font-size: 12px; font-style: italic; text-align: left; justify-self: start; min-width: 0; grid-column: 2 / -1; grid-row: 2; line-height: 1.4; padding-bottom: 2px; }

/* ---------- api docs layout ------------------------------- */
/* minmax(0, …) on every track so a non-wrapping child (a long entity name in the tree, a wide code
   block in the middle) cannot blow the column past its size and force horizontal page scroll — the
   overflow scrolls inside the column instead, and the entity-name ellipsis can then engage. */
.api-docs-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) minmax(0, 1fr); gap: var(--d-gap); min-height: calc(100vh - 160px); }
/* Three-pane app layout: every column is viewport-height and bottom-aligned, and each scrolls its
   OWN content instead of growing the page. The docs panel joins its two rails on the same 168px
   constant so the three bottoms line up exactly.

   The `> * { flex-shrink: 0 }` is load-bearing, not tidiness: .stack is a flex column, and a
   fixed-height flex container SHRINKS its children to fit rather than overflowing. The parameters
   card is overflow:hidden, so without this an eight-parameter form silently loses ~480px of
   itself — no scrollbar, no cue, the fields simply are not there. Measured before and after. */
/* One source of truth for the three panes' viewport math — they MUST agree or their bottom edges
   stop at different heights. --docs-pane-top clears the topbar (57px) + subnav (45px); the height
   subtracts that clearance PLUS main.page's 48px padding-bottom, because the document scrolls
   those 48px further than the grid and a pane sized without them runs out of sticky travel in the
   last stretch of the scroll. Change these here, never at a call site. */
:root { --docs-pane-top: 120px; --docs-pane-height: calc(100vh - 168px); }
.api-docs-grid > main { position: sticky; top: var(--docs-pane-top); align-self: start; height: var(--docs-pane-height); overflow-y: auto; }
.api-docs-grid > main > * { flex-shrink: 0; }
/* The WHOLE left rail (.docs-rail, a .stack flex column) sticks below the topbar+subnav (same
   idiom and constants as .schema-split) — the feed picker card stays put and the entity tree
   flexes into the remaining height, scrolling its own overflow, while the docs panel scrolls.
   Shared by the explorer and overview sidebars. */
/* 168px = 120 (topbar+subnav clearance) + 48 (main.page's padding-bottom). Both terms are forced,
   not taste. The second one is what keeps the rail pinned: sticky travel is containerHeight -
   elementHeight, and the document scrolls 48px FURTHER than the grid because the page's bottom
   padding sits outside it — a taller rail runs out of runway in the last stretch of the scroll and
   drifts up under the subnav, so the three columns visibly stop at different heights. 168 is the
   tallest a rail can be and still stay pinned to the last pixel; the 48px it leaves below is the
   page's own padding, so the rail reads as full-height rather than short. `height`, not
   `max-height`: a feed with five entities should still fill the column instead of hugging its
   content and leaving dead space beside a long docs panel. */
.docs-rail { position: sticky; top: var(--docs-pane-top); align-self: start; height: var(--docs-pane-height); }
.entity-tree { padding: 8px; overflow: auto; flex: 1 1 auto; min-height: 0; }
.entity-tree .entity-row { width: 100%; justify-content: space-between; font-family: var(--font-mono); font-size: 13px; height: 30px; padding: 0 8px; gap: 6px; border: 0; background: transparent; color: var(--text); cursor: pointer; border-radius: var(--radius-sm); display: flex; align-items: center; text-decoration: none; }
/* Long entity names (e.g. Ciqnontradedsharesouststding) ellipsis-truncate instead of overflowing:
   the name span may shrink (min-width:0), the field-count chip never does. */
.entity-tree .entity-row > .row { min-width: 0; overflow: hidden; }
.entity-tree .entity-row .ent-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.entity-tree .entity-row > .chip { flex-shrink: 0; }
.entity-tree .entity-row:hover { background: var(--bg-hover); }
.entity-tree .entity-row.active { background: var(--bg-hover); font-weight: 600; }
/* Pinned selection: rendered once between the filter head and the list (api_docs.jte), the block
   starts at the top and sticks there for the whole scroll — the active entity AND its operations
   are always on screen. --epc-tree-head-h is measured by epc-helpers (head height varies with the
   filter box); top subtracts the head's -8px card-padding trick and its 4px bottom margin
   (-12 total) so the pin tucks flush under the head with no see-through seam. Opaque backdrop so
   scrolled rows never ghost through; z 1 tucks under the head (2). */
.entity-tree .entity-tree-pin { position: sticky; top: calc(var(--epc-tree-head-h, 84px) - 12px); z-index: 1; background: var(--bg-elev); padding-bottom: 5px; margin-bottom: 6px; border-bottom: 1px solid var(--border); }
.entity-tree .entity-tree-pin .op-list { margin-bottom: 0; }
/* Single-entity feeds: the pin is the whole tree — no separator hanging over an empty list. */
.entity-tree .entity-tree-pin:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
/* Filter hit token: yellow glow on the matched substring (name and description lines). Composed
   from the theme's --warn so both themes stay coherent; --warn-soft's 0.14 alpha is too faint
   for a text highlight. */
mark.filter-hit { background: color-mix(in srgb, var(--warn) 26%, transparent); color: inherit; border-radius: 3px; box-shadow: 0 0 7px color-mix(in srgb, var(--warn) 38%, transparent); }
.entity-tree .op-list { padding-left: 18px; display: flex; flex-direction: column; gap: 2px; margin: 4px 0 8px; }
.entity-tree .op-item { justify-content: flex-start; font-size: 12px; height: 26px; padding: 0 8px; gap: 8px; background: transparent; border: 0; color: var(--text); cursor: pointer; border-radius: var(--radius-sm); display: flex; align-items: center; text-decoration: none; }
.entity-tree .op-item:hover { background: var(--bg-hover); }
.entity-tree .op-item.active { background: var(--accent-soft); color: var(--accent); }
.entity-tree .op-verb { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Filter head: sticks to the top of the tree's own scroll context so the filter stays reachable
   on a 200-entity list. The counter live-updates from epc-helpers' applyFilter. */
.entity-tree .entity-tree-head { position: sticky; top: -8px; z-index: 2; background: var(--bg-elev); display: flex; flex-direction: column; gap: 6px; padding: 8px 0; margin: -8px 0 4px; }
.entity-tree .entity-tree-head .input { width: 100%; }
.entity-tree .entity-tree-head #epc-entity-filter-count { padding: 0 8px; }
/* Two-line rows reuse the shared .entity-row-2l/.entity-row-sub vocabulary; these overrides only
   beat the tree's own single-line row geometry (which otherwise wins on specificity) and size the
   dim description line under the mono name. */
.entity-tree .entity-row.entity-row-2l { flex-direction: column; align-items: stretch; height: auto; min-height: 30px; padding-top: 5px; padding-bottom: 5px; gap: 1px; }
.entity-tree .entity-row-sub { font-size: 11px; color: var(--text-muted); padding-left: 18px; }

/* The operation header pins to the TOP OF ITS OWN PANE (top:0), not to a viewport offset: the docs
   panel is a scroll container in its own right, so the method, path, Explore/Try-it toggle and
   title stay on screen while params and the response body scroll beneath them. The STICKY ELEMENT
   MUST BE THIS CARD, not a strip nested inside it — sticky travel is bounded by the parent's box,
   and an inner strip could only move the height of this card before its edge carried it away.
   The card is opaque (.card sets --bg-elev), and the pane clips whatever scrolls under it, so
   nothing can ghost through above the header.
   The description sentence USED to live outside this card to keep the pin short; that was
   reversed on 2026-08-13 (see api_docs.jte). A title alone — "Current Attachment" — does not say
   what the operation does, so the pinned unit is now the whole statement and is a couple of lines
   taller by design. The cost is real: everything in here is chrome on every scroll, so keep the
   description to one sentence. */
.op-header { padding: var(--d-card-pad); position: sticky; top: 0; z-index: 2; }
.op-header .header-row { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.op-header .op-path { font-size: 14px; flex: 1; overflow: hidden; text-overflow: ellipsis; font-family: var(--font-mono); }
.op-header .op-path .accent { color: var(--accent); }
/* Nested INSIDE .op-header now (was a sibling card). No padding of its own — the card supplies it;
   the top margin is the only separation from the title row. */
.op-header .op-header-desc { margin-top: 8px; }

.response-pre { margin: 0; padding: 16px; font-size: 12px; line-height: 1.7; background: var(--bg); overflow: auto; max-height: 420px; font-family: var(--font-mono); }
/* Try-it columns close on one line: the response card absorbs the middle column's leftover height
   (grid stretches <main>, .stack makes it a flex column, the last card grows), so the response
   body gets the whole lower screen instead of a fixed 420px window. min-height keeps the "Ready"
   shell from collapsing when the params card above is tall. */
/* Was an inline style="overflow:hidden" on the element — moved here so the three-pane layout below
   can override it contextually (an inline style beats every stylesheet rule). Clips the card's
   rounded corners against the parameter table. */
#epc-tryit-root { overflow: hidden; }
/* In the three-pane layout the parameter form scrolls ITSELF rather than pushing the pane into a
   scroll: a form with eight primary-key components is taller than the pane, and if the pane
   scrolls instead, the last card is cut mid-air by the pane's clip edge and reads as a card that
   lost its bottom border. Its head stays pinned so "Send request" never scrolls out of reach.

   FIXED 40/60, not content-sized (2026-08-13). These two cards used to be `flex: 0 1 auto` and
   `flex: 1 1 auto` — the form sized to its parameter count and the response swallowed whatever
   was left. That one pair of rules caused BOTH reported problems: the boundary sat at a different
   height on every operation (1 parameter vs 4), and sending a request moved it again as the body
   replaced the "Ready" shell. A fixed basis makes the column's geometry a property of the LAYOUT
   rather than of the content, so nothing reflows when either changes.

   Third setting of these two rules, and the basis differs per pane on purpose (2026-08-16).

   Before 2026-08-13 both were `auto` basis (`0 1 auto` / `1 1 auto`). That sized the form to its
   parameters — good — but also let the RESPONSE's content set its base size, so sending a request
   moved the boundary as the body replaced the "Ready" shell. On 2026-08-13 both went to a zero
   basis (2:3), which held the boundary still at the cost of a band of empty card under a short
   form: content no longer had a vote anywhere, including where it should have.

   Each pane now gets the basis that suits it. The form keeps an `auto` basis and does not grow, so
   it is exactly as tall as its parameters. The response grows from a ZERO basis, so it takes all
   remaining height while its own content — "Ready" shell or fifty records — cannot influence that
   height. The send-time jump therefore stays fixed; only the deliberate difference between
   operations with different parameter counts remains, which is the thing being asked for.

   `min-height: 0` plus `overflow-y: auto` on the form is what lets a genuinely long parameter list
   shrink and scroll rather than push the response off the pane. Percentages are still wrong here:
   they would be taken against the whole column, which also carries the op-header card and two
   gaps, so they would sum past 100% and overflow. */
.api-docs-grid > main > #epc-tryit-root { flex: 0 1 auto; min-height: 0; overflow-y: auto; }
.api-docs-grid > main > #epc-tryit-root > .card-head { position: sticky; top: 0; z-index: 1; background: var(--bg-elev); }
.api-docs-grid > main > #epc-tryit-response { flex: 1 1 0; min-height: 240px; display: flex; flex-direction: column; }
.api-docs-grid > main > #epc-tryit-response .response-pre { flex: 1 1 auto; min-height: 0; max-height: none; }
/* The SSE ticker is the Stream tab's body and has to absorb the same leftover height the buffered
   body does — it was pinned at 420px by an inline style (which beats every stylesheet rule), so a
   live stream sat in a short window with the rest of the pane empty beneath it. */
.api-docs-grid > main > #epc-tryit-response .ep-c2-stream-ticker { flex: 1 1 auto; min-height: 0; height: auto; }
/* The right rail mirrors the left one (.docs-rail constants): sticky under the subnav, viewport-
   capped, request history absorbing the leftover height with its own scroll — the code sample
   stays at hand while a long parameter form scrolls. Try-it only; Explore's rail keeps its
   natural heights (its last card is three links — growing it reads as emptiness). */
/* Fixed height, not max-height: align-self:start sizes the rail to its content, so with short
   content the history card's flex-grow would have nothing to grow into (the left rail never
   shows this — 200 entities always overflow its cap). */
.docs-rail-right { position: sticky; top: var(--docs-pane-top); align-self: start; height: var(--docs-pane-height); }
/* Same guard as the docs panel's children, same reason: this rail is now a fixed-height flex
   column, and its cards carry overflow:hidden — without this a long code sample would be silently
   shrunk to nothing rather than scrolled. The history card is exempt: it is MEANT to absorb the
   leftover height, and scrolls its own list (next rule). */
.docs-rail-right > .card:not(.history-card) { flex-shrink: 0; }
.docs-rail-right .history-card { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.docs-rail-right .history-card #epc-tryit-history { flex: 1 1 auto; min-height: 0; overflow: auto; }
.response-pre .k { color: var(--accent); }
.response-pre .s { color: var(--success); }
.response-pre .b { color: var(--warn); }
.response-pre .p { color: var(--text-dim); }

/* In-flight placeholder for the response body. The pane's height is already fixed by the 40/60
   split above, so this is not about reserving space — it is about the body not reading as an
   EMPTY result for the duration of the request. Bars are inert decoration: aria-busy on the pane
   is what a screen reader is told, and the shimmer respects prefers-reduced-motion. */
.epc-skeleton { padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.epc-skeleton-bar {
  height: 10px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--bg-hover) 25%, var(--border-subtle) 37%, var(--bg-hover) 63%);
  background-size: 400% 100%;
  animation: epc-skeleton-shimmer 1.4s ease-in-out infinite;
}
@keyframes epc-skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .epc-skeleton-bar { animation: none; }
}

/* Inline attachment preview (Body tab, download mode). Bounded so a large image cannot push the
   pane into its own scroll — the point is recognition, not full-size viewing; the download button
   below it is still the way to get the file. */
.epc-attachment-preview { padding: 16px; display: flex; flex-direction: column; gap: 12px; align-items: flex-start; }
.epc-attachment-preview img {
  max-width: 100%;
  max-height: 320px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
}
.epc-attachment-preview .epc-preview-meta { color: var(--text-muted); font-size: 11px; }

/* API Docs attachment delivery is one operation with two wire-level modes. The response tabs
   share one viewport so attachment affordances never compete with the raw body. */
.attachment-mode { flex: 0 0 auto; }
.epc-attachments-pane {
  gap: 0;
  margin: -16px;
  white-space: normal;
  font-family: var(--font-body);
}
.epc-attachment-choice {
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-subtle);
}
.epc-attachment-choice:last-child { border-bottom: 0; }
.epc-attachment-choice:hover { background: var(--bg-hover); }

.history-item { padding: 10px 14px; border-bottom: 1px solid var(--border-subtle); display: flex; flex-direction: column; gap: 4px; }
.history-item:last-child { border-bottom: 0; }
.history-item.epc-history-restore { cursor: pointer; }
.history-item.epc-history-restore:hover { background: var(--bg-hover); }
/* Author display rules outrank the UA `[hidden] { display: none }` — restate it for the
   pagination surfaces (same idiom as .ep-loading-overlay[hidden] below). */
#epc-tryit-pager[hidden], #epc-tryit-footer[hidden] { display: none; }
/* The response head must survive a long meta line (a 304's "cached · N KB" plus a record
   count): only the status/meta/pager row may shrink — and within it only the meta, which
   ellipsizes — while the tabs+copy row keeps its natural width. */
#epc-tryit-response > .card-head { flex-wrap: nowrap; }
#epc-tryit-response > .card-head > .row:first-child { min-width: 0; flex: 1 1 auto; }
#epc-tryit-response > .card-head > .row:last-child { flex: 0 0 auto; }
/* The meta ABSORBS the row's free space (basis 0, grow 1) rather than being sized by its text.
   Sizing it to content moved the pager sideways on every send: the status chip goes "—" -> "200"
   and the meta goes "Ready" -> "343 ms · 76.8 KB", and the buttons that follow them in the same
   flex row got pushed along by the difference. With a zero basis the meta simply takes whatever
   is left, so First/Prev/Next/Last/Resume sit at a fixed right edge of the left row no matter
   what the status line says. The ellipsis still handles a meta longer than the space (a 304's
   "cached · N KB · 50 records"). */
#epc-tryit-meta {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1 1 0;
}
/* Same reasoning one element earlier: the chip is "—" before a send and "200"/"304"/"ERR" after,
   and without a floor the meta's start jitters as it changes. Three characters is the widest
   value it renders. */
#epc-tryit-status { min-width: 3.5ch; text-align: center; }
#epc-tryit-pager { flex-shrink: 0; }
#epc-tryit-pager button { white-space: nowrap; }

/* Collapsible JSON tree (Try-it Body tab). Native <details> carries the fold state; the
   "… N keys/items" summary hint shows only while folded. Token colors reuse the
   .response-pre .k/.s/.b/.p vocabulary the Raw tab's highlighter emits (list-style/marker
   suppression comes from the global `details > summary` rules). */
.response-pre .n { color: var(--json-num); }
.epc-jt .epc-jt-kids { padding-left: 18px; border-left: 1px solid var(--border-subtle); margin-left: 4px; }
.epc-jt details > .p:last-child { display: block; }
.epc-jt summary { cursor: pointer; user-select: none; }
.epc-jt summary::before { content: '▸'; display: inline-block; width: 12px; color: var(--text-dim); font-size: 10px; }
.epc-jt details[open] > summary::before { content: '▾'; }
.epc-jt details[open] > summary .epc-jt-sum { display: none; }
.epc-jt .epc-jt-sum { color: var(--text-dim); font-style: italic; }
.epc-jt .epc-jt-leaf { padding-left: 12px; }
.epc-jt summary:hover { background: var(--bg-hover); border-radius: var(--radius-sm); }
.history-item .row1 { display: flex; align-items: center; gap: 8px; }
.history-item .path { font-size: 11px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); }

/* ---------- setting tile (large) -------------------------- */
.setting-tile { padding: var(--d-card-pad); text-align: left; cursor: pointer; display: flex; gap: 16px; text-decoration: none; color: inherit; }
.setting-tile .tile-icon { width: 42px; height: 65px; border-radius: 10px; background: var(--accent-soft); color: var(--accent); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.setting-tile .tile-body { flex: 1; }
.setting-tile .tile-head { display: flex; justify-content: space-between; align-items: center; }
.setting-tile .tile-title { font-weight: 600; font-size: 15px; }
.setting-tile .tile-count { font-family: var(--font-mono); font-size: 14px; color: var(--text-muted); }
.setting-tile .tile-desc { font-size: 13px; color: var(--text-muted); margin-top: 4px; }

/* ---------- side-nav variant ------------------------------ */
[data-nav-shape="side"] .topbar .topnav { display: none; }
[data-nav-shape="side"] .side-nav { display: flex; }
.side-nav { display: none; position: fixed; top: 56px; left: 0; width: 56px; bottom: 0; padding: 12px 8px; flex-direction: column; align-items: center; gap: 6px; background: var(--bg); border-right: 1px solid var(--border); z-index: 18; }
.side-nav .nav-icon { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); color: var(--text-muted); background: transparent; border: 0; cursor: pointer; }
.side-nav .nav-icon:hover { background: var(--bg-hover); color: var(--text); }
.side-nav .nav-icon.active { color: var(--accent); background: var(--accent-soft); }
[data-nav-shape="side"] .page, [data-nav-shape="side"] .subnav .inner { padding-left: 84px; }

/* ---------- filterable-select (fs) ------------------------- */
.fs { position: relative; display: inline-flex; max-width: 100%; }
.fs-trigger {
  display: inline-flex; align-items: center; gap: 10px;
  height: 36px; padding: 0 10px 0 6px; max-width: 100%;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text); cursor: pointer;
  transition: border-color 120ms var(--ease), background 120ms var(--ease);
}
.fs-trigger:hover { border-color: var(--border-strong); background: var(--bg-hover); }
.fs-trigger[aria-expanded="true"] { border-color: var(--accent-line); background: var(--accent-soft); }
.fs-trigger .fs-chev { color: var(--text-muted); transition: transform 150ms var(--ease); flex: 0 0 14px; }
.fs-trigger[aria-expanded="true"] .fs-chev { transform: rotate(180deg); }
.fs-trigger-labels { display: inline-flex; flex: 1 1 auto; flex-direction: column; align-items: flex-start; line-height: 1.1; gap: 2px; min-width: 0; }
.fs-trigger-labels .cap { font-size: 9px; letter-spacing: 0.08em; color: var(--text-dim); }
.fs-trigger-value { font-size: 13px; font-weight: 500; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fs-trigger-value.fs-placeholder { font-weight: 400; color: var(--text-muted); }

/* Full-width modifier: when the trigger lives inside a narrow card, stretch
   it to the parent's width so the value gets all available space. */
.fs.fs-block, .fs.fs-block .fs-trigger { display: flex; width: 100%; }

.fs-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex: 0 0 24px;
  background: var(--bg-inset); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 600 10px/1 ui-monospace, Menlo, Consolas, monospace;
  color: var(--text-muted); letter-spacing: 0.04em;
}
.fs-row.active .fs-avatar { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }
/* The popover's leading "× Any" clear row — muted so results stay visually primary. */
.fs-row-clear .fs-row-primary { color: var(--text-muted); font-size: 12.5px; }
.fs-row-clear { border-bottom: 1px solid var(--border); }
/* A values-param typeahead's trailing "More matches — keep typing" row (page-limited results,
   never a silently-capped full list — see GenericGridReader#distinctValuesPage): informational
   only, not selectable, so no hover/pointer affordance. */
.fs-row-more { border-top: 1px solid var(--border); }
.fs-row-more .fs-row-link { cursor: default; }
.fs-row-more .fs-row-link:hover { background: transparent; }
.fs-row-muted { color: var(--text-muted); font-size: 12px; font-style: italic; }

.fs-scrim {
  /* Above sheets (61) and fragment dialogs (71), below the command palette (80).
     Pickers are portaled to body and must remain usable inside either form surface.
     Dim only, no blur — keep the underlying form legible. */
  position: fixed; inset: 0; z-index: 72;
  background: oklch(0 0 0 / 0.28);
  animation: epc-fadein 140ms var(--ease);
}
.fs-popover {
  position: fixed; z-index: 73;
  min-width: 360px; max-width: 460px;
  background: var(--bg-elev-2); border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow:
    0 1px 0 oklch(1 0 0 / 0.06) inset,
    0 24px 60px -12px oklch(0 0 0 / 0.7),
    0 8px 20px -8px oklch(0 0 0 / 0.5);
  padding: 8px; display: flex; flex-direction: column; gap: 6px;
  animation: epc-opacity 140ms var(--ease);
}
[data-theme="light"] .fs-popover {
  box-shadow:
    0 1px 0 oklch(1 0 0 / 0.9) inset,
    0 24px 60px -16px oklch(0 0 0 / 0.18),
    0 8px 20px -10px oklch(0 0 0 / 0.12);
}
.fs-search-row {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; background: var(--bg-inset);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
  color: var(--text-muted);
}
.fs-search-input {
  flex: 1; background: transparent; border: 0; outline: 0;
  color: var(--text); font-size: 13px; padding: 0;
}
.fs-search-input::placeholder { color: var(--text-dim); }
.fs-count { font: 500 11px ui-monospace, Menlo, Consolas, monospace; color: var(--text-dim); }

.fs-col-headers {
  display: flex; justify-content: space-between;
  padding: 4px 10px 2px;
}
.fs-col-headers .cap { font-size: 9px; letter-spacing: 0.08em; color: var(--text-dim); }

.fs-list { list-style: none; margin: 0; padding: 0; max-height: 360px; overflow-y: auto; }
.fs-row {}
.fs-row.fs-hidden { display: none; }
.fs-row-link {
  display: grid; grid-template-columns: 28px minmax(0, 1fr) auto auto;
  align-items: center; gap: 10px;
  padding: 6px 8px; border-radius: var(--radius-sm);
  color: var(--text); text-decoration: none;
}
.fs-no-avatars .fs-row-link { grid-template-columns: minmax(0, 1fr) auto auto; }
.fs-row-link:hover, .fs-row.fs-highlight > .fs-row-link { background: var(--bg-hover); }
.fs-row.active > .fs-row-link { background: var(--accent-soft); }
.fs-row.fs-disabled > .fs-row-link { opacity: .5; cursor: not-allowed; }
.fs-row.fs-disabled > .fs-row-link:hover { background: transparent; }
.fs-row-text { display: flex; flex-direction: column; min-width: 0; gap: 2px; overflow: hidden; }
.fs-row-primary { display: flex; align-items: center; gap: 6px; font-size: 13px; min-width: 0; width: 100%; }
.fs-row-primary .mono { min-width: 0; flex: 1 1 auto; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fs-row-secondary { min-width: 0; width: 100%; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fs-row-meta { display: inline-flex; flex-direction: column; align-items: flex-end; gap: 2px; font-size: 11px; }
.fs-row-meta-bottom { font-size: 10px; letter-spacing: 0.04em; color: var(--text-dim); text-transform: uppercase; }
.fs-check { color: var(--accent); }

.fs-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--text-dim); }
.fs-dot.ok { background: var(--success); }
.fs-dot.warn { background: var(--warn); }
.fs-dot.err { background: var(--danger); }
.fs-dot.idle { background: var(--text-dim); }

.fs-footer {
  display: flex; align-items: center; gap: 8px;
  margin-top: 4px; padding: 8px 10px;
  background: var(--bg-inset); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  font-size: 11px; color: var(--text-muted);
}
.fs-empty { padding: 16px; text-align: center; color: var(--text-dim); font-size: 12px; }

/* ---------- api-docs feed overview ------------------------- */
.api-docs-overview-grid { display: grid; grid-template-columns: 260px 1fr; gap: var(--d-gap); min-height: calc(100vh - 160px); }
.btn.active { background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line); }

.entity-card {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
}
.entity-card[open] { border-color: var(--border-strong); }
.entity-card-summary {
  display: grid; grid-template-columns: 14px 1fr auto;
  gap: 12px; align-items: center;
  padding: 16px 18px; cursor: pointer; list-style: none;
}
.entity-card-summary::-webkit-details-marker { display: none; }
.entity-card-summary .chev { color: var(--text-muted); transition: transform 150ms var(--ease); }
.entity-card[open] .entity-card-summary .chev { transform: rotate(90deg); }
.entity-card-title { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.entity-card-meta { display: inline-flex; align-items: center; gap: 10px; flex-shrink: 0; }
.entity-card-body { padding: 0 18px 14px; border-top: 1px solid var(--border-subtle); }
.entity-card-body .tbl { width: 100%; }

.entity-fields-tbl { table-layout: fixed; width: 100%; }
.entity-fields-tbl td { overflow-wrap: break-word; }
.chip.pk-chip { font-size: 9px; background: var(--warn-soft); color: var(--warn); padding: 1px 6px; }

/* ============================================================
   Code samples — multi-language Try-It code panel.
   Markup is written by code-samples.js; classes are themed here
   using console2 design tokens (no legacy --region-fill / --fg-*).
   ============================================================ */
.code-samples {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-inset);
}
.code-samples-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: var(--bg-elev-2);
  border-bottom: 1px solid var(--border);
}
.code-lang-picker-arrow { color: var(--text-dim); font-size: 8px; }
/* Primary-language tabs (Shell | Python | JavaScript | More ▼) — the shared .seg segmented
   control (same as the response tabs), compacted for the code-sample toolbar. */
.code-lang-tabs { padding: 2px; }
.code-lang-tabs button { padding: 3px 8px; font-size: 11px; font-family: inherit; }
.code-lang-tabs button:hover { color: var(--text); }

.code-samples-copy {
  margin-left: auto;
  height: 26px;
  padding: 0 10px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font: 500 11px var(--font-ui);
  cursor: pointer;
  transition: color 120ms var(--ease), border-color 120ms var(--ease), background 120ms var(--ease);
}
.code-samples-copy:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--bg-hover);
}

.code-samples-body {
  display: flex;
  overflow-x: auto;
}
.code-line-numbers {
  flex-shrink: 0;
  padding: 14px 8px 14px 12px;
  text-align: right;
  user-select: none;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.55;
  min-width: 28px;
  opacity: 0.6;
}
.code-line-numbers span { display: block; }
.code-samples-code {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: 14px 14px 14px 10px;
  overflow-x: auto;
  background: transparent;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre;
}
/* Token colors — picked from the theme accent ramps so dark and light both read. */
.cs-keyword { color: var(--accent); }
.cs-string  { color: var(--success); }
.cs-comment { color: var(--text-dim); font-style: italic; }
.cs-number  { color: var(--warn); }
.cs-flag    { color: var(--info); }

/* ============================================================
   Language picker dialog (used by code-samples.js).
   Reuses the modal-backdrop / modal pattern but legacy code uses
   .feed-picker-* class names — themed below to console2 tokens.
   ============================================================ */
.feed-picker-overlay {
  position: fixed; inset: 0; z-index: 80;
  display: none;
  align-items: center;
  justify-content: center;
  background: oklch(0 0 0 / 0.45);
  animation: epc-fadein 140ms var(--ease);
}
.feed-picker-overlay.active { display: flex; }
.feed-picker-dialog {
  width: 480px;
  max-width: calc(100vw - 40px);
  max-height: 70vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
  overflow: hidden;
  animation: epc-opacity 160ms var(--ease);
}
.feed-picker-dialog.closing { opacity: 0; transition: opacity 200ms var(--ease); }
.feed-picker-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}
.feed-picker-header h3 { margin: 0; font-size: 14px; font-weight: 600; }
.feed-picker-close {
  width: 28px; height: 28px;
  background: transparent;
  border: 0;
  color: var(--text-muted);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.feed-picker-close:hover { background: var(--bg-hover); color: var(--text); }

.feed-picker-search-bar {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-inset);
}
.feed-picker-search-bar input {
  width: 100%;
  height: 30px;
  padding: 0 10px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 13px;
  outline: 0;
}
.feed-picker-search-bar input:focus { border-color: var(--accent-line); }
.feed-picker-search-bar input::placeholder { color: var(--text-dim); }

.feed-picker-cards {
  padding: 8px;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 6px;
}
.feed-picker-card {
  padding: 10px 12px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color 120ms var(--ease), background 120ms var(--ease);
}
.feed-picker-card:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.feed-picker-card.focused { border-color: var(--accent-line); background: var(--accent-soft); }
.feed-picker-card.active { border-color: var(--accent-line); background: var(--accent-soft); color: var(--accent); }
.feed-picker-card h4 { margin: 0; font-size: 13px; font-weight: 500; }

/* ============================================================
   Shared spin keyframe — used by every inline spinner so we
   don't end up with duplicate per-component @keyframes blocks.
   ============================================================ */
@keyframes ep-livesync-rotate {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.ep-livesync-spin {
  animation: ep-livesync-rotate 2.4s linear infinite;
  transform-origin: 50% 50%;
}

/* ============================================================
   Active imports grid — `?` chip + slow-nav loading modal.
   ============================================================ */
.ep-chip-inspect {
  font-family: var(--font-mono, monospace);
  font-weight: 600;
  color: var(--text-2);
  background: transparent;
  border: 1px solid var(--text-3);
  min-width: 22px;
  text-align: center;
  padding: 0 6px;
}
.ep-chip-inspect:hover {
  color: var(--accent);
  border-color: var(--accent);
}

/* Generic slow-operation loading overlay — used by Active Imports and the
   NATS init inspector via the shared loadingOverlay fragment. Triggered by
   any element carrying `[data-ep-slow-nav]`. */
.ep-loading-overlay {
  position: fixed;
  inset: 0;
  background: oklch(0 0 0 / 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  opacity: 0;
  transition: opacity 140ms var(--ease);
}
/* Author `display: flex` outranks the UA `[hidden] { display: none }`
   rule, so the hidden state has to be restored explicitly. */
.ep-loading-overlay[hidden] { display: none; }
.ep-loading-overlay-visible { opacity: 1; }
.ep-loading-card {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--d-radius);
  padding: 22px 28px;
  text-align: center;
  box-shadow: var(--shadow-modal);
  min-width: 220px;
}
.ep-loading-spinner {
  display: inline-block;
  width: 28px;
  height: 28px;
  margin-bottom: 12px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: ep-livesync-rotate 700ms linear infinite;
}
.ep-loading-detail {
  font-size: 11px;
  margin-top: 4px;
}

/* ============================================================
   NATS init-message inspector (Console2 tool view).
   Summary banner, filters, table, per-row "{ }" hover overlay,
   pager, and slow-op loading modal.
   ============================================================ */
.ep-c2-nats-summary {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--text-muted);
  padding: 12px var(--d-card-pad);
  margin-bottom: 14px;
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: var(--d-radius);
}
.ep-c2-nats-summary .mono { color: var(--text); font-weight: 500; }

.ep-c2-nats-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
  padding: 12px var(--d-card-pad);
  margin-bottom: 12px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--d-radius);
}
.ep-c2-nats-filter-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 240px;
  min-width: 0;
}
.ep-c2-nats-filter-row .cap { flex: 0 0 auto; }
.ep-c2-nats-filter-mode { flex: 0 0 auto; width: auto; min-width: 110px; }
.ep-c2-nats-filter-input { flex: 1 1 120px; min-width: 0; }

.ep-c2-nats-time-toggle {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.ep-c2-nats-time-btn {
  background: transparent;
  color: var(--text-muted);
  border: 0;
  padding: 4px 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 120ms var(--ease), color 120ms var(--ease);
}
.ep-c2-nats-time-btn + .ep-c2-nats-time-btn { border-left: 1px solid var(--border); }
.ep-c2-nats-time-btn:hover { color: var(--text); }
.ep-c2-nats-time-btn.active { background: var(--accent-soft); color: var(--accent); }

.ep-c2-nats-table-wrap {
  max-height: calc(100vh - 360px);
  min-height: 240px;
}
.ep-c2-nats-table th { white-space: nowrap; }
.ep-c2-nats-cell {
  max-width: 420px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
}
.ep-c2-nats-data { max-width: 560px; }

.chip.ep-c2-nats-op-init_start  { color: var(--info);    background: var(--info-soft);    border-color: transparent; }
.chip.ep-c2-nats-op-init_data   { color: var(--success); background: var(--success-soft); border-color: transparent; }
.chip.ep-c2-nats-op-init_finish { color: var(--accent);  background: var(--accent-soft);  border-color: transparent; }
.chip.ep-c2-nats-op-init_cancel { color: var(--danger);  background: var(--danger-soft);  border-color: transparent; }

.ep-c2-nats-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
  padding: 10px var(--d-card-pad);
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: var(--d-radius);
}
.ep-c2-nats-pager-link {
  color: var(--text-muted);
  text-decoration: none;
}
.ep-c2-nats-pager-link:hover { color: var(--accent); }

/* Per-row inspect button — appears at the right edge of each row. */
.ep-c2-nats-rawcell { text-align: right; padding-right: 8px; }
.ep-c2-nats-rawbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 24px;
  width: 26px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color 120ms var(--ease), color 120ms var(--ease), background 120ms var(--ease);
}
.ep-c2-nats-rawbtn:hover, .ep-c2-nats-rawbtn:focus {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
  outline: none;
}

/* Click modal that pretty-prints the raw CloudEvent JSON. */
.ep-c2-nats-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 90;
  opacity: 0;
  transition: opacity 120ms var(--ease);
}
.ep-c2-nats-overlay[hidden] { display: none; }
.ep-c2-nats-overlay-visible { opacity: 1; }
.ep-c2-nats-overlay-backdrop {
  position: absolute;
  inset: 0;
  background: oklch(0 0 0 / 0.45);
}
.ep-c2-nats-overlay-card {
  position: relative;
  max-width: min(820px, 92vw);
  max-height: 75vh;
  width: 720px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--d-radius);
  box-shadow: var(--shadow-modal);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.ep-c2-nats-overlay-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 8px 14px;
  background: var(--bg-inset);
  border-bottom: 1px solid var(--border);
}
.ep-c2-nats-overlay-body {
  margin: 0;
  padding: 12px 14px;
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  line-height: 1.55;
  overflow: auto;
  color: var(--text);
  white-space: pre;
  tab-size: 2;
}
.ep-c2-jsx-key { color: var(--json-key); }
.ep-c2-jsx-str { color: var(--json-str); }
.ep-c2-jsx-num { color: var(--json-num); }

/* On a roomy screen, dialogs begin at the content work surface rather than floating around the
   viewport midpoint. Short and narrow viewports deliberately keep the centered defaults above:
   centering is the safer placement when there is not enough room below the app chrome. */
@keyframes epc-dialog-work-surface-pop {
  from { opacity: 0; transform: translate(-50%, 0) scale(0.97); }
  to   { opacity: 1; transform: translate(-50%, 0) scale(1); }
}

@media (min-width: 761px) and (min-height: 760px) {
  .sheet,
  .modal,
  .fmodal {
    top: var(--dialog-work-surface-top);
    max-height: calc(100vh - var(--dialog-work-surface-top) - var(--dialog-viewport-gutter));
    max-height: calc(100dvh - var(--dialog-work-surface-top) - var(--dialog-viewport-gutter));
    transform: translate(-50%, 0);
  }

  .modal.epc-secret-manifest {
    max-height: calc(100vh - var(--dialog-work-surface-top) - var(--dialog-viewport-gutter));
    max-height: calc(100dvh - var(--dialog-work-surface-top) - var(--dialog-viewport-gutter));
  }

  .sheet { animation-name: epc-dialog-work-surface-pop; }

  .feed-picker-overlay,
  .ep-c2-nats-overlay {
    align-items: flex-start;
    padding-top: var(--dialog-work-surface-top);
    padding-bottom: var(--dialog-viewport-gutter);
  }

  .feed-picker-dialog,
  .ep-c2-nats-overlay-card {
    max-height: calc(100vh - var(--dialog-work-surface-top) - var(--dialog-viewport-gutter));
    max-height: calc(100dvh - var(--dialog-work-surface-top) - var(--dialog-viewport-gutter));
  }
}
.ep-c2-jsx-lit { color: var(--json-lit); font-weight: 600; }

/* ============================================================
   Files-tab: file tree
   ============================================================ */
.files-tree { line-height: 1.7; }
.files-tree-root { color: var(--text-muted); padding: 4px 0 8px; font-size: 12px; }
.files-tree-dir { padding: 0; }
.files-tree-dir > summary { list-style: none; cursor: pointer; padding: 4px 8px; border-radius: 6px; display: flex; align-items: center; gap: 8px; }
.files-tree-dir > summary::-webkit-details-marker { display: none; }
.files-tree-dir > summary:hover { background: var(--bg-elev-2); }
.files-tree-caret { display: inline-block; width: 12px; color: var(--text-muted); transition: transform 120ms var(--ease); font-size: 10px; }
.files-tree-dir[open] > summary .files-tree-caret { transform: rotate(90deg); }
.files-tree-label { color: var(--text); }
.files-tree-size { margin-left: auto; color: var(--text-muted); }
.files-tree-children { padding-left: 16px; border-left: 1px dashed var(--border); margin-left: 6px; }
.files-tree-leaf { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 6px; }
.files-tree-leaf:hover { background: var(--bg-elev-2); }
.files-tree-bullet { color: var(--text-muted); width: 12px; display: inline-block; text-align: center; }
.files-tree-age { min-width: 60px; text-align: right; }
.files-tree-md5 { min-width: 70px; text-align: right; opacity: 0.7; }
.inner-tab.is-disabled { opacity: 0.4; pointer-events: none; cursor: not-allowed; }

/* ============================================================
   Activity feed + record history drawers
   Used by:
     - fragments/console2/recentActivitySheet.jte
     - fragments/console2/recordHistorySheet.jte
   ============================================================ */

/* Page-header "Recent activity" button. Keeps the ghost-button base but tightens
   the gap so the activity icon sits flush against the label. */
.btn.epc-recent-btn { gap: 6px; }
.btn.epc-recent-btn svg { color: var(--accent); }

/* Per-row "History" button. Sized to match the adjacent action buttons; the
   svg keeps the same color cue as the activity icon so the user reads the two
   surfaces as the same family. */

/* Recent activity body ---------------------------------------- */
.activity-feed { display: flex; flex-direction: column; gap: 4px; }
.activity-feed-summary { color: var(--text-muted); font-size: 13px; padding-bottom: 14px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.activity-feed-rule { display: flex; justify-content: space-between; align-items: center; padding: 0 2px 10px; }
.activity-feed-rule .cap { color: var(--text-dim); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; }
.activity-feed-empty { padding: 32px 12px; text-align: center; color: var(--text-muted); border: 1px dashed var(--border); border-radius: var(--radius-sm); background: var(--bg-inset); }
.activity-feed-empty .empty-title { color: var(--text); font-weight: 600; margin-bottom: 6px; }

/* Day header – sticky-ish dim caption between groups */
.activity-day {
  font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-dim); font-weight: 600;
  padding: 16px 2px 8px; border-top: 1px dashed var(--border-subtle);
  margin-top: 4px;
}
.activity-day:first-of-type { border-top: 0; margin-top: 0; padding-top: 4px; }

/* Each activity row */
.activity-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.activity-row {
  display: grid; grid-template-columns: 12px 1fr; gap: 12px;
  padding: 8px 4px; border-radius: var(--radius-sm);
  transition: background 120ms var(--ease);
}
.activity-row:hover { background: var(--bg-elev-2); }
.activity-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--text-dim); margin-top: 7px; margin-left: 2px;
  box-shadow: 0 0 0 3px transparent;
}
.activity-dot.tone-ok   { background: var(--success); box-shadow: 0 0 0 3px var(--success-soft); }
.activity-dot.tone-err  { background: var(--danger);  box-shadow: 0 0 0 3px var(--danger-soft); }
.activity-dot.tone-info { background: var(--warn);    box-shadow: 0 0 0 3px var(--warn-soft); }

.activity-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.activity-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.activity-ident {
  font-family: var(--font-mono); font-size: 13px; font-weight: 500;
  color: var(--text); text-decoration: underline; text-decoration-color: var(--border-strong);
  text-underline-offset: 3px; text-decoration-thickness: 1px;
  cursor: pointer;
}
.activity-ident:hover { color: var(--accent); text-decoration-color: var(--accent-line); }
.activity-ident-block { display: inline-flex; align-items: baseline; gap: 2px; min-width: 0; flex: 1; }
.activity-action { color: var(--text-muted); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.activity-time { white-space: nowrap; font-variant-numeric: tabular-nums; }
.activity-summary { font-size: 13px; color: var(--text-muted); }
.activity-diffs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }

/* Field diff chip — "Role viewer → operator" */
.diff-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; padding: 3px 8px;
  background: var(--bg-elev-2); border: 1px solid var(--border-subtle);
  border-radius: 6px;
  max-width: 100%;
}
.diff-chip .diff-label { color: var(--text-dim); letter-spacing: 0.02em; font-weight: 500; }
.diff-chip .diff-old, .diff-chip .diff-new { display: inline-flex; align-items: center; gap: 4px; min-width: 0; }
.diff-chip .diff-old { color: var(--text-dim); }
.diff-chip .diff-arrow { color: var(--text-dim); font-size: 10px; }
.diff-chip .diff-new { color: var(--text); font-weight: 600; }
.diff-chip .diff-old .diff-val { max-width: 14ch; }
.diff-chip .diff-new .diff-val { max-width: 22ch; }
.diff-chip .diff-val { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.diff-chip .diff-ic { display: inline-flex; flex-shrink: 0; }
.diff-chip .diff-ic svg { display: block; }

/* Record history body ----------------------------------------- */
.record-history { display: flex; flex-direction: column; gap: 0; }
.record-history-summary { color: var(--text-muted); font-size: 13px; padding-bottom: 14px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }

.version-timeline { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 14px; position: relative; }
.version-timeline::before {
  content: ""; position: absolute; left: 17px; top: 12px; bottom: 12px;
  width: 0; border-left: 1px dotted var(--border-strong);
}
.version-row { display: grid; grid-template-columns: 36px 1fr; gap: 12px; position: relative; }
/* Related rows deep-link to the child's own History — a stretched link covers the whole row. The
   negative inset gives a comfortable hit area; the badge/text underneath stay purely visual. */
.version-row--link { cursor: pointer; }
.version-row--link .version-rowlink { position: absolute; inset: -3px -8px; z-index: 2; border-radius: 8px; }
.version-row--link:hover { background: var(--bg-hover); border-radius: 8px; }
.version-row--link .version-rowlink:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 0; }
/* The event a related deep-link points at: a glowing accent panel + a "→" marker in the gutter. The
   cancelling margin/padding extends the panel without shifting the badge or the connector line. */
.version-row--hl { margin: -3px -8px; padding: 3px 8px; background: var(--accent-soft); border-radius: 8px; box-shadow: 0 0 18px -6px var(--accent); }
.version-row--hl::before { content: "→"; position: absolute; left: -14px; top: 3px; height: 22px; display: inline-flex; align-items: center; color: var(--accent); font-weight: 700; font-size: 13px; }
/* Per-version related "show more": overflow rows collapse; the control reveals them in place. */
.version-row--hidden { display: none; }
.version-more { padding-left: 48px; }
.version-more-btn { background: none; border: 0; padding: 2px 0; color: var(--text-dim); font-size: 12px; cursor: pointer; }
.version-more-btn:hover { color: var(--accent); }

.version-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 22px; border-radius: 999px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  background: var(--bg-elev); border: 1px solid var(--border);
  color: var(--text-muted);
  position: relative; z-index: 1;
}
.version-badge.tone-ok   { border-color: var(--success); color: var(--success); background: var(--success-soft); }
.version-badge.tone-err  { border-color: var(--danger);  color: var(--danger);  background: var(--danger-soft); }
.version-badge.tone-info { border-color: var(--warn);    color: var(--warn);    background: var(--warn-soft); }
/* Related-entity rows carry the parent's version GRAY (placed after the tone rules so it wins): the
   change happened in the record's orbit but didn't advance its own version. */
.version-badge.version-badge--related { border-color: var(--border-subtle); color: var(--text-dim); background: var(--bg-elev); }

.version-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; padding-top: 1px; }
.version-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.version-actor { display: inline-flex; align-items: baseline; gap: 8px; min-width: 0; font-size: 13px; color: var(--text); }
.version-op-icon { display: inline-flex; flex-shrink: 0; align-self: center; color: var(--text-dim); }
.version-op-icon svg { display: block; width: 14px; height: 14px; }
.version-action { color: var(--text); }
.version-subject { font-family: var(--font-mono); color: var(--text-2); }
.version-op-meta { color: var(--text-dim); font-size: 12px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.version-op-id { font-family: var(--font-mono); color: var(--text-2); }
.version-actor-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.version-time { white-space: nowrap; font-variant-numeric: tabular-nums; }
.version-summary { color: var(--text-dim); font-size: 12px; font-style: italic; }
/* Single-line variant (2026-08-05): rides the head row between the actor cluster and the time —
   flex:1 claims the slack, ellipsis guards overflow, text-align pins it toward the time side. */
.version-summary--inline { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; margin-left: 10px; }

/* Full-page History view (pages/console2/history.jte) ---------- */
.history-page { display: flex; flex-direction: column; gap: 18px; }

.history-head { display: flex; flex-direction: column; gap: 10px; }
.history-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.history-title-row .h1 { font-size: 22px; }
.history-ident { color: var(--accent); }
.history-subtitle { color: var(--text-muted); font-size: 12px; margin-top: 4px; }

.history-caption-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; border-top: 1px dashed var(--border-subtle); padding-top: 10px; }
.history-legend { display: flex; gap: 14px; flex-wrap: wrap; }
.legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); }
.legend-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-dim); display: inline-block; }
.legend-dot.tone-ok   { background: var(--success); }
.legend-dot.tone-err  { background: var(--danger); }
.legend-dot.tone-info { background: var(--warn); }

.history-timeline { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 14px; position: relative; }
.history-timeline::before {
  content: ""; position: absolute; left: 22px; top: 18px; bottom: 18px;
  width: 1px; background: var(--border-subtle);
}
.history-row { display: grid; grid-template-columns: 50px 1fr; gap: 16px; align-items: start; position: relative; }

.history-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 22px; border-radius: 999px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  background: var(--bg-elev); border: 1px solid var(--border);
  color: var(--text-muted);
  position: relative; z-index: 1;
}
.history-badge.tone-ok   { border-color: var(--success); color: var(--success); background: var(--success-soft); }
.history-badge.tone-err  { border-color: var(--danger);  color: var(--danger);  background: var(--danger-soft); }
.history-badge.tone-info { border-color: var(--warn);    color: var(--warn);    background: var(--warn-soft); }

.history-card {
  background: var(--bg-elev); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
}
.history-card-body { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px; }
.history-card-snapshot[open] { border-top: 1px solid var(--border-subtle); }
.history-card-snapshot > summary {
  cursor: pointer; padding: 8px 14px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  border-top: 1px dashed var(--border-subtle);
  font-size: 11px; color: var(--text-dim); user-select: none;
}
.history-card-snapshot > summary:hover { color: var(--text-muted); background: var(--bg-elev-2); }
.history-card-snapshot > summary::after {
  content: "▸"; font-size: 9px; transition: transform 120ms var(--ease);
}
.history-card-snapshot[open] > summary::after { transform: rotate(90deg); }

.history-head-line { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.history-chip { font-size: 10px; letter-spacing: 0.08em; font-weight: 600; padding: 2px 8px; }
.history-chip.tone-ok   { color: var(--success); background: var(--success-soft); border-color: transparent; }
.history-chip.tone-err  { color: var(--danger);  background: var(--danger-soft);  border-color: transparent; }
.history-chip.tone-info { color: var(--warn);    background: var(--warn-soft);    border-color: transparent; }
.history-actor { font-size: 13px; color: var(--text); }
.history-summary-text { flex: 1; min-width: 0; }
.history-ts-block { display: inline-flex; align-items: baseline; gap: 6px; margin-left: auto; white-space: nowrap; }
.history-rel { font-style: normal; }

.history-note { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding-left: 2px; }
.history-meta-line { display: flex; gap: 10px; color: var(--text-dim); padding-left: 2px; font-size: 11px; }

.history-snapshot {
  padding: 12px 16px 14px;
  background: var(--bg-inset);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
}
.review-grid.compact { grid-template-columns: 180px 1fr; row-gap: 6px; column-gap: 12px; font-size: 12px; }

.empty-card {
  background: var(--bg-elev); border: 1px dashed var(--border);
  border-radius: var(--radius-sm); padding: 32px; text-align: center;
  margin: 24px 0;
}
.empty-card .empty-title { font-size: 16px; font-weight: 600; margin-bottom: 6px; }
.empty-card .empty-detail { color: var(--text-muted); }

/* ============================================================
   Validation errors — rendered by window.epcAnchorErrors when
   Console2Handler.behaviorOp returns a Failed Result. Banners
   at the top of the form, .epc-field-error spans under inputs
   keyed by the structured error path (dotted paths supported
   for nested VO sub-fields).
   ============================================================ */
.epc-form-error-banner {
  background: var(--danger-soft);
  border: 1px solid var(--danger);
  color: var(--danger);
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  margin-bottom: 12px;
  font-size: 13px;
}
.epc-form-error-banner-version {
  background: var(--warn-soft);
  border-color: var(--warn);
  color: var(--warn);
}
.epc-field-error {
  display: block;
  color: var(--danger);
  font-size: 12px;
  margin-top: 4px;
}
/* Per-record live-stream "gone" banner (Console2Handler.reviewStream): prepended atop the
   review-live-region when a change makes the row disappear from the caller's auth-scoped read
   (deleted, or scope lost). Reuses the warn-toned form-error-banner palette. Non-dismissible:
   while present, the sibling rule below renders the retained (stale) region content inert —
   its op buttons would otherwise still launch sheets against an id the stream just declared
   unresolvable. Last-known values stay readable, not actionable. */
.epc-record-gone-banner {
  display: flex;
  align-items: center;
  gap: 12px;
}
#review-live-region:has(.epc-record-gone-banner) > :not(.epc-record-gone-banner) {
  pointer-events: none;
  opacity: 0.55;
}
.epc-has-field-error {
  border-color: var(--danger) !important;
  box-shadow: 0 0 0 2px var(--danger-soft);
}

/* ── Error pages (4xx/5xx) ───────────────────────────────────────────────── */
/* Scoped to the error-panel <section> (error.jte) so this full-page centering
   layout can't leak onto the many inline elements that carry `err` purely as a
   tone modifier (.wq-tag.err, .dot.err, .chip.err, span.err, …) — those are
   spans, never sections. A bare `.err` here previously inflated e.g. the
   work-queue IMPORTS chip with a 12vh top margin + column flow. */
section.err {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 2px;
  max-width: 520px;
  margin: clamp(48px, 12vh, 140px) auto 0;
  padding: 8px 24px 24px;
  animation: epc-fadein 320ms var(--ease);
}
/* A neutral, tone-tinted status glyph above the code (never the brand logomark). */
.err-glyph {
  margin-bottom: 8px;
  line-height: 0;
  opacity: 0.85;
  color: var(--text-dim);
}
.err--neutral .err-glyph { color: var(--text-dim); }
.err--warn    .err-glyph { color: var(--warn); }
.err--danger  .err-glyph { color: var(--danger); }
.err-code {
  font-size: clamp(72px, 14vw, 120px);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.02em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.err--neutral .err-code { color: var(--text-muted); }
.err--warn    .err-code { color: var(--warn); }
.err--danger  .err-code { color: var(--danger); }
.err-title {
  margin: 14px 0 0;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}
.err-message {
  margin: 10px 0 0;
  max-width: 40ch;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-muted);
}
.err-detail {
  margin: 14px 0 0;
  padding: 8px 12px;
  font-size: 12px;
  color: var(--text-dim);
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  max-width: 100%;
  overflow-wrap: anywhere;
}
.err-actions {
  margin-top: 28px;
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}
.err-actions .btn { min-width: 132px; justify-content: center; }

/* ── Info popover (metric self-documentation) ─────────────────────────────
   Rendered by _components/info_dot.jte from a MetricInfo. Click the ⓘ to pin;
   epcInfoDot in epc-helpers.js toggles .open and flips when near a viewport edge. */
.info-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.info-dot {
  appearance: none;
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
  line-height: 0;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: 99px;
  transition: color 0.12s var(--ease);
}
.info-dot:hover,
.info-dot[aria-expanded="true"] { color: var(--accent); }
.info-dot:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* position: fixed so the popover escapes any overflow:hidden ancestor (table
   cards, scroll panels). Coordinates are set by epcInfoDot in epc-helpers.js. */
.info-pop {
  position: fixed;
  width: 300px;
  max-width: min(80vw, 340px);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  box-shadow: var(--shadow-modal);
  z-index: 200;
  display: none;
  flex-direction: column;
  gap: 8px;
  text-align: left;
  white-space: normal;
  cursor: default;
}
.info-pop.open { display: flex; }
.operational-help-pop { max-height: calc(100vh - 16px); overflow: auto; }
.info-pop-title { font-weight: 600; font-size: 13px; color: var(--text); }
.info-pop-def { font-size: 12px; color: var(--text-muted); line-height: 1.45; }
.info-pop-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 10px;
  border-top: 1px solid var(--border-subtle);
  padding-top: 8px;
}
.info-pop-k {
  font-size: 10.5px;
  color: var(--text-dim);
  font-family: var(--font-mono);
}
.info-pop-v { font-size: 10.5px; color: var(--text-muted); line-height: 1.4; }
.info-pop-v.mono { font-family: var(--font-mono); overflow-wrap: anywhere; }
.info-pop-link {
  font-size: 12px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
}
.info-pop-link:hover { text-decoration: underline; }

/* ── Governance overview: SLO band ───────────────────────────────────────── */
.slo-band {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-bottom: 24px;
}
.work-queue { margin-bottom: 24px; }
@media (max-width: 900px) {
  .slo-band { grid-template-columns: 1fr; }
}
.slo-tile {
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--d-radius);
  box-shadow: var(--shadow-card);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.slo-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.slo-value-row {
  display: flex;
  align-items: baseline;
  gap: 7px;
}
.slo-value { font-size: 30px; font-weight: 700; line-height: 1; }
.slo-unit { font-size: 11px; color: var(--text-dim); }
.slo-foot { display: flex; align-items: center; gap: 7px; }
.sla-track { height: 4px; border-radius: 99px; background: var(--bg-inset); overflow: hidden; }
.sla-fill { display: block; height: 100%; border-radius: 99px; background: var(--success); }
.sla-fill.warn { background: var(--warn); }
.sla-fill.idle { background: transparent; }

/* ── Governance overview: work queue ─────────────────────────────────────── */
.work-queue .card-head-l {
  display: flex;
  align-items: center;
  gap: 9px;
}
.wq-empty {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 16px;
  color: var(--text-muted);
  font-size: 13px;
}
.wq-list { list-style: none; margin: 0; padding: 0; }
.wq-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 16px;
  border-top: 1px solid var(--border-subtle);
}
.wq-row:first-child { border-top: 0; }
.wq-tag {
  flex: none;
  width: 96px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--text-muted);
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  border-radius: 5px;
  padding: 3px 8px;
}
.wq-tag.warn { color: var(--warn); background: var(--warn-soft); border-color: transparent; }
.wq-tag.err { color: var(--danger); background: var(--danger-soft); border-color: transparent; }
.wq-main { flex: 1 1 auto; display: flex; flex-direction: column; gap: 3px; }
.wq-title { font-size: 13px; font-weight: 500; }

/* ── Governance overview: feeds table ─────────────────────────────────────
   FEED is the wide left column; everything else shrinks to content and clusters
   on the right (the inventory-table look from the mock). */
.feeds-tbl td:not(.feed-td),
.feeds-tbl th:not(:first-child) {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}
.feeds-tbl th .th-inline {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.feeds-tbl th:not(:first-child) .th-inline { justify-content: flex-end; }
.feed-cell { display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
.feed-cell .feed-name { font-family: var(--font-mono); font-size: 12.5px; color: var(--text); }
.feed-cell:hover .feed-name { color: var(--accent); }
.feeds-tbl .type-col { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); }
.feed-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  color: var(--text-muted);
  border: 1px solid var(--border-subtle);
  background: var(--bg-inset);
  border-radius: 99px;
  padding: 2px 9px;
}
.feed-status.s-ok { color: var(--success); }
.feed-status.s-err { color: var(--danger); }

/* ── Feed detail: audit timeline ─────────────────────────────────────────── */
.audit-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 11px 16px;
  border-bottom: 1px solid var(--border-subtle);
}
.audit-row:last-child { border-bottom: 0; }
.audit-when { width: 96px; flex: none; }
.audit-target { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Feed detail: schema table optional (nullable) marker ────────────────── */
/* Optional/nullable field marker in the schema type column — a muted, compact pill next to the
   amber .pk-chip (base .chip supplies the muted colors + white-space:nowrap so it never wraps). */
.chip.opt-chip {
  font-size: 9px;
  padding: 1px 6px;
}

/* ── Feed detail: Files metadata-strip reconciliation detail ─────────────── */
.recon-detail {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 16px;
  border-top: 1px solid var(--border-subtle);
}
.recon-detail > .truncate { max-width: 100%; }


/* ── Metamodel inspector (operator-only) ── */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.back-link { color: var(--text-dim); font-size: 12.5px; text-decoration: none; align-self: flex-start; }
.back-link:hover { color: var(--text); }
.mm-file { margin-top: 12px; }
.mm-file-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.mm-file-head .btn.icon { width: auto; padding: 0 9px; height: 26px; gap: 5px; font-size: 11.5px; }
.mm-copy-label { font-size: 11.5px; }
.mm-yaml { margin: 0; padding: 12px 14px; background: var(--bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: 12px; line-height: 1.55; color: var(--text); overflow-x: auto; white-space: pre; tab-size: 2; }
/* YAML syntax tokens (added by epcMmHighlight; theme-adaptive via semantic vars). */
.mm-yaml .yml-k { color: var(--accent); }
.mm-yaml .yml-t, .mm-yaml .yml-p { color: var(--text-dim); }
.mm-yaml .yml-c { color: var(--text-dim); font-style: italic; }
.mm-yaml .yml-q { color: var(--info); }
.mm-yaml .yml-s { color: var(--text); }
.mm-yaml .yml-n { color: var(--warn); }
.mm-yaml .yml-b { color: oklch(0.62 0.16 300); }

/* ── Metamodel inspector · treegrid ── */
.mm-page { display: flex; flex-direction: column; gap: 16px; }
/* The viewer is two panes side by side: declarations (sheet/tree/source) on the left, the visual
   (map/ER) on the right, split by a draggable gutter. --mm-left is the left pane's share; the
   script sets it from the operator's last drag. The visual pane is sticky so it stays in view while
   a long datasheet scrolls beside it; its canvas takes the viewport height under the chrome. */
.mm-workspace { --mm-left: 55%; display: grid; grid-template-columns: minmax(0, calc(var(--mm-left) - 7px)) 14px minmax(0, 1fr); align-items: start; }
.mm-workspace.is-solo { grid-template-columns: minmax(0, 1fr); }
.mm-pane { min-width: 0; display: flex; flex-direction: column; gap: 12px; }
/* The left pane is a size container so the Tree's own two-column split stacks when the pane, not
   the viewport, gets narrow — the gutter can take it well under the page-level breakpoint. */
.mm-pane-left { container-type: inline-size; }
@container (max-width: 720px) {
  .mm-pane-left .mm-split { grid-template-columns: minmax(0, 1fr); }
  .mm-pane-left .mm-detail { position: static; }
}
.mm-pane-right { position: sticky; top: var(--docs-pane-top, 120px); }
.mm-pane-right .mm-map { height: calc(100vh - 240px); min-height: 360px; }
.mm-gutter { align-self: stretch; position: relative; cursor: col-resize; touch-action: none; }
/* Invisible at rest — the panes' own edges already separate them — and lit while the pointer is
   over it, so the handle announces itself exactly when it can be used. */
.mm-gutter::before { content: ""; position: absolute; top: 0; bottom: 0; left: 6px; width: 2px; border-radius: 2px; background: transparent; transition: background 140ms var(--ease), box-shadow 140ms var(--ease); }
.mm-gutter:hover::before, .mm-gutter.is-dragging::before, .mm-gutter:focus-visible::before { background: var(--accent); box-shadow: 0 0 10px 2px var(--accent-soft), 0 0 2px var(--accent); }
.mm-gutter:focus-visible { outline: none; }
.mm-visual-seg { margin-left: auto; }
@media (max-width: 1100px) {
  .mm-workspace { grid-template-columns: minmax(0, 1fr); row-gap: 16px; }
  .mm-gutter { display: none; }
  .mm-pane-right { position: static; }
}
.mm-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 0; }
.mm-controls-sep { width: 1px; height: 20px; background: var(--border); margin: 0 4px; }
.mm-seg { display: inline-flex; background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 2px; }
.mm-seg-opt {
  padding: 4px 12px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 12.5px;
  text-decoration: none;
}
button.mm-seg-opt { cursor: pointer; }
.mm-seg-opt.active { background: var(--bg-elev-2, var(--bg)); color: var(--text); box-shadow: 0 1px 2px rgba(0,0,0,.3); }
.mm-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px; font-size: 12.5px; border: 1px solid var(--border-strong); border-radius: 999px; color: var(--text-dim); text-decoration: none; }
.mm-chip:hover { color: var(--text); }
.mm-chip .mm-chip-dot { width: 7px; height: 7px; border-radius: 50%; border: 1px solid var(--border-strong); }
.mm-chip.on { color: var(--text); border-color: var(--accent); }
.mm-chip.on .mm-chip-dot { background: var(--accent); border-color: var(--accent); }

/* Entity stepper (‹ prev · current · next ›) in the metamodel page head. */
.mm-stepper { display: inline-flex; align-items: center; gap: 4px; }
.mm-step { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--border-strong); color: var(--text-dim); text-decoration: none; font-size: 18px; line-height: 1; }
.mm-step:hover { color: var(--text); border-color: var(--accent); }
.mm-step.is-off { opacity: .3; pointer-events: none; }
.mm-step-cur { font-size: 13px; color: var(--text); padding: 0 8px; }

/* Relationship map (Map mode) — D3 force graph of the entity neighbourhood. */
.mm-map-wrap { display: flex; flex-direction: column; gap: 8px; }
.mm-map { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); height: calc(100vh - 340px); min-height: 420px; overflow: hidden; }
.mm-map-empty { display: flex; align-items: center; justify-content: center; height: 100%; }
.mm-map-hint { text-align: center; }
/* Map classes are namespaced .mm-map-* to avoid colliding with the lifecycle state graph's .mm-g*. */
.mm-map-edge { stroke: var(--border-strong); stroke-width: 1.4px; }
.mm-map-arrow { fill: var(--border-strong); }
.mm-map-edge-label { fill: var(--text-dim); font-size: 10px; font-family: var(--font-mono); }
.mm-map-node rect { fill: var(--bg); stroke: var(--border-strong); stroke-width: 1.2px; transition: stroke 120ms var(--ease); }
.mm-map-node:hover rect { stroke: var(--accent); }
.mm-map-node.is-center rect { fill: var(--accent-soft); stroke: var(--accent); stroke-width: 1.6px; }
.mm-map-node-label { fill: var(--text); font-family: var(--font-mono); font-size: 12px; pointer-events: none; }
/* ── Metamodel ER diagram (ER mode) ────────────────────────────────────────────
   Tables with field rows; a 3px mark at the row's left says what the row is (key = accent, a
   metadata column = its group's colour). The four metadata groups get their own hue tokens here,
   so a swatch in the legend and a row in a box always agree. */
.mm-er-g-chain { --mm-er-g: var(--info); }
.mm-er-g-version { --mm-er-g: var(--danger); }
.mm-er-g-hash { --mm-er-g: var(--warn); }
.mm-er-g-provenance { --mm-er-g: var(--success); }
.mm-er-legend { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; font-size: 12.5px; min-height: 22px; }
.mm-er-legend[hidden] { display: none; }
/* One line for the visual pane's controls: the Map/ER/Off switch and the ER legend. The legend
   never wraps; the script drops its lead label and note (is-compact) or folds it behind the pill
   as a popover (is-folded) when the strip overflows. */
.mm-visual-controls { flex-wrap: nowrap; }
.mm-er-legend-wrap { position: relative; display: flex; align-items: center; min-width: 0; }
.mm-er-legend-wrap .mm-er-legend { flex-wrap: nowrap; white-space: nowrap; }
.mm-er-legend-wrap.is-compact .mm-er-legend > .text-3,
.mm-er-legend-wrap.is-compact .mm-er-semantic-text { display: none; }
.mm-er-legend-pill { cursor: pointer; }
.mm-er-legend-pill[hidden] { display: none; }
.mm-er-legend-wrap.is-folded .mm-er-legend {
  display: none;
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 6;
  flex-direction: column; align-items: flex-start; gap: 8px;
  padding: 10px 12px; white-space: nowrap;
  background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
}
.mm-er-legend-wrap.is-folded.is-open .mm-er-legend { display: flex; }
.mm-er-leg { display: inline-flex; align-items: center; gap: 6px; color: var(--text-dim); cursor: pointer; user-select: none; }
.mm-er-leg:hover { color: var(--text); }
.mm-er-leg input { margin: 0; accent-color: var(--accent); }
.mm-er-swatch { width: 10px; height: 10px; border-radius: 2px; background: var(--mm-er-g); }
.mm-er svg { display: block; }
.mm-er-frame { fill: var(--bg); stroke: var(--border-strong); stroke-width: 1.2px; transition: stroke 120ms var(--ease); }
.mm-er-box:hover .mm-er-frame { stroke: var(--accent-line); }
.mm-er-box.is-center .mm-er-frame { stroke: var(--accent); stroke-width: 1.6px; }
.mm-er-head-bg { fill: var(--bg-elev); }
.mm-er-box.is-center .mm-er-head-bg { fill: var(--accent-soft); }
.mm-er-box:not(.is-center) .mm-er-head:hover .mm-er-head-bg { fill: var(--accent-soft); }
.mm-er-title { fill: var(--text); font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; pointer-events: none; }
.mm-er-row-bg { fill: transparent; }
.mm-er-row:hover .mm-er-row-bg { fill: var(--bg-elev); }
.mm-er-row-mark { fill: transparent; }
.mm-er-row.is-pk .mm-er-row-mark { fill: var(--accent); }
.mm-er-row.is-ref .mm-er-row-mark { fill: var(--border-strong); }
.mm-er-row.is-meta .mm-er-row-mark { fill: var(--mm-er-g); }
.mm-er-name { fill: var(--text); font-family: var(--font-mono); font-size: 11.5px; pointer-events: none; }
.mm-er-row.is-optional .mm-er-name { fill: var(--text-dim); }
.mm-er-row.is-meta .mm-er-name { fill: var(--mm-er-g); opacity: .85; }
.mm-er-type { fill: var(--text-dim); font-family: var(--font-mono); font-size: 10.5px; pointer-events: none; }
.mm-er-key { fill: var(--accent); font-family: var(--font-mono); font-size: 9px; font-weight: 700; letter-spacing: .04em; pointer-events: none; }
.mm-er-edge { fill: none; stroke: var(--border-strong); stroke-width: 1.4px; }
.mm-er-edge.is-child { stroke-dasharray: 4 3; }
.mm-er-edge.is-semantic { stroke: var(--accent-line); stroke-dasharray: 8 5; }
.mm-er-box.is-virtual .mm-er-frame { stroke: var(--accent); stroke-dasharray: 6 4; }
.mm-er-box.is-virtual .mm-er-head-bg { fill: var(--accent-soft); }
.mm-er-semantic-legend { display: inline-flex; align-items: center; gap: 8px; color: var(--text-dim); }
.mm-er-semantic-legend::before { content: ""; width: 24px; border-top: 2px dashed var(--accent-line); }
.mm-er-edge:hover { stroke: var(--accent); }
.mm-er-marker { fill: var(--border-strong); }
.mm-er-marker-many { fill: none; stroke: var(--border-strong); stroke-width: 1.4px; }

.mm-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 16px; align-items: start; }
@media (max-width: 1100px) { .mm-split { grid-template-columns: 1fr; } }
.mm-tree { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); padding: 8px; max-height: calc(100vh - 320px); overflow: auto; }
.mm-tree ul { list-style: none; margin: 0; padding: 0; }
.mm-tree .mm-children { margin-left: 14px; border-left: 1px solid var(--border-subtle); padding-left: 6px; }
.mm-node > details > summary { list-style: none; }
.mm-node > details > summary::-webkit-details-marker { display: none; }
.mm-row { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 6px; cursor: pointer; user-select: none; position: relative; }
.mm-row:hover { background: var(--bg-elev-2, rgba(255,255,255,.03)); }
.mm-row.active { background: color-mix(in oklch, var(--accent) 16%, transparent); }
details > summary.mm-row::before { content: "▸"; color: var(--text-dim); font-size: 10px; width: 10px; display: inline-block; transition: transform 120ms; }
details[open] > summary.mm-row::before { transform: rotate(90deg); }
.mm-leaf { padding-left: 26px; }
.mm-ico { display: inline-flex; color: var(--text-dim); }
.mm-row.active .mm-ico { color: var(--accent); }
.mm-label { font-size: 13px; color: var(--text); font-family: var(--font-mono); }
.mm-sec { font-size: 11.5px; color: var(--text-dim); margin-left: auto; padding-left: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 45%; }

.mm-detail { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; position: sticky; top: 16px; min-height: 180px; }
.mm-detail-empty { padding: 24px 8px; text-align: center; }
.mm-d-head { display: flex; align-items: center; gap: 8px; padding-bottom: 10px; border-bottom: 1px solid var(--border-subtle); margin-bottom: 12px; }
.mm-d-title { font-size: 15px; font-weight: 600; font-family: var(--font-mono); color: var(--text); }
.mm-d-kind { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim); border: 1px solid var(--border); border-radius: 999px; padding: 1px 8px; }
/* The help mark stays beside the heading it explains; pushed to the far edge of a wide card it read
   as page chrome rather than as "what is this?" for the row. */
.mm-help-btn { width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--border-strong); background: var(--bg-elev); color: var(--text-dim); font-size: 12px; font-weight: 700; cursor: pointer; line-height: 1; }
.mm-help-btn:hover, .mm-help-btn.on { color: var(--accent); border-color: var(--accent); }
.mm-help { background: color-mix(in oklch, var(--accent) 7%, transparent); border: 1px solid color-mix(in oklch, var(--accent) 30%, transparent); border-radius: var(--radius-sm); padding: 10px 12px; margin-bottom: 12px; font-size: 12.5px; line-height: 1.55; }
.mm-help-mean { color: var(--text); }
.mm-help-how { color: var(--text-dim); margin-top: 6px; }
.mm-help-tag { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--accent); font-weight: 600; margin-right: 4px; }
/* One label rail for every key/value block on a page, so nested and top-level blocks share a left
   edge and a value edge. Auto table layout put the value column at ~55% and left a 600px gutter;
   a fixed label column removes both the gutter and the wobble. */
.mm-props { width: 100%; border-collapse: collapse; margin-bottom: 12px; table-layout: fixed; }
.mm-props .mm-prop-k { width: var(--mm-label-w, 190px); }
.mm-props td { padding: 4px 6px; font-size: 12.5px; vertical-align: top; border-bottom: 1px solid var(--border-subtle); }
.mm-prop-k { color: var(--text-dim); width: 38%; white-space: nowrap; }
.mm-prop-v { color: var(--text); font-family: var(--font-mono); word-break: break-word; }

/* ── Metamodel inspector · type-specific detail visualizations ── */
.mm-detail :focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.mm-row[tabindex] { scroll-margin: 8px; }
.mm-row:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }

/* enum pills */
.mm-pills { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.mm-pill { display: inline-flex; align-items: baseline; gap: 6px; padding: 4px 10px; border-radius: 999px; background: color-mix(in oklch, var(--accent) 12%, transparent); border: 1px solid color-mix(in oklch, var(--accent) 35%, transparent); }
.mm-pill-name { font-family: var(--font-mono); font-size: 12.5px; color: var(--text); font-weight: 600; }
.mm-pill-sub { font-size: 11px; color: var(--text-dim); }

/* lifecycle flow */
.mm-flow { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.mm-flow-row { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--radius-sm); background: var(--bg); border: 1px solid var(--border-subtle); cursor: pointer; }
.mm-flow-row:hover { border-color: var(--border-strong); }
.mm-flow-big { cursor: default; font-size: 14px; padding: 10px 12px; }
.mm-state { font-family: var(--font-mono); font-size: 12px; padding: 2px 8px; border-radius: 5px; background: var(--bg-elev-2, var(--bg-elev)); border: 1px solid var(--border); color: var(--text); }
.mm-arrow { color: var(--accent); font-weight: 700; }
.mm-trans { margin-left: auto; font-size: 11.5px; color: var(--text-dim); font-family: var(--font-mono); }

/* invariant rule card */
.mm-rule { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; margin-bottom: 10px; border-radius: var(--radius-sm); background: var(--bg); border: 1px solid var(--border-subtle); border-left: 3px solid var(--accent); }
.mm-rule-line { display: flex; align-items: baseline; gap: 8px; }
.mm-rule-kw { font-size: 10px; font-weight: 700; letter-spacing: .06em; color: var(--accent); min-width: 42px; }
.mm-rule-then { color: var(--text-dim); }
.mm-rule code, .mm-doc code { font-family: var(--font-mono); font-size: 12px; color: var(--text); background: var(--bg-elev); padding: 1px 5px; border-radius: 4px; }

/* chip rows (fields, operations) */
.mm-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 10px; }
.mm-type-badge { font-family: var(--font-mono); font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 6px; background: color-mix(in oklch, var(--accent) 14%, transparent); color: var(--text); border: 1px solid color-mix(in oklch, var(--accent) 30%, transparent); }
.mm-flag { font-size: 11px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border-strong); color: var(--text-dim); }
.mm-flag.sm { font-size: 10px; padding: 1px 6px; }
.mm-flag.req { color: var(--accent); border-color: color-mix(in oklch, var(--accent) 40%, transparent); }
.mm-flag.pk { color: #e8b84b; border-color: color-mix(in oklch, #e8b84b 45%, transparent); }
.mm-flag.tag { font-family: var(--font-mono); }
.mm-flag.gate { color: #e0795a; border-color: color-mix(in oklch, #e0795a 40%, transparent); }
.mm-doc { font-size: 12px; color: var(--text-dim); line-height: 1.55; margin: 4px 0 8px; }
.mm-doc-k { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--accent); font-weight: 600; margin-right: 6px; }

/* Related-list ("has many") relationship statement in the detail pane. */
.mm-rel { margin-bottom: 14px; }
.mm-rel-lead { font-size: 13px; color: var(--text); line-height: 1.5; margin-bottom: 12px; }
.mm-rel-graph { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.mm-rel-end { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.mm-rel-ent { font-family: var(--font-mono); font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 6px; background: color-mix(in oklch, var(--accent) 12%, transparent); color: var(--text); border: 1px solid color-mix(in oklch, var(--accent) 30%, transparent); }
.mm-rel-card { font-size: 9.5px; text-transform: uppercase; letter-spacing: .1em; color: var(--text-dim); }
.mm-rel-conn { font-family: var(--font-mono); font-size: 16px; color: var(--accent); letter-spacing: -1px; }
.mm-rel-via { font-size: 12px; color: var(--text-dim); }
.mm-rel-via code { font-family: var(--font-mono); color: var(--text); }

/* Capability grid: the whole set, with the ones this interpretation does not serve dimmed rather
   than omitted — the absence is the point ("why has this entity no log?"). */
.mm-caps { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.mm-cap { display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 11px; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--border-subtle); color: var(--text-dim); }
.mm-cap.on { color: var(--text); border-color: color-mix(in oklch, var(--accent) 35%, transparent); background: color-mix(in oklch, var(--accent) 10%, transparent); }
.mm-cap.off { opacity: .5; text-decoration: line-through; text-decoration-color: color-mix(in oklch, var(--text-dim) 60%, transparent); }
.mm-cap-mark { font-size: 10px; }
.mm-cap.on .mm-cap-mark { color: var(--accent); }

/* Datasheet: the model down the page, one card per aspect, fields as a real table. */
.mm-sheet { display: flex; flex-direction: column; gap: var(--d-gap, 16px); }
.mm-sheet-card { padding: 0 0 14px; }
.mm-sheet-card > .card-head { padding: 12px 16px; }
.mm-sheet-card > .mm-props, .mm-sheet-card > .tbl-wrap { margin: 0 16px; width: calc(100% - 32px); }
.mm-sheet { --mm-label-w: 200px; }
.mm-sheet-items { display: flex; flex-direction: column; padding: 0 16px; }
/* Aspects inside a section are sub-sections, not cards. Boxes inside boxes doubled the border and
   padding vocabulary and pushed their contents off the page's label rail. */
.mm-sheet-item { padding: 14px 0; border-top: 1px solid var(--border-subtle); }
.mm-sheet-item:first-child { border-top: 0; padding-top: 4px; }
.mm-sheet-item .mm-d-head { margin-bottom: 10px; }
/* Sheet card heads keep their count ("7 fields", "3") next to the title, on the same left rail as
   the rows beneath, instead of the generic card-head's space-between that strands it at the right. */
.mm-sheet-card > .card-head { justify-content: flex-start; }
.mm-sheet-item .mm-props, .mm-sheet-item .mm-props:last-child { margin-bottom: 0; }
/* The section heading already says what kind these are; repeating it per item is noise here (the
   tree's detail pane keeps it, where there is no section to say it). */
.mm-sheet .mm-d-kind { display: none; }
.mm-sheet-rows { display: flex; flex-direction: column; padding: 0 16px; }
.mm-sheet-row { border-bottom: 1px solid var(--border-subtle); }
.mm-sheet-row:last-child { border-bottom: 0; }
.mm-sheet-sum { display: flex; align-items: center; gap: 9px; padding: 7px 0; cursor: pointer; list-style: none; font-size: 12.5px; }
.mm-sheet-sum::-webkit-details-marker { display: none; }
.mm-sheet-sum:hover .mm-sheet-sum-label { color: var(--accent); }
.mm-sheet-sum-label { font-family: var(--font-mono); color: var(--text); }
.mm-sheet-sum-kind { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-dim); }
/* Name + kind occupy the same label rail as every key/value block on the page, so the row's value
   (execution model, state field, layout name) lands on the value edge instead of drifting to the
   far right of a wide card, where it read as unrelated to its row. */
.mm-sheet-sum-head, .mm-content-head { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; min-width: var(--mm-label-w, 190px); }
.mm-sheet-sum-sec { font-family: var(--font-mono); font-size: 11.5px; color: var(--text-dim); min-width: 0; overflow-wrap: anywhere; }
/* No horizontal padding: an expanded row's contents belong on the same label rail as everything
   else on the page, not indented four pixels off it. */
.mm-sheet-rowbody { padding: 4px 0 14px; }
.mm-fieldtbl { width: 100%; border-collapse: collapse; font-size: 12.5px; table-layout: fixed; }
.mm-fieldtbl .mm-ft-c-name { width: var(--mm-label-w, 200px); }
.mm-fieldtbl .mm-ft-c-type { width: 230px; }
.mm-fieldtbl .mm-ft-c-flags { width: 130px; }
.mm-fieldtbl .mm-ft-c-rel { width: 48px; }
.mm-fieldtbl .mm-ft-c-rel abbr { text-decoration: none; cursor: help; border-bottom: 1px dotted currentColor; }
.mm-fieldtbl th { text-align: left; font-size: 9.5px; text-transform: uppercase; letter-spacing: .1em; color: var(--text-dim); font-weight: 600; padding: 6px 8px; border-bottom: 1px solid var(--border-strong); white-space: nowrap; }
.mm-fieldtbl td { padding: 7px 8px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; }
.mm-fieldtbl tr:last-child td { border-bottom: 0; }
.mm-fieldtbl tbody tr:hover { background: color-mix(in oklch, var(--accent) 5%, transparent); }
/* Enum fields: the type is a disclosure that opens the symbol list as a row under the field. */
.mm-ft-enum-toggle { appearance: none; background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; max-width: 100%; vertical-align: bottom; }
.mm-ft-enum-toggle:hover, .mm-ft-enum-toggle[aria-expanded="true"] { color: var(--accent); }
.mm-ft-enum-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.mm-ft-enum-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mm-ft-enum-chev { flex: 0 0 6px; width: 6px; height: 6px; margin-top: -3px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(45deg); transition: transform 120ms var(--ease); }
.mm-ft-enum-toggle[aria-expanded="true"] .mm-ft-enum-chev { margin-top: 3px; transform: rotate(-135deg); }
.mm-ft-enum-row[hidden] { display: none; }
.mm-fieldtbl .mm-ft-enum-row td { padding: 2px 8px; border-bottom: 0; font-size: 12px; line-height: 1.5; background: color-mix(in oklch, var(--accent) 4%, transparent); }
.mm-fieldtbl .mm-ft-enum-row.is-first td { padding-top: 6px; }
.mm-fieldtbl .mm-ft-enum-row:has(+ tr:not(.mm-ft-enum-row)) td, .mm-fieldtbl .mm-ft-enum-row:last-child td { padding-bottom: 8px; border-bottom: 1px solid var(--border-subtle); }
.mm-fieldtbl tbody tr.mm-ft-enum-row:hover { background: transparent; }
.mm-ft-sym-code { color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mm-ft-sym-name { color: var(--text-muted); }
.mm-ft-sym-doc { color: var(--text-dim); }
/* NOT flex: display:flex on a td takes the cell out of the table layout, so its border-bottom sits
   at its own height instead of the row's and the rules across a row stagger. Inline children with
   a shared baseline keep the cell a real table-cell. */
/* Fixed columns need explicit truncation, or a long type (enum<data_slice3_status>) or a reference
   overruns into the next column instead of stopping at its edge. Full value on hover. */
.mm-ft-name, .mm-ft-type { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mm-ft-name > * { vertical-align: middle; }
.mm-ft-name .mm-ico { margin-right: 7px; }
.mm-ft-flags .mm-flag { vertical-align: middle; margin-right: 4px; }
.mm-ft-ref { font-size: 11px; color: var(--accent); text-decoration: none; margin-left: 7px; }
.mm-ft-ref:hover { text-decoration: underline; }
/* Clamped: one long field doc otherwise sets the height of every row around it. Full text on
   hover, and the Source view has it verbatim. */
.mm-ft-doc { line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* Change relevance: what makes an event vs what the diff throws away, then the churn headline. */
.mm-rel-split { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; margin-bottom: 14px; }
.mm-rel-col { min-width: 0; }
.mm-rel-cap { font-size: 9.5px; text-transform: uppercase; letter-spacing: .1em; color: var(--text-dim); margin-bottom: 6px; }
.mm-rel-col .mm-chips { margin-bottom: 0; }
.mm-churn { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; margin-bottom: 14px; padding: 10px 12px; border-radius: 8px; border: 1px solid var(--border-subtle); background: var(--bg); }
.mm-churn-n { font-family: var(--font-mono); font-size: 20px; font-weight: 600; color: var(--text); }
.mm-churn-k { font-size: 12px; color: var(--text-dim); }
.mm-churn-sub { font-size: 11.5px; color: var(--text-dim); margin-left: auto; }

/* API method: the request it is, and the way onto the surface that can send it. */
.mm-req { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 12px; }
.mm-req-line { font-family: var(--font-mono); font-size: 12.5px; padding: 5px 10px; border-radius: 6px; background: color-mix(in oklch, var(--accent) 12%, transparent); border: 1px solid color-mix(in oklch, var(--accent) 30%, transparent); color: var(--text); overflow-wrap: anywhere; }
.mm-req-try { font-size: 12px; color: var(--accent); text-decoration: none; white-space: nowrap; }
.mm-req-try:hover { text-decoration: underline; }

/* Schedule: the expression next to when it actually fires next. */
.mm-sched { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-bottom: 14px; }
.mm-sched-cron { font-family: var(--font-mono); font-size: 13px; padding: 4px 10px; border-radius: 6px; background: color-mix(in oklch, var(--accent) 12%, transparent); border: 1px solid color-mix(in oklch, var(--accent) 30%, transparent); color: var(--text); }
.mm-sched-next { display: flex; align-items: center; gap: 6px; }
.mm-sched-k { font-size: 9.5px; text-transform: uppercase; letter-spacing: .1em; color: var(--text-dim); }
.mm-sched-t { font-family: var(--font-mono); font-size: 12px; color: var(--text-dim); padding: 2px 7px; border-radius: 5px; border: 1px solid var(--border-subtle); }
.mm-sched-t:first-of-type { color: var(--text); border-color: var(--border-strong); }

/* operation param list */
.mm-paramlist { display: flex; flex-direction: column; gap: 4px; margin-top: 2px; }
.mm-param-row { display: flex; flex-direction: column; align-items: stretch; gap: 4px; padding: 6px 9px; border-radius: 6px; background: var(--bg); border: 1px solid var(--border-subtle); cursor: pointer; }
.mm-param-row:hover { border-color: var(--border-strong); }
.mm-param-head { display: flex; align-items: center; gap: 10px; }
.mm-param-head .mm-flag { margin-left: auto; }
.mm-param-name { font-family: var(--font-mono); font-size: 12.5px; color: var(--text); }
.mm-param-type { font-size: 11.5px; color: var(--text-dim); font-family: var(--font-mono); }
.mm-param-doc { font-size: 11.5px; color: var(--text-dim); line-height: 1.5; }
.mm-rule-code { font-family: var(--font-mono); font-size: 12px; color: var(--text); }
/* Section header inside the operation detail pane (Parameters / Rules / Side effects). */
.mm-sec-h { font-size: 10px; text-transform: uppercase; letter-spacing: .08em; color: var(--text-dim); font-weight: 600; margin: 14px 0 6px; }
.mm-opdoc { font-size: 12.5px; color: var(--text); line-height: 1.55; margin: 2px 0 10px; }

/* children index (parent nodes) */
.mm-contents { display: flex; flex-direction: column; gap: 3px; }
.mm-content-row { display: flex; align-items: flex-start; gap: 8px; padding: 6px 8px; border-radius: 6px; cursor: pointer; }
.mm-content-row:hover { background: var(--bg-elev-2, rgba(255,255,255,.03)); }
.mm-content-row .mm-ico { color: var(--text-dim); display: inline-flex; }
.mm-content-label { font-family: var(--font-mono); font-size: 12.5px; color: var(--text); }
.mm-content-kind { font-size: 10px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim); border: 1px solid var(--border); border-radius: 999px; padding: 0 7px; }
/* Wrap (don't clip) the child's secondary — e.g. a detail-section's full field list — so nothing is
   hidden. Short secondaries (a field's type) still sit on one line; long ones wrap right-aligned. */
.mm-content-sec { font-size: 11px; color: var(--text-dim); white-space: normal; overflow-wrap: anywhere; min-width: 0; }
.mm-content-label, .mm-content-kind { line-height: 1.5; }

/* collapsible source */
/* Raw source YAML per node: hidden unless the page-level "Source YAML" toggle (.mm-show-yaml) is on. */
.mm-source { display: none; margin-top: 14px; border-top: 1px solid var(--border-subtle); padding-top: 10px; }
.mm-show-yaml .mm-source { display: block; }
.mm-source-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.mm-source-head .btn.icon { width: auto; padding: 0 9px; height: 26px; gap: 5px; font-size: 11.5px; }
.mm-source-sum { font-size: 11.5px; color: var(--text-dim); }

/* ── Metamodel inspector · lifecycle state graph ── */
.mm-graph { position: relative; height: 168px; margin-bottom: 14px; }
.mm-gsvg { position: absolute; inset: 0; overflow: visible; color: var(--accent); z-index: 0; pointer-events: none; }
.mm-gstates { position: absolute; top: 50%; left: 0; right: 0; transform: translateY(-50%); display: flex; justify-content: space-around; align-items: center; gap: 8px; padding: 0 14px; z-index: 1; }
.mm-gstate { font-family: var(--font-mono); font-size: 12px; font-weight: 600; padding: 6px 12px; border-radius: 8px; background: var(--bg-elev-2, var(--bg-elev)); border: 1px solid var(--border-strong); color: var(--text); white-space: nowrap; box-shadow: 0 1px 3px rgba(0,0,0,.3); }
.mm-gedge { fill: none; stroke: color-mix(in oklch, var(--accent) 55%, transparent); stroke-width: 1.6; }
.mm-glabel { fill: var(--text-dim); font-size: 10.5px; font-family: var(--font-mono); paint-order: stroke; stroke: var(--bg-elev); stroke-width: 3px; stroke-linejoin: round; }

/* ── Metamodel inspector · cross-entity navigation ── */
.mm-crumbs { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12.5px; align-self: flex-start; }
.mm-crumb { color: var(--text-dim); text-decoration: none; font-family: var(--font-mono); }
.mm-crumb:hover { color: var(--text); }
.mm-crumb-cur { color: var(--text); font-weight: 600; }
.mm-crumb-sep { color: var(--text-dim); opacity: .6; }
.mm-refnav { display: flex; align-items: center; gap: 8px; padding: 8px 10px; margin-bottom: 12px; border-radius: var(--radius-sm); background: color-mix(in oklch, var(--accent) 9%, transparent); border: 1px solid color-mix(in oklch, var(--accent) 28%, transparent); }
.mm-ref-arrow { color: var(--accent); font-weight: 700; }
.mm-ref-link { font-family: var(--font-mono); font-size: 13px; font-weight: 600; color: var(--accent); background: none; border: none; padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.mm-ref-link:hover { color: var(--text); }
.mm-peek-pop { position: absolute; z-index: 1000; max-width: 280px; padding: 8px 11px; border-radius: var(--radius-sm); background: var(--bg-elev-2, var(--bg-elev)); border: 1px solid var(--border-strong); box-shadow: 0 6px 22px rgba(0,0,0,.4); pointer-events: none; }
.mm-peek-name { font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; color: var(--text); margin-bottom: 2px; }
.mm-peek-sum { font-size: 11.5px; color: var(--text-dim); }

/* render-hint chips on field detail */
.mm-flag.render { color: var(--text); border-color: var(--border); opacity: .85; }

/* ── Metamodel index (entity picker) ── */
.mm-index-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; margin-top: 16px; }
.mm-index-card { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: var(--radius); background: var(--bg-elev); border: 1px solid var(--border); text-decoration: none; transition: border-color 120ms; }
.mm-index-card:hover { border-color: var(--accent); }
.mm-index-ico { color: var(--text-dim); display: inline-flex; }
.mm-index-card:hover .mm-index-ico { color: var(--accent); }
.mm-index-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.mm-index-name { font-family: var(--font-mono); font-size: 13px; color: var(--text); font-weight: 600; }
.mm-index-sum { font-size: 11.5px; color: var(--text-dim); }
.mm-index-arrow { color: var(--text-dim); margin-left: auto; }
.mm-index-card:hover .mm-index-arrow { color: var(--accent); }

/* api-docs parameter hint pill — hover explains the parameter's mechanism */
.param-hint {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-left: 5px; vertical-align: 1px;
  border: 1px solid var(--border); border-radius: 50%;
  color: var(--text-muted); font-size: 9px; font-weight: 600; cursor: help;
}
.param-hint:hover { color: var(--accent); border-color: var(--accent); }

/* try-it input flash when a value was auto-filled from the last response */
.epc-tryit-param { transition: border-color 120ms var(--ease), box-shadow 0.5s ease; }
.epc-tryit-param.epc-autofilled { box-shadow: 0 0 0 1px var(--accent); }

/* try-it 304: cached body rendered ghost-style to signal it is cache-served, not freshly fetched */
.epc-ghost { opacity: 0.55; filter: saturate(0.5); }
.ghost-note {
  margin-bottom: 10px; padding: 8px 10px;
  border-left: 2px solid var(--accent); border-radius: 4px;
  background: color-mix(in oklab, var(--accent) 8%, transparent);
  color: var(--text-muted); font-size: 12px;
}

/* ── Usage cascade (docs/design/usage-consumption-views.md §11) ── */
.usage-crumb { display: flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: 12px; flex-wrap: wrap; }
.usage-crumb a { color: var(--text-muted); text-decoration: none; }
.usage-crumb a:hover { color: var(--accent); }
.usage-crumb .sep { color: var(--text-dim); }
.usage-crumb .here { color: var(--text); font-weight: 600; }

/* Operator account-filter (typeahead over account3) in the page-header actions — a compact,
   margin-free host for the entity_ref_picker so it sits inline beside the period selector. */
.usage-acct-picker { display: inline-flex; min-width: 210px; }
.usage-acct-picker .form-field { margin: 0; width: 100%; }

/* ============================================================
   Console2 › Streams — live SSE ticker (/console2/streams)
   ============================================================ */
.ep-c2-stream-ticker { font-family: var(--font-mono); font-size: 12px; line-height: 1.6; background: var(--bg); border-top: 1px solid var(--border); border-radius: 0 0 var(--radius) var(--radius); height: 520px; overflow-y: auto; padding: 6px 0; }
.ep-c2-stream-row { display: flex; align-items: baseline; gap: 12px; padding: 3px 16px; cursor: pointer; white-space: nowrap; border-left: 2px solid transparent; }
.ep-c2-stream-row:hover { background: var(--bg-hover); border-left-color: var(--accent-line); }
.ep-c2-stream-eid { color: var(--accent); min-width: 72px; }
.ep-c2-stream-ver { color: var(--text-dim); min-width: 36px; }
.ep-c2-stream-ts { color: var(--text-muted); min-width: 96px; }
.ep-c2-stream-pk { color: var(--text); overflow: hidden; text-overflow: ellipsis; }
.ep-c2-stream-json { display: none; margin: 2px 16px 6px 46px; padding: 10px 12px; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-sm); white-space: pre-wrap; word-break: break-all; }
.ep-c2-stream-json.open { display: block; }
.ep-c2-stream-json .k { color: var(--json-key); }
.ep-c2-stream-json .s { color: var(--json-str); }
.ep-c2-stream-json .n { color: var(--json-num); }
.ep-c2-stream-json .l { color: var(--json-lit); }
.ep-c2-stream-note { margin: 6px 16px; padding: 6px 10px; border: 1px dashed var(--border-strong); border-radius: var(--radius-sm); color: var(--text-muted); }
.ep-c2-stream-note.warn { border-color: var(--warn); color: var(--warn); }
.ep-c2-stream-note.err { border-color: var(--danger); color: var(--danger); }

/* The mapper's cells are all single-line since its timestamps went relative-only, but the row is
   still as tall as its tallest control (the 27px bulk-select cell, the 20px status chip). Against
   the table's default `vertical-align: top` that leaves the 15px text sitting ~4px above centre —
   visible as soon as nothing fills the lower half of the row any more.

   Scoped to this entity on purpose. Grids that still stack a second line under the display name
   (`stackWith:`) want top alignment, so their primary line stays level with the single-line cells
   beside it; centring those would float the pair against its neighbours. Lift this to `.tbl td`
   the day the last stacked cell goes. */
.epc-entity--data_slice_curation3 .tbl td {
  vertical-align: middle;
}

/* Checkboxes ---------------------------------------------------------------
   Retrace's own control, not the browser's. The native box paints a hard white
   square that, on a dark grid, is the brightest thing on screen — for something
   most rows never use. This is the "ghost" candidate from the bake-off: no
   border until you approach it, a dim fill at rest, accent only once it means
   something.

   Applies to every checkbox in the console, which is the point — a control that
   looks like this in the grid and like the OS default in a filter panel is worse
   than either. Radios are deliberately untouched (different affordance, and they
   still read correctly as native). The toggle-switch's input is excluded because
   it is visually hidden by design and draws its own track and thumb. */
input[type="checkbox"]:not(.toggle-switch-input) {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 14px;
  height: 14px;
  margin: 0;
  padding: 0;
  flex: 0 0 14px;
  border: 1px solid transparent;
  border-radius: 3px;
  background: color-mix(in srgb, var(--text-dim) 22%, transparent);
  cursor: pointer;
  transition: background-color 120ms var(--ease), border-color 120ms var(--ease);
}
input[type="checkbox"]:not(.toggle-switch-input):hover {
  border-color: var(--text-dim);
}
input[type="checkbox"]:not(.toggle-switch-input):checked,
input[type="checkbox"]:not(.toggle-switch-input):indeterminate {
  background: color-mix(in srgb, var(--accent) 28%, transparent);
  border-color: var(--accent);
}
input[type="checkbox"]:not(.toggle-switch-input):checked::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 1px;
  background: var(--accent);
}
/* Partial selection. `appearance: none` also removes the native indeterminate
   dash, so a half-selected group would otherwise look identical to an empty one
   — the one state where the checkbox is carrying real information. */
input[type="checkbox"]:not(.toggle-switch-input):indeterminate::after {
  content: "";
  position: absolute;
  left: 3px;
  right: 3px;
  top: 50%;
  height: 2px;
  margin-top: -1px;
  border-radius: 1px;
  background: var(--accent);
}
input[type="checkbox"]:not(.toggle-switch-input):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
input[type="checkbox"]:not(.toggle-switch-input):disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Row checkboxes indent under their band heading, so the column shows the
   nesting instead of running dead straight. Grouped grids only — a flat grid has
   nothing to nest under. */
.tbl-wrap:has(.grid-band) tbody tr:not(.grid-band) td:first-child .epc-bulk-cell {
  justify-content: flex-start;
  /* 11px, not an arbitrary indent: it is the value that lands the column's dashed rule exactly on
     the centre of the band's caret, so the nesting line continues the disclosure it belongs to
     instead of running parallel to it 11px away. Measured against the rendered caret — if the
     caret's size or the band's gap changes, this has to be re-measured, not guessed. */
  padding-left: 11px;
}

/* "6 open, none selected" — the selection state reads as part of the summary
   phrase rather than as a separate chip, so a band answers "how much is left"
   and "how much have I picked" in one line. Dim at zero: it is a null state, and
   accenting every unselected band would light up the whole table. */
/* Pulled back over `.grid-band__inner`'s 9px flex gap so the comma sits against the summary it
   continues — "6 open, 2 selected", not "6 open , 2 selected". A sibling rather than a child of
   `.grid-band__summaries` because that element is rebuilt server-side on every band render. */
.tbl .grid-band__selected { margin-left: -9px; color: var(--text-dim); font-weight: 500; }
.tbl .grid-band__selected .num {
  color: var(--accent);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* Grouped grids: hairline column separators inside a band's rows.
   A band's children are a small table inside a bigger one, and with the row rules alone the eye has
   nothing to run down — values in the middle columns float. Dotted rather than solid, and only
   between the child rows, so the structure reads as guidance and never competes with the band
   headings that divide the actual groups. The last column is skipped: a rule against the card's
   own edge reads as a border that lost its table. */
.tbl-wrap:has(.grid-band) tbody tr:not(.grid-band) td:not(:last-child) {
  /* Dashed verticals at full strength, no horizontals: settled by looking at the combinations
     rather than by argument. The columns carry the structure; a horizontal rule as well turned the
     nested area into a grid of boxes. */
  border-right: 1px dashed var(--border-strong, var(--border));
}
/* No horizontal rule between a band's child rows — the verticals plus the row backgrounds are
   enough, and both axes together boxed every cell in. */
.tbl-wrap:has(.grid-band) tbody tr:not(.grid-band):not(:last-child) td {
  border-bottom: none;
}

/* Info card: a details/summary disclosure in a grid cell, following the Access tab's .net-ents
   popover — zero JS, keyboard-reachable, closes on Escape. Click rather than hover, because a
   hover-only card cannot be reached on touch. */
.epc-info-cell { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.epc-info { position: relative; display: inline-flex; flex: 0 0 auto; }
.epc-info > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border: 1px solid var(--border-strong, var(--border));
  border-radius: 50%;
  color: var(--text-dim);
  cursor: pointer;
  list-style: none;
  font-family: var(--font-body);
  font-size: 9px;
  font-style: italic;
  font-weight: 700;
  line-height: 1;
  /* The row is a click target of its own (it opens the review sheet); without this the icon opens
     the sheet as well as the card. */
  user-select: none;
}
.epc-info > summary::-webkit-details-marker { display: none; }
.epc-info > summary:hover,
.epc-info[open] > summary { border-color: var(--accent); color: var(--accent); }
.epc-info > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.epc-info-pop {
  position: absolute;
  left: 0;
  top: calc(100% + 6px);
  z-index: 60;
  min-width: 300px;
  max-width: 440px;
  /* Banded: the padding lives on the rows, not the box, so each term/definition pair gets its own
     faint band. Four paragraph-length definitions in a flat grid read as a wall — this is what
     makes them scannable. */
  padding: 14px 0 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elev-2);
  box-shadow: var(--shadow-card);
  white-space: normal;
  text-align: left;
  cursor: default;
}
.epc-info-title {
  margin: 0 16px 8px;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
}
.epc-info-note { margin: 0 16px 12px; color: var(--text-2); font-size: 11.5px; line-height: 1.55; }
.epc-info-facts { display: block; margin: 0; font-size: 11.5px; }
.epc-info-facts dt {
  padding: 9px 16px 0;
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
}
.epc-info-facts dd {
  margin: 0;
  padding: 2px 16px 9px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  color: var(--text-2);
  line-height: 1.5;
}
.epc-info-facts dd:last-child { border-bottom: 0; }
/* Beside a page title: same control, sized to sit with an h1 rather than inside a grid row. */
.page-head-title { display: flex; align-items: center; gap: 9px; min-width: 0; }
.epc-info--page > summary { width: 16px; height: 16px; font-size: 10px; }

/* The state a card takes while epc-helpers.js has it parked on <body>. It has to leave the grid
   entirely: the wrapper's `overflow: clip` — the rule that gives grouped grids sticky headings
   without a scroll container — clips even fixed-position descendants, so no amount of z-index or
   repositioning inside the table could make a band's card visible. Placed by JS, hence no offsets
   here. */
.epc-info-pop.epc-info-pop--fixed { position: fixed; }

/* A right-aligned cell would throw the card off the right edge of the viewport. */
.tbl td[style*="right"] .epc-info-pop, .tbl td:last-child .epc-info-pop { left: auto; right: 0; }

/* Customer Explorer — Operator / Customer toggle + protocol pills (2026-08-25) */
.explorer-view { display: flex; align-items: center; gap: 12px; margin: 6px 0 10px; }
.explorer-view .seg { display: inline-flex; gap: 4px; }
/* Author display beats the UA [hidden] rule — the per-component idiom, so the pills really hide. */
.explorer-view .seg[hidden] { display: none; }
/* Customer view on: the data filters are inert (they are the operator view's question). */
.explorer-filters.is-inert { opacity: .45; pointer-events: none; }
.explorer-view .btn[aria-pressed="true"] { background: var(--accent-soft, var(--bg-hover)); color: var(--accent-ink, var(--text)); border-color: var(--accent, var(--border)); }
.explorer-view .btn[disabled] { opacity: .5; cursor: not-allowed; }
