/* =========================================================================
 * Manifold default theme — neutral, instrument-grade aesthetic.
 *
 * Brand-neutral by design. Karl's animated Erratic theme lives at
 * themes/erratic/ for users who prefer that look; this is the default
 * for fresh installs and for users without a saved preference.
 *
 * IBM Plex Sans + Plex Mono. Deep slate background with warm amber
 * accent — distinct from the cyan-glassmorphism look the Erratic theme
 * has, and explicitly avoiding colors common in generic dashboards.
 *
 * card-builder.jsx and charts.js read theme tokens via window-level
 * shims (window.ManifoldChartsTheme, etc.); see manifold.jsx for the
 * theme bootstrap that wires this CSS to those objects.
 * ========================================================================= */

:root[data-theme="default"] {
  /* --- Surfaces --- */
  --m-bg:           #0d0f12;
  --m-surface:      #15181d;
  --m-surface-2:    #1d2128;
  --m-surface-hi:   #232831;
  --m-border:       #252a32;
  --m-border-hi:    #353c47;

  /* --- Text --- */
  --m-text:         #e8e6e3;
  --m-text-muted:   #8b8985;
  --m-text-faint:   #82817e;

  /* --- Accent --- */
  --m-accent:       #f5a623;
  --m-accent-hi:    #ffb84a;
  --m-accent-low:   rgba(245, 166, 35, 0.12);
  --m-accent-line:  rgba(245, 166, 35, 0.32);

  /* --- Semantic state colors --- */
  --m-success:      #6e9b65;
  --m-warning:      #d2945e;
  --m-error:        #d16b62;

  /* --- Type --- */

  /* --- Layout: emitted by codegen — see @manifold-tokens block below */

  /* --- Header --- */
  --m-header-row-height: 52px;

  /* --- Motion --- */
  --m-t-fast: 120ms cubic-bezier(0.2, 0, 0, 1);
  --m-t-norm: 220ms cubic-bezier(0.2, 0, 0, 1);
  --m-t-slow: 400ms cubic-bezier(0.2, 0, 0, 1);

  /* --- JS-token aliases (--mt-*) emitted by codegen --------------------
   * Phase 15 closed CR-01: the 14 canonical --mt-* names (bg, bg-deep,
   * bg-elev, border, border-strong, fg, dim, muted, accent, accent-muted,
   * live, warn, error, code) are now emitted from design-tokens/<id>.json +
   * _base.json into the @manifold-tokens block below. The hand-authored
   * var(--m-*) bridge layer that used to live here is retired —
   * tests/plugin_spec/test_design_tokens.py::
   *   test_no_codegen_vars_declared_outside_sentinel
   * hard-fails if any of those 14 names is re-declared up here.
   * --m-* legacy names ABOVE remain (they feed older callers via var()).
   * --------------------------------------------------------------------- */

  /* Phase 5: warn-hi alias for the easyPie intermediate color stop.
     Sits between --mt-warn and --mt-error in the semantic warmth ramp.
     Used by the easyPie color ramp in card-builder-renderers.jsx. */
  --mt-warn-hi:        #ff7a3a;

  /* Phase 5: multi-series categorical palette (Tableau 10 + 2 supplemental).
     Drives multiSeriesChart, multiBigNumber, rankedBars, donut, eventBadges
     via the i % 12 cycle. WCAG AA 3:1 enforced against the chart background
     by tools/audit_series_contrast.py. */
  --mt-series-1:       #4E79A7;
  --mt-series-2:       #F28E2B;
  --mt-series-3:       #E15759;
  --mt-series-4:       #76B7B2;
  --mt-series-5:       #59A14F;
  --mt-series-6:       #EDC948;
  --mt-series-7:       #B07AA1;
  --mt-series-8:       #FF9DA7;
  --mt-series-9:       #9C755F;
  --mt-series-10:      #BAB0AC;
  --mt-series-11:      #7B5EA7;
  --mt-series-12:      #46787C;

  /* Phase 5: sequential heatmap ramp (Viridis-derived perceptual sequence).
     Drives HeatmapRenderer default color stops. Per-theme overrides land
     here when an intensity reading misreads against the theme. */
  --mt-heat-1:         #2C3E50;
  --mt-heat-2:         #3D6F8E;
  --mt-heat-3:         #7C9885;
  --mt-heat-4:         #D9A26B;
  --mt-heat-5:         #E55934;

  /* Phase 122 hover token set (THLEG-02) — routes :hover rules off raw hex.
     Placed above the sentinel so build_tokens.py regeneration leaves them intact.
     --mt-hover-bg must clear 3:1 WCAG contrast vs --mt-bg (gate: tools/audit_theme_legibility.py).
     Future user themes must define all three tokens.
       --mt-hover-bg:     hover surface background
       --mt-hover-fg:     hover foreground / text color
       --mt-hover-border: hover border accent color */
  --mt-hover-bg:     #607888;        /* steel-slate lift — 3.85:1 vs #15181d */
  --mt-hover-fg:     #e8e6e3;        /* main text — stays readable on hover surface */
  --mt-hover-border: rgba(245, 166, 35, 0.53); /* amber accent-line (#f5a62388 decoded) */

  /* LOOK-02 (Phase 144, D-09): shared tabular row-text size token.
     ListRenderer JS reads this at render time as the single source of truth
     for the default row font-size on all list/recents cards (Sonarr, Lidarr,
     Radarr, Plex, and any future list-kind card). 12px matches the Phase-140
     'sm' default so existing default-sized cards do not shift.
     Placed outside @manifold-tokens sentinel — hand-authored, not regenerated
     by build_tokens.py. Only the list/recents renderer path reads this token;
     the existing monospace font consumers are NOT affected (D-09 scope guard). */
  --m-text-row: 12px;

  /* @manifold-tokens:start */
  --mt-accent: #f5a623;
  --mt-accent-muted: #8e691b;
  --mt-bg: #15181d;
  --mt-bg-deep: #0d0f12;
  --mt-bg-elev: #1d2128;
  --mt-black: #000000;
  --mt-blue: #3b82c4;
  --mt-border: #252a32;
  --mt-border-strong: #353c47;
  --mt-btn-primary-fg: #0d0f12;
  --mt-code: #f5a623;
  --mt-dim: #8b8985;
  --mt-error: #d16b62;
  --mt-fg: #e8e6e3;
  --mt-green: #3f9d55;
  --mt-grey: #808080;
  --mt-info: #6a9fd4;
  --mt-live: #6e9b65;
  --mt-muted: #82817e;
  --mt-offline: #6b6f76;
  --mt-red: #d64545;
  --mt-warn: #d2945e;
  --mt-white: #ffffff;
  --mt-yellow: #d4a017;

  --mt-text-xs-size: 10px;
  --mt-text-xs-lh: 1.5;
  --mt-text-sm-size: 12px;
  --mt-text-sm-lh: 1.5;
  --mt-text-md-size: 14px;
  --mt-text-md-lh: 1.5;
  --mt-text-lg-size: 18px;
  --mt-text-lg-lh: 1.5;
  --mt-text-xl-size: 24px;
  --mt-text-xl-lh: 1.5;
  --mt-text-2xl-size: 32px;
  --mt-text-2xl-lh: 1.5;
  --mt-text-3xl-size: 48px;
  --mt-text-3xl-lh: 1.5;

  --m-font-weight-regular: 400;
  --mt-weight-regular: 400;
  --m-font-weight-medium: 500;
  --mt-weight-medium: 500;
  --m-font-weight-semibold: 600;
  --mt-weight-semibold: 600;
  --m-font-weight-bold: 700;
  --mt-weight-bold: 700;

  --m-pad-2xs: 2px;
  --mt-space-2xs: 2px;
  --m-pad-xs: 4px;
  --mt-space-xs: 4px;
  --m-pad-sm: 8px;
  --mt-space-sm: 8px;
  --m-pad-md: 14px;
  --mt-space-md: 14px;
  --m-pad-lg: 20px;
  --mt-space-lg: 20px;
  --m-pad-xl: 32px;
  --mt-space-xl: 32px;
  --m-pad-2xl: 48px;
  --mt-space-2xl: 48px;

  --m-radius-sm: 2px;
  --m-radius-md: 4px;
  --m-radius-lg: 6px;
  --m-radius-full: 9999px;

  --m-font-sans: "IBM Plex Sans", system-ui, sans-serif;
  --m-theme-font-sans: "IBM Plex Sans", system-ui, sans-serif;
  --m-font-display: "IBM Plex Sans Condensed", "IBM Plex Sans", system-ui, sans-serif;
  --m-theme-font-display: "IBM Plex Sans Condensed", "IBM Plex Sans", system-ui, sans-serif;
  --m-font-mono: "IBM Plex Mono", ui-monospace, monospace;
  --m-theme-font-mono: "IBM Plex Mono", ui-monospace, monospace;
/* @manifold-tokens:end */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--m-bg);
  color: var(--m-text);
  font-family: var(--m-font-sans);
  font-size: 14px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

body {
  background:
    radial-gradient(ellipse 1200px 600px at 50% 0%, rgba(245, 166, 35, 0.025), transparent 60%),
    var(--m-bg);
  background-attachment: fixed;
}

#manifold-root {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* =========================================================================
 * Skeleton — what's visible while waiting for the catalog
 * ========================================================================= */

.manifold-bootstrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
  font-family: var(--m-font-mono);
  color: var(--m-text-muted);
  font-size: 12px;
  letter-spacing: 0.06em;
}

.manifold-bootstrap-mark {
  font-family: var(--m-font-display);
  font-size: 32px;
  font-weight: var(--mt-weight-semibold);
  letter-spacing: 0.18em;
  color: var(--m-text);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 18px;
  padding: 28px 40px;
}

/* Backlog 999.184: the Manifold brand mark (a raster PNG at
 * core/dashboard/assets/manifold-m.png), rendered as a soft watermark
 * behind the wordmark. object-fit:contain keeps the mark's own aspect
 * ratio instead of stretching it to this box's old flat-bar shape (that
 * shape was tailored to the retired inline-SVG glyph, which used
 * width/height:100% directly). Per-theme opacity/filter/animation
 * overrides below are unchanged; `fill` in those blocks is now inert
 * (raster images don't take SVG paint properties) but harmless. */
.manifold-bootstrap-glyph {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  opacity: 0.18;
  z-index: 0;
}

.manifold-bootstrap-wordmark {
  position: relative;
  z-index: 1;
  opacity: 0.5;
}

.manifold-bootstrap-status {
  font-family: var(--m-font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--m-text-faint);
}

.manifold-bootstrap-status.error {
  color: var(--m-error);
}

/* =========================================================================
 * Top-level layout — header strip, tab bar, tab content
 * ========================================================================= */

.manifold-header {
  background: var(--m-surface);
  border-bottom: 1px solid var(--m-border);
  min-height: var(--m-header-row-height);
  position: relative;
  /* Stacking context above content area. Cards in the content area set
   * their own `position: relative` which creates inline stacking
   * contexts; without a header z-index, those cards rise above the
   * gear-menu dropdown anchored inside the header. z-index: 50 keeps
   * the header (and its children, including the gear menu) consistently
   * above content cards but below the modal overlay (z-index: 1000) and
   * the floating edit FAB (z-index: 900). */
  z-index: 50;
}

.manifold-tabbar {
  background: var(--m-surface);
  border-bottom: 1px solid var(--m-border);
  display: flex;
  align-items: stretch;
  padding: 0 var(--m-pad-lg);
  height: 38px;
  /* Mobile-tolerance: when many tabs exceed viewport width, scroll
     horizontally instead of wrapping into a wider-than-screen row.
     -webkit-overflow-scrolling preserves the iOS momentum-scroll feel. */
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  /* Hide horizontal scrollbar in webkit browsers — the scroll still
     works via touch / shift+wheel. Safari shows a thin bar by
     default which clutters mobile chrome. */
  scrollbar-width: thin;
}
.manifold-tabbar::-webkit-scrollbar { height: 4px; }
.manifold-tab {
  /* Prevent tabs from shrinking below their content width when the
     row overflows — let the bar scroll instead. */
  flex-shrink: 0;
}

.manifold-tab {
  display: flex;
  align-items: center;
  padding: 0 var(--m-pad-md);
  font-size: calc(12px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-medium);
  color: var(--m-text-muted);
  cursor: pointer;
  position: relative;
  letter-spacing: 0.02em;
  transition: color var(--m-t-fast);
}

.manifold-tab:hover { color: var(--m-text); }

.manifold-tab.active { color: var(--m-accent); }

.manifold-tab.active::after {
  content: "";
  position: absolute;
  bottom: -1px;
  left: var(--m-pad-md);
  right: var(--m-pad-md);
  height: 2px;
  background: var(--m-accent);
}

.manifold-content {
  flex: 1;
  padding: var(--m-pad-xl);
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-lg);
}

.manifold-empty-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
  text-align: center;
  padding: var(--m-pad-xl);
  color: var(--mt-dim);
  font-family: var(--m-font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
}

.manifold-empty-tab h2 {
  font-family: var(--m-font-display);
  font-size: 18px;
  font-weight: var(--mt-weight-medium);
  color: var(--m-text);
  margin: 0 0 var(--m-pad-md);
  letter-spacing: 0.04em;
}

.manifold-empty-tab .button {
  margin-top: var(--m-pad-lg);
  background: var(--m-accent);
  color: var(--m-bg);
  border: none;
  padding: var(--m-pad-sm) var(--m-pad-lg);
  font-family: var(--m-font-sans);
  font-weight: var(--mt-weight-medium);
  font-size: 13px;
  cursor: pointer;
  border-radius: var(--m-radius-sm);
  letter-spacing: 0.02em;
  transition: background var(--m-t-fast);
}

.manifold-empty-tab .button:hover {
  background: var(--m-accent-hi);
}

/* =========================================================================
 * Degraded-mode banner — surfaces required-service failures
 * ========================================================================= */

.manifold-banner {
  background: var(--m-error);
  color: var(--m-text);
  padding: var(--m-pad-md) var(--m-pad-lg);
  font-size: 13px;
  font-weight: var(--mt-weight-medium);
  display: flex;
  align-items: center;
  gap: var(--m-pad-md);
}

.manifold-banner.warning {
  background: var(--m-warning);
}

.manifold-banner-actions {
  margin-left: auto;
  display: flex;
  gap: var(--m-pad-sm);
}

.manifold-banner button {
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.2);
  color: var(--m-text);
  padding: 4px var(--m-pad-md);
  border-radius: var(--m-radius-sm);
  cursor: pointer;
  font-family: var(--m-font-sans);
  font-size: 12px;
}

.manifold-banner button:hover {
  background: var(--mt-hover-bg, rgba(255,255,255,0.22));
}

/* =========================================================================
 * Tab bar — add button + per-tab remove control
 * ========================================================================= */

.manifold-tab {
  display: flex;
  align-items: center;
  gap: 6px;
}

.manifold-tab-remove {
  display: none;
  background: transparent;
  border: none;
  color: var(--m-text-faint);
  font-size: calc(14px * var(--m-renderer-text-scale, 1));
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
  border-radius: var(--m-radius-sm);
  transition: color var(--m-t-fast), background var(--m-t-fast);
}

.manifold-tab:hover .manifold-tab-remove,
.manifold-tab.active .manifold-tab-remove {
  display: inline;
}

.manifold-tab-remove:hover {
  color: var(--m-error);
  background: var(--m-surface-2);
}

/* Rename pencil — same visibility rules as ×, but hovers to accent
 * instead of error. Only renders in tabEditMode (gated in JSX). */
.manifold-tab-rename {
  display: none;
  background: transparent;
  border: none;
  color: var(--m-text-faint);
  font-size: calc(11px * var(--m-renderer-text-scale, 1));
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
  border-radius: var(--m-radius-sm);
  transition: color var(--m-t-fast), background var(--m-t-fast);
}
.manifold-tab:hover .manifold-tab-rename,
.manifold-tab.active .manifold-tab-rename {
  display: inline;
}
.manifold-tab-rename:hover {
  color: var(--m-accent);
  background: var(--m-surface-2);
}

.manifold-tab-add {
  background: transparent;
  border: none;
  color: var(--m-text-faint);
  font-family: var(--m-font-mono);
  font-size: calc(16px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-medium);
  cursor: pointer;
  margin-left: var(--m-pad-sm);
  letter-spacing: 0;
}

.manifold-tab-add:hover {
  color: var(--m-text);
}

/* Floating bottom-right edit-mode toggle. Always-visible affordance
 * for owner/admin (unless hidden via the "Hide edit button" gear-menu
 * preference). Color-shifts when active so the user can tell at a
 * glance which mode the dashboard is in. */
.manifold-edit-fab {
  position: fixed;
  right: 18px; bottom: 18px;
  z-index: 900;
  padding: 9px 16px;
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* Phase 71 THEME-07 / D-01: route the FAB through the per-theme --mt-*
     token layer (not legacy --m-*, which the 5 light themes never redefine,
     so the button inherited dark-assumed values and the dim text vanished on
     a light page). Rest-state text is --mt-fg — the theme's PRIMARY body text,
     which plan 71-01's contrast gate proves >= 4.5:1 against the surface —
     giving a STRUCTURAL contrast floor, not a magic number. The strong border
     keeps the pill edge readable on a light surface too. Fallbacks degrade
     gracefully if a var is ever missing. */
  background: var(--mt-bg-elev, #1d2128);
  color: var(--mt-fg, #e8e6e3);
  border: 1px solid var(--mt-border-strong, #353c47);
  border-radius: 999px;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
  transition: background 120ms, color 120ms, border-color 120ms;
}
.manifold-edit-fab:hover {
  color: var(--mt-fg, #e8e6e3);
  border-color: var(--mt-accent, #f5a623);
}
.manifold-edit-fab[data-active="true"] {
  /* Active (editing) state — a deliberate high-contrast inversion onto the
     accent surface, which plan 71-01's gate proves >= 3:1 against bg. */
  background: var(--mt-accent, #f5a623);
  color: var(--mt-bg-deep, #0d0f12);
  border-color: var(--mt-accent, #f5a623);
}

/* Sign-in FAB — same bottom-right slot as the Edit FAB, but for
   anonymous users. Accent-colored so the call-to-action is obvious to a
   first-time visitor. Karl 2026-05-08; routed through --mt-* in Phase 71. */
.manifold-signin-fab {
  background: var(--mt-accent, #f5a623);
  color: var(--mt-bg-deep, #0d0f12);
  border-color: var(--mt-accent, #f5a623);
  text-decoration: none;
  font-weight: var(--mt-weight-semibold);
}
.manifold-signin-fab:hover,
.manifold-signin-fab:focus-visible {
  background: var(--mt-accent, #f5a623);
  filter: brightness(1.1);
  outline: none;
}

/* Undo/redo toast — bottom-center, transient. Karl 2026-05-08. */
.manifold-history-toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 9000;
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  padding: 8px 16px;
  font-family: var(--m-font-mono, monospace);
  font-size: 12px;
  background: var(--m-bg-elev, #1f2024);
  color: var(--m-fg, #fff);
  border: 1px solid var(--m-border-strong, #555);
  border-radius: 4px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  animation: manifold-history-toast-in 120ms ease-out;
}
.manifold-history-toast-kind {
  font-weight: var(--mt-weight-semibold);
  color: var(--m-accent, #8b8);
}
.manifold-history-toast-label {
  opacity: 0.85;
}
@keyframes manifold-history-toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(8px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* Tab edit mode (?edit / ?tabedit deep-link). Drag-to-reorder is gated
 * on this mode; the edit-mode chrome here makes it visually obvious
 * the bar's accepting drags. */
.manifold-tabbar--editing {
  outline: 1px dashed var(--m-accent, currentColor);
  outline-offset: 2px;
  border-radius: 4px;
  padding: 2px;
}
.manifold-tabbar-edit-hint {
  font-family: var(--m-font-mono);
  font-size: calc(10px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--m-accent, currentColor);
  align-self: center;
  margin-right: 6px;
  opacity: 0.85;
  white-space: nowrap;
}
.manifold-tab-edit-enter,
.manifold-tab-edit-done {
  font-family: var(--m-font-mono);
  font-size: calc(11px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-bold);
  cursor: pointer;
  background: transparent;
  color: var(--m-text-dim, currentColor);
  border: 1px solid var(--m-border, currentColor);
  border-radius: 4px;
  padding: 2px 8px;
  margin-left: 4px;
  letter-spacing: 0.04em;
}
.manifold-tab-edit-done {
  color: var(--m-accent, currentColor);
  border-color: var(--m-accent, currentColor);
  background: color-mix(in srgb, var(--m-accent) 12%, transparent);
}
.manifold-tab-edit-enter:hover {
  color: var(--m-text, currentColor);
  border-color: var(--m-text, currentColor);
}

/* =========================================================================
 * Modal — add tab dialog (and the shape future modals will reuse)
 * ========================================================================= */

.manifold-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(13, 15, 18, 0.7);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--m-pad-xl);
}

.manifold-modal {
  background: var(--m-surface-2);
  border: 1px solid var(--m-border-hi);
  border-radius: var(--m-radius-md);
  width: 100%;
  max-width: 720px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.7);
}

.manifold-modal-header {
  padding: var(--m-pad-lg);
  border-bottom: 1px solid var(--m-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.manifold-modal-header h2 {
  font-family: var(--m-font-display);
  font-size: 16px;
  font-weight: var(--mt-weight-semibold);
  letter-spacing: 0.04em;
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--m-pad-sm);
  color: var(--m-text);
}

.manifold-modal-header h2::before {
  content: "";
  width: 4px;
  height: 16px;
  background: var(--m-accent);
  display: inline-block;
}

.manifold-modal-close {
  background: transparent;
  border: 1px solid var(--m-border);
  color: var(--m-text-muted);
  width: 28px;
  height: 28px;
  border-radius: var(--m-radius-sm);
  font-family: var(--m-font-mono);
  font-size: 14px;
  cursor: pointer;
  transition: border-color var(--m-t-fast), color var(--m-t-fast);
}

.manifold-modal-close:hover {
  border-color: var(--m-border-hi);
  color: var(--m-text);
}

.manifold-modal-body {
  padding: var(--m-pad-lg);
  overflow-y: auto;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-sm);
}

.manifold-section-label {
  font-family: var(--m-font-mono);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--m-text-faint);
  margin-bottom: 4px;
}

/* Suggestion grid — Power, Storage, Network, etc. */
.manifold-suggestion-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--m-pad-sm);
}

.manifold-suggestion {
  background: var(--m-surface);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-md);
  padding: var(--m-pad-md);
  cursor: pointer;
  text-align: left;
  font-family: var(--m-font-sans);
  display: flex;
  flex-direction: column;
  gap: 4px;
  transition: border-color var(--m-t-fast), background var(--m-t-fast);
}

.manifold-suggestion:hover {
  border-color: var(--m-accent-line);
  background: var(--m-accent-low);
}

.manifold-suggestion.empty {
  opacity: 0.6;
}

.manifold-suggestion-name {
  font-family: var(--m-font-display);
  font-size: 14px;
  font-weight: var(--mt-weight-semibold);
  color: var(--m-text);
  letter-spacing: 0.02em;
}

.manifold-suggestion-caps {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--m-text-muted);
  letter-spacing: 0.04em;
}

.manifold-suggestion-empty-flag {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--m-text-faint);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-top: 4px;
}

/* Blank-tab form */
.manifold-blank-form {
  display: flex;
  gap: var(--m-pad-sm);
  align-items: center;
}

.manifold-text-input {
  flex: 1;
  background: var(--m-bg);
  border: 1px solid var(--m-border);
  color: var(--m-text);
  font-family: var(--m-font-sans);
  font-size: 13px;
  padding: var(--m-pad-sm) var(--m-pad-md);
  border-radius: var(--m-radius-sm);
}

.manifold-text-input:focus {
  outline: 1px solid var(--m-accent);
  outline-offset: -1px;
  border-color: var(--m-accent);
}

.manifold-button {
  background: var(--m-surface-2);
  border: 1px solid var(--m-border);
  color: var(--m-text);
  font-family: var(--m-font-sans);
  font-size: 12px;
  font-weight: var(--mt-weight-medium);
  padding: var(--m-pad-sm) var(--m-pad-md);
  border-radius: var(--m-radius-sm);
  cursor: pointer;
  transition: border-color var(--m-t-fast), background var(--m-t-fast);
  letter-spacing: 0.02em;
}

.manifold-button:hover:not(:disabled) {
  border-color: var(--m-border-hi);
  background: var(--m-surface-hi);
}

.manifold-button.primary {
  background: var(--m-accent);
  color: var(--m-bg);
  border-color: var(--m-accent);
}

.manifold-button.primary:hover:not(:disabled) {
  background: var(--m-accent-hi);
  border-color: var(--m-accent-hi);
}

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

.manifold-loading,
.manifold-error-line {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--m-text-muted);
  text-align: center;
  padding: var(--m-pad-md);
}

.manifold-error-line {
  color: var(--m-error);
}

/* Compact "+ Add card" row at the bottom of populated tabs */
.manifold-add-card-row {
  display: flex;
  justify-content: center;
  padding: var(--m-pad-md) 0 0;
  border-top: 1px dashed var(--m-border);
  margin-top: var(--m-pad-md);
}

/* =========================================================================
 * HEADER STRIP — configurable strip-tab above the tab bar (§ 22)
 * ========================================================================= */

.manifold-h-strip {
  display: grid;
  gap: 1px;
  background: var(--m-border);
  min-height: var(--m-header-row-height);
}

.manifold-h-cell {
  background: var(--m-surface);
  display: flex;
  align-items: center;
  justify-content: stretch;
  position: relative;
  min-height: var(--m-header-row-height);
}

.manifold-h-cell.empty {
  background:
    repeating-linear-gradient(45deg,
      transparent 0, transparent 8px,
      rgba(255,255,255,0.018) 8px, rgba(255,255,255,0.018) 16px),
    var(--m-surface);
}

/* Tab-bar widget inside the header strip. The standalone TabBar
 * component carries its own background, border-bottom, and padding
 * — appropriate when it lived as a separate band, but redundant /
 * disconnecting when nested inside a header cell. Strip those down
 * here so the tabbar reads as a continuous part of the header band
 * rather than a separate strip below it. */
.manifold-h-tabbar-host {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: stretch;
}
.manifold-h-tabbar-host .manifold-tabbar {
  background: transparent;
  border-bottom: none;
  height: auto;
  flex: 1;
  align-items: center;
}
/* full-row cell (the row carrying the tabbar) — no internal padding,
 * let the tabbar own the row width edge-to-edge. */
.manifold-h-cell.full-row {
  padding: 0;
}

.manifold-h-widget-wrap {
  width: 100%; height: 100%;
  display: flex; align-items: center;
  position: relative;
}

.manifold-h-widget-remove {
  display: none;
  position: absolute;
  top: 4px; right: 4px;
  width: 16px; height: 16px;
  padding: 0; border: none;
  background: var(--m-error); color: var(--m-text);
  font-family: var(--m-font-mono); font-size: 11px; line-height: 1;
  border-radius: var(--m-radius-sm);
  cursor: pointer;
}

.manifold-h-cell.edit-mode:hover .manifold-h-widget-remove {
  display: flex; align-items: center; justify-content: center;
}

/* --- Brand widget (wordmark mode) ---
 * Same Layer 4 rationale as .manifold-h-custom — natural-width inline
 * element. The renderer's outer wrap positions it via widget.style.align.
 */
/* #210: the wordmark asks the Card title part. This is header-strip.jsx's
 * rule, so pointing Card title somewhere moves the REAL header strip as
 * well as a brand card — intended, per Karl's 2026-08-12 ruling that the
 * header may carry the colours and nothing else. */
.manifold-h-brand {
  font-family: var(--m-font-display);
  font-size: calc(18px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-semibold);
  letter-spacing: 0.04em;
  color: var(--mt-part-title, var(--m-text));
  display: inline-flex; align-items: baseline; gap: 10px;
}

.manifold-h-accent-bar {
  display: inline-block;
  width: 4px; height: 16px;
  background: var(--m-accent);
}

/* #107: the size left this shared rule and each class states its own.
 *
 * The two empty states share every other property and were written as one
 * rule, but they belong to different widgets: `brand` has a size control now
 * and the header's leaf widget does not. Scaling both would put a scaled
 * declaration on a class no scale ever reaches — inert today, and a lie about
 * which widget owns it the moment somebody reads it. Leaving the size here and
 * overriding it below would work too, and would mean the size for one of these
 * classes is stated in two places. Neither is worth it for one property. */
.manifold-h-brand-empty,
.manifold-h-leaf-empty {
  font-family: var(--m-font-mono);
  color: var(--m-text-faint);
  text-transform: lowercase;
  letter-spacing: 0.1em;
  padding: 0 var(--m-pad-md);
}

.manifold-h-brand-empty { font-size: calc(10px * var(--m-renderer-text-scale, 1)); }
.manifold-h-leaf-empty  { font-size: 10px; }

/* --- Custom HTML widget ---
 * Layer 4: card-builder's HeaderRendererBrand owns positioning via
 * widget.style.align (the 9-position picker). This class intentionally
 * imposes neither size nor flex centering — the user's pasted HTML
 * controls its own layout, and the renderer's outer wrap pins it to
 * the chosen corner / edge. Padding moves to the renderer wrap too.
 */
.manifold-h-custom {
  display: inline-block;
  color: var(--m-text);
}

.manifold-h-custom > * { max-height: 100%; }

/* --- Gear widget --- */
.manifold-h-gear {
  width: 32px; height: 32px;
  background: transparent;
  /* #186: THE BORDER IS STILL DRAWN, AND IT IS INVISIBLE UNTIL YOU REACH FOR IT.
     It has to keep its 1px so the button does not shift by two pixels on hover,
     when the rule below gives it a colour. What changed is the resting colour:
     it was `var(--m-border)`, which put a permanent box around the gear in the
     header — and because the colour comes from the theme, a theme with a
     contrasty border made a box that had always been drawn suddenly obvious.
     Karl could not turn it off from the card editor and never will be able to:
     this is a stylesheet rule on a header button, not a setting on a card. */
  border: 1px solid transparent;
  color: var(--m-text-muted);
  cursor: pointer;
  border-radius: var(--m-radius-md);
  display: flex; align-items: center; justify-content: center;
  margin: 0 var(--m-pad-md) 0 auto;
  transition: border-color var(--m-t-fast), background var(--m-t-fast), color var(--m-t-fast);
  padding: 0;
}

.manifold-h-gear:hover,
.manifold-h-gear.open {
  border-color: var(--m-accent-line);
  background: var(--m-accent-low);
  color: var(--m-accent);
}

.manifold-h-gear svg { width: 16px; height: 16px; }
.manifold-h-gear-svg { display: inline-flex; }
.manifold-h-gear-svg svg { width: 16px; height: 16px; }

/* --- Gear-icon picker (in the header editor) --- */
.manifold-h-gear-icon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
  gap: 6px;
  max-height: 240px;
  overflow-y: auto;
  padding: 4px;
  border: 1px solid var(--m-border);
  border-radius: 4px;
  background: var(--m-bg);
}
.manifold-h-gear-icon-tile {
  display: flex; flex-direction: column; align-items: center;
  gap: 4px; padding: 6px 4px;
  background: transparent; border: 1px solid transparent; border-radius: 3px;
  color: inherit; cursor: pointer; font: inherit;
  transition: background 0.1s, border-color 0.1s;
}
.manifold-h-gear-icon-tile:hover { background: var(--m-bg-hover); }
.manifold-h-gear-icon-tile.selected {
  border-color: var(--m-accent);
  background: var(--m-bg-hover);
}
.manifold-h-gear-icon-tile img,
.manifold-h-gear-icon-tile svg {
  width: 20px; height: 20px; object-fit: contain;
}
.manifold-h-gear-icon-tile .tile-name {
  font-size: 9px; opacity: 0.75; max-width: 64px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* --- Clock widget --- */
/* #107. The clock had no font-size rule of its own and inherited one; this is
 * the first. The 14px is MEASURED, not chosen — the conformance walk read 14px
 * off a clock card on a canvas, and `.manifold-header` computes 14px on the
 * live site with nothing between it and the clock that sets a size, so the two
 * contexts agree. Writing it here pins nothing that was not already 14px, and
 * it is what lets the size menu reach a widget whose text belonged to whatever
 * happened to be above it. */
/* #210: the clock declared NO colour and inherited from the header strip.
 * `inherit` as the stock keeps that exactly true until somebody points Row
 * value somewhere; the precedent is .manifold-renderer-unit-color, which
 * has read var(--mt-part-unit, inherit) since #164. */
.manifold-h-clock {
  font-size: calc(14px * var(--m-renderer-text-scale, 1));
  color: var(--mt-part-rowvalue, inherit);
}

/* --- Auth state widget --- */
.manifold-h-auth {
  display: flex; align-items: center; gap: var(--m-pad-sm);
  font-family: var(--m-font-mono); font-size: calc(12px * var(--m-renderer-text-scale, 1));
  padding: 0 var(--m-pad-md);
}

.manifold-h-auth-pill {
  padding: 4px 10px 4px 8px;
  background: var(--m-surface-2);
  border: 1px solid var(--m-border);
  border-radius: 11px;
  display: flex; align-items: center; gap: 6px;
  color: var(--m-text);
}

.manifold-h-auth-pill::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--m-success); display: inline-block;
}

.manifold-h-auth-role {
  color: var(--m-text-muted); font-size: calc(10px * var(--m-renderer-text-scale, 1));
  text-transform: uppercase; letter-spacing: 0.08em;
}

/* --- Spacer widget --- */
.manifold-h-spacer { width: 100%; height: 100%; }

.manifold-h-spacer-mark {
  font-family: var(--m-font-mono); font-size: 9px;
  color: var(--m-text-faint);
  letter-spacing: 0.1em; margin: auto;
}

/* --- Leaf display widget --- */
.manifold-h-leaf {
  display: flex; flex-direction: column; justify-content: center;
  gap: 2px; font-family: var(--m-font-mono);
  padding: 0 var(--m-pad-md);
}

.manifold-h-leaf-label {
  font-size: 9px; color: var(--m-text-faint);
  letter-spacing: 0.1em; text-transform: uppercase;
}

.manifold-h-leaf-value {
  font-size: 16px; font-weight: var(--mt-weight-medium); color: var(--m-text);
  display: flex; align-items: baseline; gap: 3px;
}

.manifold-h-leaf-unit {
  font-size: 10px; color: var(--m-text-muted); font-weight: var(--mt-weight-regular);
}

/* --- Status widget --- */
.manifold-h-status {
  display: flex; align-items: center; gap: var(--m-pad-sm);
  font-family: var(--m-font-mono); font-size: 11px;
  color: var(--m-text-muted); text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0 var(--m-pad-md);
}

.manifold-h-status-dot {
  width: 8px; height: 8px; border-radius: 50%; display: inline-block;
}
.manifold-h-status-dot.ok    { background: var(--m-success); box-shadow: 0 0 8px var(--m-success); }
.manifold-h-status-dot.warn  { background: var(--m-warning); box-shadow: 0 0 8px var(--m-warning); }
.manifold-h-status-dot.error { background: var(--m-error); box-shadow: 0 0 8px var(--m-error); }

/* =========================================================================
 * GEAR MENU — small dropdown anchored to the gear widget
 * ========================================================================= */

.manifold-gear-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: var(--m-pad-md);
  width: 280px;
  background: var(--m-surface-2);
  border: 1px solid var(--m-border-hi);
  border-radius: var(--m-radius-md);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
  padding: var(--m-pad-sm) 0;
  /* Above content cards (which can have their own positive z-index from
   * inline styles like position:sticky on header sections, or PiP
   * widgets that stack inside the card grid). Below modal overlays
   * (1000) so a Save/Cancel modal still wins. */
  z-index: 950;
}

.manifold-gear-section {
  padding: var(--m-pad-sm) 0;
  border-bottom: 1px solid var(--m-border);
}

.manifold-gear-section:last-child { border-bottom: none; }

.manifold-gear-header-text {
  font-family: var(--m-font-mono); font-size: 9px;
  text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--m-text-faint);
  padding: 4px var(--m-pad-md);
}

.manifold-gear-row {
  width: 100%;
  padding: var(--m-pad-sm) var(--m-pad-md);
  display: flex; align-items: center; justify-content: space-between;
  background: transparent; border: none;
  font-family: var(--m-font-sans); font-size: 12px; color: var(--m-text);
  cursor: pointer;
  transition: background var(--m-t-fast);
  text-align: left;
}

.manifold-gear-row:hover { background: var(--m-surface-hi); }

/* BACKLOG-06. The closed Style row's label. Style names are user-typed with no
   length cap; a 58-character one took this row from 32px to two lines at 44px
   and moved every row under it.

   min-width: 0 is the load-bearing one and it is the reason a one-property fix
   was not enough: a flex item's min-width defaults to its content width, so
   nowrap + ellipsis alone never get a chance to fire. Same trio as
   .manifold-gear-preview-line and .msp-basedon, for the same reason, written
   the same way. NOT a second .manifold-gear-row rule — that class is defined
   exactly once in this sheet and two tests hold it there. */
.manifold-gear-row-label {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The first line, wrapped in its own box so text-overflow has a box to apply to.
   With .has-cards-line on, the label holds an inline run followed by a block
   child; CSS puts that run in an anonymous block box, and an anonymous box is
   not the box the ellipsis was set on — so without this the split state clips
   with no ellipsis and a cut name reads as a short name. */
.manifold-gear-row-line {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The ▸ / → must never be squeezed out by a long name. It had no flex property
   at all, so it was shrinkable. A no-op on every short row in the menu. */
.manifold-gear-arrow { color: var(--m-text-faint); font-family: var(--m-font-mono); flex: 0 0 auto; }

/* Phase 186 (NAV-02): "Home tab" picker dropdown — sits inside a
 * .manifold-gear-row label, right-aligned by the row's own
 * space-between flex. Small, unobtrusive select matching the gear
 * menu's compact type scale (mirrors .manifold-theme-editor-startfrom
 * select's shape, gear-menu surface tokens instead of theme-editor ones). */
.manifold-gear-select {
  background: var(--m-surface-2);
  color: var(--m-text);
  border: 1px solid var(--m-border-hi);
  border-radius: var(--m-radius-sm);
  padding: 3px 6px;
  font-family: var(--m-font-sans);
  font-size: 12px;
  max-width: 140px;
}

.manifold-gear-info {
  padding: 4px var(--m-pad-md);
  font-family: var(--m-font-mono); font-size: 10px;
  color: var(--m-text-muted);
  line-height: 1.6;
}

/* Theme-picker Dark/Light grouping + swatches (Phase 71 D-07).
 * The group label reuses the gear header-text treatment but indents to
 * line up under the nested theme rows. Each theme row carries a 3-chip
 * swatch (surface / text / accent) whose colors arrive as inline CSS
 * custom properties (--swatch-bg / --swatch-fg / --swatch-accent) set by
 * the JSX from th.tokens — the allowlisted dynamic-color carrier (a custom
 * property key, not a forbidden `background`/`color` inline key). The
 * literal `background:` lives here in CSS, never in the JSX inline style. */
.manifold-gear-group-label {
  font-family: var(--m-font-mono); font-size: 9px;
  text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--m-text-faint);
  padding: 6px var(--m-pad-md) 2px 28px;
}

.manifold-theme-swatch {
  display: inline-flex;
  gap: 3px;
  margin-right: 8px;
  vertical-align: middle;
}

.manifold-theme-swatch-chip {
  width: 11px; height: 11px;
  border-radius: 3px;
  border: 1px solid var(--m-border, rgba(128,128,128,0.4));
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
}

.manifold-theme-swatch-chip.is-bg     { background: var(--swatch-bg, var(--mt-bg-elev)); }
.manifold-theme-swatch-chip.is-fg     { background: var(--swatch-fg, var(--mt-fg)); }
.manifold-theme-swatch-chip.is-accent { background: var(--swatch-accent, var(--mt-accent)); }

/* The theme row's label sits next to its swatch. */
/* BACKLOG-06, second surface: the built-in, saved and from-a-card rows carry the
   same uncapped user-typed names. The label is display:inline-flex, so it needs
   min-width:0 in its own right; the swatch and the check gutter must hold their
   width while the name gives way. The check gutter already has flex: 0 0 auto. */
.manifold-gear-theme-row .manifold-gear-theme-label {
  display: inline-flex; align-items: center;
  min-width: 0;
  overflow: hidden;
}
/* Fixed-width check gutter so the active row's ✓ and inactive rows' blank take
   the same space — every theme name lines up at one indented x (dark + light
   groups identical). Replaces the old "✓ " / "  " text prefix that nudged the
   active name right. */
.manifold-gear-theme-row .manifold-gear-theme-check {
  flex: 0 0 auto;
  width: 1em;
  margin-left: 6px;
  text-align: center;
}

.manifold-gear-theme-row .manifold-theme-swatch {
  flex: 0 0 auto;
}

.manifold-gear-theme-name {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* #200 — the theme this row was built on has moved, and there is a press
 * waiting for it in Manage your themes.
 *
 * A DOT AND NOT A WORD, AND NOT A WARNING COLOUR. Karl's call, 2026-08-15:
 * nothing is wrong — a saved theme is allowed to be a snapshot, that is what
 * saving one is for — so this is the same register as the row's own name, only
 * quieter. `--mt-muted` is the dimmest thing the gear already draws in. A word
 * here would wrap the row on a narrow menu and an accent or error colour would
 * make a normal state look like a fault.
 *
 * THE SENTENCE IS ON THE ROW'S `title`, not beside the dot. The dot is
 * aria-hidden and carries no meaning of its own; the row's own title says
 * "Deep Sea has changed since you saved this" in full, which is what a screen
 * reader and a hover both get.
 *
 * Only a theme that KNOWS its base moved is ever marked here — see the JSX for
 * why a theme with nothing written down stays silent in this menu. */
.manifold-gear-theme-row .manifold-gear-theme-moved {
  flex: 0 0 auto;
  width: 5px;
  height: 5px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--mt-muted);
}

/* Phase 224 (D-02): the open Style list's own box.
 *
 * It has NO visual job, and that is the point of writing it down: a plain
 * block, so the five groups stack exactly as they did when they sat straight
 * inside .manifold-gear-section. It exists because a React Fragment can carry
 * a class but not a handler, and two things now need to belong to the LIST
 * rather than to any one row — the roving-focus keydown, and the pointer-leave
 * plan 224-06 hangs here to put a preview back.
 *
 * It deliberately takes no max-height and adds no scroll region. The gear menu
 * has never had a cap, this phase makes the list taller, and repairing that is
 * out of scope — record it if the live walk runs the menu off a short screen. */
.manifold-gear-list {
  display: block;
}

/* The focus ring for the arrow-key walk (Phase 224, D-06's keyboard half).
 *
 * Only one row is in Tab order at a time, so every other stop on the walk is a
 * scripted focus — and a stop you cannot see is a walk you cannot follow. This
 * is the panel's own shipped ring value, in tokens only, because the gear menu
 * repaints itself while a look is previewing and a literal colour would be the
 * one thing on screen that does not move with it. */
.manifold-gear-list .manifold-gear-theme-row:focus-visible {
  outline: 2px solid var(--mt-accent);
  outline-offset: 1px;
}

/* Phase 224 (D-06): the one line at the foot of the open Style list.
 *
 * ALWAYS PRESENT WHILE THE LIST IS OPEN AND ALWAYS EXACTLY ONE LINE TALL, in
 * both of its two states. That is the whole reason for the nowrap trio below: a
 * line that grew to two would push every row above the cursor down by a row's
 * worth of height mid-hover, and turn the preview Karl is reading into a click
 * on the look underneath the one he wanted.
 *
 * At 10px mono in the 280px menu with the same 28px indent the theme rows use,
 * there is room for about 48 characters. The longest string this line can hold
 * from a built-in look is 47. A longer saved-Style name truncates with an
 * ellipsis rather than wrapping.
 *
 * Values: .manifold-gear-info's own type and colour (10px mono, --m-text-muted,
 * 1.6), indented to 28px to sit under the rows, closed above by the same
 * hairline every other block in this menu uses. Nothing here is a new size and
 * nothing here is a literal colour — the gear menu repaints ITSELF during a
 * preview (the list is inside the thing being previewed), so a hardcoded value
 * would be the one thing on screen that did not move with the look. */
.manifold-gear-preview-line {
  border-top: 1px solid var(--m-border);
  padding: 4px var(--m-pad-md) 4px 28px;
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--m-text-muted);
  line-height: 1.6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The row being previewed. The panel's own way of saying "this one"
 * (.msp-family.is-selected, .msp-saved-item.is-chosen), borrowed rather than
 * invented.
 *
 * It is weight and nothing else — no colour, no background, no ring. The check
 * mark still belongs to the committed look and must stay the louder of the two,
 * because it is the one that answers "where does moving away land". */
.manifold-gear-theme-row.is-previewing .manifold-gear-theme-label {
  font-weight: var(--mt-weight-semibold);
}

/* Phase 224 (D-04): the closed row's second line, present only while the box
 * under the list is ticked.
 *
 * 12px and --m-text-muted, INSIDE the row rather than in a row of its own. The
 * gear has no smaller sans size, and a second line at the row's own size reads
 * as part of the same statement rather than as a footnote.
 *
 * display: block is the whole of the stacking. .manifold-gear-row is a flex
 * container, so its label span is already a block-level flex item, and a block
 * child simply sits under the inline text above it. That is why nothing was
 * wrapped and why the shipped span was left alone: with the box unticked this
 * rule has nothing to apply to, and the row is byte-for-byte the single line it
 * has always been.
 *
 * The row's own `align-items: center` would otherwise hang the ▸ between the two
 * lines. .has-cards-line pulls it back up beside the name, which is where the
 * UI-SPEC draws it, and it is a MODIFIER — .manifold-gear-row is still defined
 * exactly once in this sheet and every other row in the menu is untouched. */
.manifold-gear-row.has-cards-line {
  align-items: flex-start;
}

.manifold-gear-cards-line {
  display: block;
  font-size: 12px;
  color: var(--m-text-muted);
  /* BACKLOG-06: the second line carries a Style name too, and text-overflow on
     the label above does not reach into a child box. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Phase 224 (D-04): `Give the cards a different look`, and the two segments
 * under it that say which of the page and the cards the next pick lands on.
 *
 * THE ROW IS A REAL LABEL WRAPPING A REAL CHECKBOX, not a div with a click
 * handler, so Space toggles it and a screen reader reads the box and its words
 * as one control. It borrows .manifold-gear-row whole and changes exactly two
 * of its values: the padding gains the same 28px left indent
 * .manifold-gear-group-label uses, so the box lines up under the theme rows
 * rather than under the section; and the row packs from the left instead of
 * space-between, because space-between would fling the words to the far edge
 * and leave the tick stranded on its own.
 *
 * THE SEGMENTS ARE 223'S SCOPE SEGMENTS, LIFTED. Same track, same 13% accent
 * wash on the active one, same 11px sentence case, same inset focus ring. It is
 * the same question ("which one am I setting?") in a different menu, and Karl
 * already reads that control — a second idiom for one idea is the thing this
 * milestone exists to remove. The values come from .msp-scope-seg in
 * components/style-panel.css; only the token FAMILY moves, from the panel's
 * --mt-* surfaces to the gear's own --m-* ones, because these sit on
 * --m-surface-2 and every other rule in this menu is written in that family.
 * The accent and the focus ring stay --mt-accent, matching the shipped
 * .manifold-gear-theme-row:focus-visible rule a few lines above.
 *
 * NOT ONE LITERAL COLOUR, for the reason the whole gear section carries: the
 * menu is inside the thing being previewed and repaints itself under the
 * cursor, so a hardcoded value here would be the one thing on screen that did
 * not move with the look.
 *
 * prefers-reduced-motion is already handled. This sheet's reduce block is a
 * universal `*` rule near the foot of the file, so the 120ms transition below
 * is inside it by construction; naming these two selectors there as well would
 * be dead CSS. */
.manifold-gear-split-row {
  padding: var(--m-pad-sm) var(--m-pad-md) var(--m-pad-sm) 28px;
  justify-content: flex-start;
  gap: 8px;
}

.manifold-gear-split-row input {
  accent-color: var(--mt-accent);
  cursor: pointer;
  margin: 0;
  flex: 0 0 auto;
}

/* One bordered track, two segments sharing it, inset to the same 28px the box
 * above it and the theme rows above that both sit at. */
.manifold-gear-target-track {
  display: flex;
  margin: 2px var(--m-pad-md) 4px 28px;
  border: 1px solid var(--m-border-hi);
  border-radius: var(--m-radius-sm);
  overflow: hidden;
}

.manifold-gear-target-seg {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  padding: 6px 8px;
  border: none;
  background: transparent;
  font-family: var(--m-font-sans);
  font-size: 11px;
  font-weight: var(--mt-weight-regular);
  letter-spacing: normal;
  text-transform: none;
  color: var(--m-text-muted);
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--m-t-fast), color var(--m-t-fast);
}

/* The divider drawn on the second segment rather than as a trailing border on
 * every one — a trailing border on the last doubles up with the track's own
 * right edge. */
.manifold-gear-target-seg + .manifold-gear-target-seg {
  border-left: 1px solid var(--m-border);
}

.manifold-gear-target-seg:hover {
  background: var(--m-surface-hi);
  color: var(--m-text);
}

/* Inset, because the segments sit flush inside a bordered track and an outset
 * ring would be drawn over its neighbour. */
.manifold-gear-target-seg:focus-visible {
  outline: 2px solid var(--mt-accent);
  outline-offset: -2px;
}

.manifold-gear-target-seg.is-active {
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
  color: var(--mt-accent);
  font-weight: var(--mt-weight-semibold);
}

/* =========================================================================
 * EDIT HEADER MODAL
 * ========================================================================= */

.manifold-h-editor-preview {
  border: 1px dashed var(--m-border-hi);
  border-radius: var(--m-radius-sm);
  background: var(--m-bg);
  margin-top: 4px;
  position: relative;
  overflow: hidden;
}

.manifold-h-editor-grid-controls {
  display: flex; align-items: center; gap: var(--m-pad-sm);
  padding: var(--m-pad-sm) 0;
}

.grid-input {
  background: var(--m-surface);
  border: 1px solid var(--m-border);
  color: var(--m-text);
  font-family: var(--m-font-mono); font-size: 11px;
  width: 38px; text-align: center;
  padding: 3px 4px;
  border-radius: var(--m-radius-sm);
}

.grid-input-x {
  font-family: var(--m-font-mono);
  color: var(--m-text-faint); font-size: 10px;
}

.field-label {
  font-family: var(--m-font-mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--m-text-muted);
  margin-bottom: 4px;
}

.manifold-h-editor-cells {
  display: flex; flex-direction: column; gap: var(--m-pad-sm);
}

.manifold-h-edit-cell {
  background: var(--m-surface);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  padding: var(--m-pad-md);
  display: flex; flex-direction: column; gap: var(--m-pad-sm);
}

.manifold-h-edit-cell.empty {
  border-style: dashed;
  background: transparent;
}

.manifold-h-edit-cell-label {
  display: flex; align-items: center; gap: var(--m-pad-md);
  font-family: var(--m-font-mono); font-size: 11px;
}

.cell-num {
  display: inline-block;
  min-width: 24px; height: 20px; line-height: 20px;
  text-align: center;
  background: var(--m-surface-2);
  border: 1px solid var(--m-border-hi);
  border-radius: var(--m-radius-sm);
  color: var(--m-text); font-weight: var(--mt-weight-medium);
  padding: 0 6px;
}

.cell-type {
  color: var(--m-text-muted);
  text-transform: uppercase; letter-spacing: 0.08em;
}

.manifold-h-cell-add-picker {
  display: flex; flex-wrap: wrap; gap: 4px;
}

.manifold-h-cell-form {
  background: var(--m-surface-2);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  padding: var(--m-pad-md);
  display: flex; flex-direction: column; gap: var(--m-pad-md);
}

.manifold-h-cell-fields {
  display: flex; flex-wrap: wrap; gap: var(--m-pad-md);
}

.manifold-h-cell-field {
  display: flex; flex-direction: column; gap: 2px; flex: 1 1 180px;
}

.manifold-h-brand-form {
  display: flex; flex-direction: column; gap: var(--m-pad-md);
}

.manifold-h-mode-chooser {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--m-pad-sm);
}

.manifold-h-mode-tab {
  background: var(--m-surface);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-md);
  padding: var(--m-pad-md);
  cursor: pointer;
  font-family: var(--m-font-sans); text-align: left;
  display: flex; flex-direction: column; gap: 2px;
  transition: border-color var(--m-t-fast), background var(--m-t-fast);
}

.manifold-h-mode-tab:hover {
  border-color: var(--m-border-hi);
  background: var(--m-surface-hi);
}

.manifold-h-mode-tab.active {
  border-color: var(--m-accent);
  background: var(--m-accent-low);
}

.manifold-h-mode-name {
  font-family: var(--m-font-display); font-size: 14px; font-weight: var(--mt-weight-semibold);
  color: var(--m-text); letter-spacing: 0.02em;
}

.manifold-h-mode-tab.active .manifold-h-mode-name { color: var(--m-accent-hi); }

.manifold-h-mode-desc {
  font-size: 11px; color: var(--m-text-muted); line-height: 1.4;
}

.manifold-h-checkbox-row {
  display: flex; align-items: center; gap: var(--m-pad-sm);
  cursor: pointer; user-select: none;
  font-size: 13px; color: var(--m-text);
}

.manifold-h-checkbox-row input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 16px; height: 16px;
  border: 1px solid var(--m-border-hi);
  background: var(--m-surface);
  border-radius: var(--m-radius-sm);
  cursor: pointer; position: relative; margin: 0;
}

.manifold-h-checkbox-row input[type="checkbox"]:checked {
  background: var(--m-accent); border-color: var(--m-accent);
}

.manifold-h-checkbox-row input[type="checkbox"]:checked::after {
  content: "✓";
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  color: var(--m-bg); font-size: 11px; font-weight: var(--mt-weight-bold);
  font-family: var(--m-font-mono); line-height: 1;
}

.manifold-h-code-editor {
  font-family: var(--m-font-mono); font-size: 12px;
  background: var(--m-bg);
  border: 1px solid var(--m-border);
  color: var(--m-text);
  padding: var(--m-pad-md);
  border-radius: var(--m-radius-sm);
  width: 100%; min-height: 200px; resize: vertical;
  line-height: 1.5; tab-size: 2;
}

.manifold-h-sanitizer {
  font-family: var(--m-font-mono); font-size: 11px;
  padding: var(--m-pad-sm) var(--m-pad-md);
  border-radius: var(--m-radius-sm);
}

.manifold-h-sanitizer.clean {
  background: rgba(110, 155, 101, 0.08);
  border: 1px solid rgba(110, 155, 101, 0.32);
  color: var(--m-success);
}

.manifold-h-sanitizer.modified {
  background: rgba(210, 148, 94, 0.08);
  border: 1px solid rgba(210, 148, 94, 0.32);
  color: var(--m-warning);
}

.manifold-h-leaf-picker {
  display: flex; flex-direction: column; gap: 4px;
}

.manifold-h-leaf-hint {
  font-family: var(--m-font-mono); font-size: 10px;
  color: var(--m-text-faint);
}

.manifold-modal-footer {
  padding: var(--m-pad-md) var(--m-pad-lg);
  border-top: 1px solid var(--m-border);
  display: flex; justify-content: flex-end; gap: var(--m-pad-sm);
}

.manifold-button.small { font-size: 11px; padding: 4px var(--m-pad-sm); }
.manifold-button.danger { background: var(--m-error); border-color: var(--m-error); color: #fff; }
.manifold-button.danger:hover { border-color: var(--m-error); color: var(--m-error); }
/* Plan 16-04 W3.3: .compact gives slightly more horizontal breathing room
 * than .small for short labels like "+ Add card". Routes through --mt-space-*
 * so theme cascade still applies (xs=4, md=14 in default; per-theme values
 * tracked via Phase 15 token surface). */
.manifold-button.compact { font-size: 11px; padding: var(--mt-space-xs) var(--mt-space-md); }

/* Phase 19 Plan 05a: renderer visual-primitives (Placeholder, NoDataVisual,
 * Label inside card-builder-renderers.jsx) consume a small set of CSS
 * custom properties for the values the audit can't see as theme tokens —
 * the `color` argument is a per-call runtime prop, the dash size is a
 * relative em that has no fixed token equivalent, and the hint sub-text
 * lives below the smallest mt-text-* bucket (10px → 9px).
 *
 * The JSX sets `--m-renderer-color` once on the wrapper element; child
 * elements that need to consume it (the foreground text, the small dot
 * background, the hollow ring border) all read the same var so callers
 * don't have to thread the same value through three child styles.
 *
 * Falls back to var(--mt-muted) when the JSX omits the var (e.g. an
 * older / unmigrated caller, or a deliberate "use the theme's muted
 * tone" intent). */
.manifold-renderer-fg         { color: var(--m-renderer-color, var(--mt-muted)); }
/* #164 — the `unit` part of a card: the small suffix beside a number (TiB,
 * Mbps, °F). It has never had a colour of its own. It inherits the value's,
 * dimmed by the opacity:0.7 the renderer sets inline, and that is exactly what
 * `inherit` here keeps doing while --mt-part-unit is unset — which it is on
 * every dashboard nobody has run the theme wizard on. Name a colour for the
 * unit and every suffix moves together, still at 0.7. */
.manifold-renderer-unit-color { color: var(--mt-part-unit, inherit); }
.manifold-renderer-bg-color   { background-color: var(--m-renderer-color, var(--mt-muted)); }
.manifold-renderer-ring       { border: 1.5px solid var(--m-renderer-color, var(--mt-muted)); }
.manifold-renderer-label-size { font-size: var(--m-renderer-label-size, var(--mt-text-xs-size)); }
/* #107: the 9px default now follows a renderer's text scale when that renderer
 * sets one. Unset — which is every renderer that predates #107, and every card
 * nobody has picked a size on — the scale falls back to 1 and this computes to
 * the same 9px it always did. A renderer that writes --m-renderer-hint-size
 * itself (multiSeriesChart, sparkline) still wins outright; this is the
 * fallback arm, so those sites are untouched. */
.manifold-renderer-hint-text  { font-size: var(--m-renderer-hint-size, calc(9px * var(--m-renderer-text-scale, 1))); }
.manifold-renderer-scalar-dash { font-size: 1.4em; }
/* Phase 19 Plan 05b — primary value font-size for the scalar/number renderer
 * family (BigNumber, Sparkline, MultiBigNumber, Count, Text). Same shape as
 * .manifold-renderer-label-size: a single CSS custom property feeds a class,
 * letting the JSX move runtime fontSize off the audit-watched inline style
 * surface. No fallback — when the JSX omits the var the consumer is misused;
 * callers always compute fontSize via resolveStyle so the var is always set. */
.manifold-renderer-value-size { font-size: var(--m-renderer-value-size); }
/* Phase 19 Plan 05b — MultiBigNumberRenderer's "no data yet" per-slot ghost
 * badge. A dashed border + small padding + radius that visually marks a
 * series slot as awaiting its first message. The dashed border is the
 * sibling of NoDataVisual chart mode's repeating-linear-gradient stripe,
 * scaled for an inline-badge surface. Bundled into one class because the
 * three properties (dashed border, square-ish padding, small radius) read
 * as one visual primitive — splitting across three utility classes would
 * dilute the semantic. */
.manifold-renderer-ghost-slot {
  border: 1px dashed var(--mt-dim);
  border-radius: var(--m-radius-sm, 4px);
  padding: 2px 6px;
}

/* Phase 19 Plan 05c — LineChart + MultiSeriesChart need a SECOND runtime
 * fontSize escape hatch beside .manifold-renderer-value-size. The unit-
 * suffix span is sized proportionally to the primary value (e.g.
 * Math.max(9, fontSize*0.55)), so it carries a distinct runtime size.
 * Same shape as .manifold-renderer-value-size: a single CSS custom
 * property feeds a class so the audit-watched fontSize key moves off
 * the JSX inline-style surface. No fallback — callers always set the
 * var via resolveStyle / fontSize math. */
.manifold-renderer-unit-size { font-size: var(--m-renderer-unit-size); }

/* Phase 19 Plan 05c — per-series colors for MultiSeriesChartRenderer.
 * Each series carries a color computed from widget.series[i].color (or
 * tokens.accent if unspecified). The cascade must preserve per-series
 * differentiation across theme switches; collapsing every series to one
 * .mt-text-accent is a regression (feedback_sparkline_leaf_source).
 *
 * The JSX sets `--m-series-color` per-element with the resolved color;
 * the classes below consume that var with different alpha values and
 * different CSS properties so each visual affordance (chip background,
 * chip border, legend swatch, value text) re-skins automatically when
 * the series color changes — which can happen mid-render if the user
 * edits the widget.
 *
 * Alpha targets come from the literal-hex patterns the renderer used
 * pre-migration:
 *   ${c}15  → 0x15/0xFF ≈ 8.2%  (chip background tint when isolated)
 *   ${c}66  → 0x66/0xFF ≈ 40%   (chip border tint when isolated)
 * color-mix expresses the same intent in a theme-friendly way and works
 * with any CSS color (named, hex, var, currentColor) — the hex-suffix
 * trick the JSX used only worked because every series color happened to
 * be a 6-digit hex literal. */
.manifold-series-color-text     { color: var(--m-series-color); }
.manifold-series-swatch-bg      { background: var(--m-series-color); }
.manifold-series-swatch-dashed  { border-top: 2px dashed var(--m-series-color); }
.manifold-series-chip {
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--m-radius-sm, 4px);
  /* `color: inherit` here so the chip's text picks up the surrounding
   * card's foreground rather than UA default button text. The override
   * lives on the class so the JSX inline-style stays off the FORBIDDEN
   * audit surface. */
  color: inherit;
  /* Phase 19-05g — close the 19-05c gap. The button element does NOT
   * inherit font-family / font-size by default; the pre-migration JSX
   * carried `fontFamily:'inherit', fontSize:'inherit'` inline to opt in.
   * Baking the inherits into the class moves both FORBIDDEN keys
   * (fontFamily, fontSize) off the inline surface. Padding also varies
   * with compact-mode — handled by data-compact attr selector below. */
  font-family: inherit;
  font-size: inherit;
  padding: 1px 6px;
}
.manifold-series-chip[data-compact="true"] {
  padding: 1px 4px;
}

/* Phase 19-05g — MultiSeriesChartRenderer legend `meta` span. Sits at
 * the end of the chip row and pushes itself to the right edge via
 * margin-left:auto (a flex-distribution shape, not a spacing value).
 * Dedicated class instead of a utility because `margin-left: auto` is
 * not part of the locked utility-class catalog and the chip's meta
 * is the only consumer. */
.manifold-series-meta-spacer {
  margin-left: auto;
}
.manifold-series-chip[data-isolated="true"] {
  background: color-mix(in srgb, var(--m-series-color) 8%, transparent);
  border-color: color-mix(in srgb, var(--m-series-color) 40%, transparent);
}

/* Phase 19 Plan 05c — MultiSeriesChartRenderer hover tooltip. The dark
 * translucent backdrop + 1px hairline border + small radius + 5px/8px
 * padding read as one visual primitive (same posture as
 * .manifold-renderer-ghost-slot). The backdrop intentionally does NOT
 * follow --mt-bg — the tooltip floats over chart content and needs to
 * stay readable regardless of theme; rgba(10,12,18,0.93) is the
 * historically-tuned tooltip backdrop used across the chart family. */
.manifold-chart-tooltip {
  background: rgba(10, 12, 18, 0.93);
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 6px;
  padding: 5px 8px;
}

/* Phase 19 Plan 05c — MultiSeriesChartRenderer live-cursor time label.
 * A small monospace timestamp pill that floats next to the cursor at
 * each gridline x position (and a parallel use inside the time-axis
 * labels row at the bottom of the chart). Background uses color-mix so
 * the pill stays legible in any theme. */
.manifold-chart-time-label {
  background: color-mix(in srgb, var(--mt-bg) 80%, transparent);
  padding: 0 4px;
  border-radius: var(--m-radius-sm, 4px);
}

/* Phase 19 Plan 05c — MultiSeriesChartRenderer hover crosshair. A 1px
 * white-translucent vertical line marking the hovered x position. */
.manifold-chart-cursor-line {
  background: rgba(255, 255, 255, 0.18);
}

/* Phase 19 Plan 05c — MultiSeriesChartRenderer hover-point dot. Two
 * classes because the primary-series dot carries an extra outer glow
 * (the original JSX did this with a conditional
 *   boxShadow: i === 0 ? '0 0 6px ${c}' : 'none'
 * ). The base class handles the round colored fill for every series
 * dot; the `-primary` modifier adds the glow. Size + position stay
 * inline (state-driven: sz comes from chart-size math, left/top are
 * percent of chart width/height). */
.manifold-chart-hover-dot {
  border-radius: 50%;
  background: var(--m-series-color);
}
.manifold-chart-hover-dot-primary {
  box-shadow: 0 0 6px var(--m-series-color);
}

/* Phase 19 Plan 05d — PillRenderer body. The pill carries a state-driven
 * color (OK green / WARN amber / ERROR red etc.) and uses a 12% tinted
 * background + 33% tinted border + 1px ring + full pill radius. Pre-
 * migration the JSX baked the alpha as a hex suffix on the color literal
 * (`${color}20` / `${color}55`). color-mix expresses the same intent
 * in a theme-friendly way and works with any CSS color, so themed
 * status tokens (var(--mt-warn)) and ad-hoc hex literals both work.
 *
 * Padding is runtime (driven by fontSize so the pill scales with the
 * card) so it stays inline via two CSS custom properties. */
.manifold-renderer-pill {
  border-radius: 9999px;
  background: color-mix(in srgb, var(--m-renderer-color, var(--mt-muted)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--m-renderer-color, var(--mt-muted)) 33%, transparent);
  color: var(--m-renderer-color, var(--mt-muted));
  padding: var(--m-renderer-pad-y, 4px) var(--m-renderer-pad-x, 10px);
}

/* Phase 19 Plan 05d — DotRenderer body. The dot is a solid colored
 * circle with a soft glow matching the dot color. Same per-instance
 * color hook (--m-renderer-color) as the rest of the renderer family.
 * Diameter + glow size stay inline (state-driven from fontSize math).
 * The glow scales via --m-renderer-glow-size so the box-shadow key
 * lives behind a class instead of an inline-style key. */
.manifold-renderer-dot {
  border-radius: 50%;
  background: var(--m-renderer-color, var(--mt-muted));
  box-shadow: 0 0 var(--m-renderer-glow-size, 6px) var(--m-renderer-color, var(--mt-muted));
  flex-shrink: 0;
}

/* Phase 19 Plan 05d — RangePickerRenderer button + chip primitives.
 * The picker button is a small mono-uppercase chip-style trigger that
 * pops the v5 calendar (window.Picker). Hover state shifts the
 * foreground + border from `dim` to `accent`; pre-migration the JSX
 * implemented this via inline-style mutation on onMouseEnter/Leave —
 * a class with :hover replaces that imperative pattern with declarative
 * CSS that the audit can't flag.
 *
 * .manifold-range-chip is the "X hours" duration pill the picker button
 * carries on its right side. Same color-mix accent backdrop pattern as
 * the health-overall pill, but smaller and more compact. */
.manifold-renderer-rangepicker-btn {
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-md, 6px);
  padding: 6px var(--m-renderer-pad-x, 12px);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.manifold-renderer-rangepicker-btn:hover {
  color: var(--mt-accent);
  border-color: var(--mt-accent);
}
/* #35 — the chip used to paint --mt-accent text on a backdrop tinted with
 * --mt-accent. Tinting a surface with the same colour as the text on it always
 * costs contrast, because the background moves toward the text: the "5m" label
 * measured 3.94:1 on Soft Pastel and 2.54:1 on Cozy Neutral before the theme
 * colours were raised, and still 3.94:1 after.
 *
 * Reducing the tint does not fix it. Six of the twelve accents clear 4.5:1
 * against their own background by less than a point (Sentry 4.61 on bg-elev,
 * Soft Pastel 4.63 on bg-deep), so ANY self-tint pushes them under — 5% still
 * leaves Cozy Neutral at the line. The tint is not the bug; using one colour
 * for both roles is.
 *
 * So the fill keeps the accent — the pill still reads as the accent thing —
 * and the label takes the theme's ordinary text colour, which is the highest-
 * contrast colour the theme has. Worst case across all twelve themes and all
 * three surfaces is 6.25:1.
 *
 * The health-overall pill above uses the same backdrop pattern but paints its
 * label in a state colour, not the accent, so it is not self-tinting and is
 * left alone. */
.manifold-range-chip {
  padding: 0 6px;
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
  color: var(--mt-fg);
  border-radius: 99px;
}

/* Phase 19 Plan 05d — VideoRenderer letterbox. The video element's
 * outer container fills with solid black so any aspect-ratio mismatch
 * paints as a true letterbox bar (not theme bg). This is INTENTIONALLY
 * theme-independent: every video looks better against pure black, and
 * the renderer's JSX comment pre-migration explicitly called this out
 * ("intrinsic — video element background; theme-independent letterbox
 * color"). Keeping the same intent under a class so the audit doesn't
 * flag the background key. */
.manifold-renderer-video-letterbox {
  background: #000;
}

/* Backlog #53 — the camera tile's failure notice.
 *
 * Drawn over the letterbox when a stream can't play, so a blank rectangle
 * is never the whole story. Three failures have to be tellable apart from
 * across the room: this browser can't decode what the camera sends, the
 * camera can't be reached, and it's connected but drawing nothing. The
 * words do that work; this rule just has to keep them readable.
 *
 * Theme-independent colors, same reasoning as the letterbox above: this
 * text sits on deliberate black, so var(--mt-fg) would vanish under every
 * light theme. Scrim is translucent rather than solid so a half-drawn
 * frame still shows through underneath — evidence that bytes are arriving.
 *
 * pointer-events: none — the notice must never swallow a card drag or a
 * click through to the tile in edit mode.
 *
 * Mono family matches .manifold-renderer-fg (the Placeholder empty state),
 * which is the established voice for a renderer explaining itself. */
.manifold-video-notice {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 8px 10px;
  overflow: hidden;
  text-align: center;
  background: rgba(0, 0, 0, 0.72);
  font-family: var(--m-font-mono);
  pointer-events: none;
}
/* A camera tile can be one grid cell. When the box is too short for both
 * lines, the headline is the one that has to survive — so it never shrinks
 * and the hint gives up its space first. */
.manifold-video-notice-title {
  flex: 0 0 auto;
  font-size: 12px;
  font-weight: var(--mt-weight-semibold);
  color: #e9edf0;
}
.manifold-video-notice-hint {
  flex: 0 1 auto;
  min-height: 0;
  overflow: hidden;
  font-size: 10px;
  line-height: 1.35;
  color: #9aa4ad;
  max-width: 34ch;
}
/* The streaming server's own wording, kept for diagnosis. Dimmed hard so it
 * reads as a footnote and never competes with the plain-language headline. */
.manifold-video-notice-detail {
  font-size: 9px;
  line-height: 1.3;
  color: #6f7a83;
  max-width: 44ch;
  word-break: break-word;
}

/* ── Source-fault notice (#56) ───────────────────────────────────────────────
 *
 * A polled source that fails used to look exactly like one that had not spoken
 * yet. Karl picked this look from three rendered mocks on 2026-08-03: flag the
 * card at EVERY size, add the sentence wherever it fits, and never cover up a
 * reading that is still on screen.
 *
 * The overlay is the size container. It is absolutely positioned and out of
 * flow, so giving it containment cannot affect the widget's own layout — which
 * is what lets the size queries below decide what fits with no ResizeObserver,
 * no measurement code, and no re-render on resize.
 *
 * Where container queries are unsupported (Safari < 16) none of them match, so
 * the card gets the flag and no sentence. That degrades to the "flag only" look
 * rather than to nothing.
 *
 * Colours: the flag rides var(--mt-error) so it tracks every theme. The
 * sentence sits on deliberate black with fixed light text, for the same reason
 * .manifold-video-notice does — var(--mt-fg) would vanish under a light theme
 * on top of a dark scrim. Mono family matches .manifold-renderer-fg, the
 * established voice for a card explaining itself. */
.manifold-source-fault {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  container-type: size;
}
/* The flag: a hairline along the top edge, readable across a room, plus a
 * corner mark so it is still identifiable in a monochrome screenshot. Drawn on
 * the 2px bar's own ::after so the whole flag is one element in the markup. */
.manifold-source-fault-flag {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 1;   /* the empty-mode scrim is a later sibling and would bury it */
  background: var(--mt-error);
}
.manifold-source-fault-flag::after {
  content: "";
  position: absolute;
  top: 6px;
  right: 5px;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 9px solid var(--mt-error);
}
/* Hidden until a size query says there is room. */
.manifold-source-fault-say {
  display: none;
  font-family: var(--m-font-mono);
}
.manifold-source-fault-title {
  flex: 0 0 auto;
  font-size: 12px;
  font-weight: var(--mt-weight-semibold);
  color: #e9edf0;
}
.manifold-source-fault-hint {
  display: none;
  flex: 0 1 auto;
  min-height: 0;
  overflow: hidden;
  font-size: 10px;
  line-height: 1.35;
  color: #9aa4ad;
  max-width: 34ch;
}
/* The server's own wording, kept for diagnosis and dimmed hard so it never
 * competes with the plain-language headline — same treatment as
 * .manifold-video-notice-detail. */
.manifold-source-fault-detail {
  display: none;
  font-size: 9px;
  line-height: 1.3;
  color: #6f7a83;
  max-width: 44ch;
  word-break: break-word;
}
/* empty — nothing on the card to protect, so the sentence takes the card. */
.manifold-source-fault[data-fault-mode="empty"] .manifold-source-fault-say {
  position: absolute;
  inset: 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 8px 10px;
  text-align: center;
  /* Denser than #53's .72: what shows through here is the renderer's own
   * "waiting for first message", which CONTRADICTS the notice. A camera tile
   * wanted its half-drawn frame to show through; this does not. */
  background: rgba(0, 0, 0, 0.9);
}
/* stale — there IS a reading on screen, so NOTHING is drawn over the content.
 * The flag above is the whole visible signal; the sentence is delivered by the
 * hover text SourceFaultFrame puts on the widget wrapper.
 *
 * This started as a one-line strip along the bottom. On Karl's live dashboard
 * (2026-08-03) that strip covered the fourth reading on the Ambient Temp card —
 * exactly what he ruled out when he chose "keep showing it, marked old" over
 * "blank it out". A renderer fills its box, so there is no reliable slack to
 * borrow at the bottom of a card. Rather than measure for room that usually
 * is not there, the stale case does not draw a sentence at all.
 *
 * The .say block is not rendered in this mode (see SourceFaultNotice), so there
 * is no rule here to hide — this comment exists so the next person does not
 * "restore" the strip without knowing it was removed on purpose. */

@container (min-width: 150px) and (min-height: 62px) {
  .manifold-source-fault[data-fault-mode="empty"] .manifold-source-fault-say {
    display: flex;
  }
}
@container (min-width: 150px) and (min-height: 96px) {
  .manifold-source-fault-hint {
    display: block;
  }
}
@container (min-width: 150px) and (min-height: 120px) {
  .manifold-source-fault-detail {
    display: block;
  }
}

/* Phase 19 Plan 05d — TopNRenderer rows + secondary text.
 * .manifold-renderer-topn-row carries the row chrome: hairline bottom
 * border + 2px bottom padding so each ranked entry has a clear divider.
 * .manifold-renderer-topn-secondary is the secondary-text shape used
 * for sub_label / camera / value (muted color + 6px left gap) — sits
 * after the primary in the same row.
 * 11px / 9px fontSize values stay as the runtime --m-renderer-value-size
 * + --m-renderer-hint-text shapes already shipped. */
.manifold-renderer-topn-row {
  border-bottom: 1px solid var(--mt-border);
  padding-bottom: 2px;
}
/* #210: the dimmer qualifier beside the name is a row label. The ROW around
 * it is deliberately left alone — it is a box, and every piece of text
 * inside it sets its own colour, so a part pointed there would move
 * nothing. */
.manifold-renderer-topn-secondary {
  color: var(--mt-part-rowlabel, var(--mt-muted));
  margin-left: 6px;
}

/* Phase 19 Plan 05d — EventBadgesRenderer item badge. Three variants:
 *   1. Round icon-only (default mode) — borderRadius:50%, square padding
 *   2. Pill with icon + name (sub_label present) — borderRadius:9999px,
 *      asymmetric padding
 *   3. Text fallback when no icon matches — same shape as 1, plus inner
 *      span carrying the literal text
 *
 * Background tints at 33% (${color}55 pre-migration) and border tints at
 * 66% (${color}aa pre-migration) of the badge's state-driven itemColor.
 * The color flows in via --m-renderer-color per badge — same hook every
 * other renderer in this family uses. Padding/border-radius stay runtime
 * (proportional to iconSize, varies by isNamed). */
.manifold-renderer-badge {
  background: color-mix(in srgb, var(--m-renderer-color, var(--mt-muted)) 33%, transparent);
  border: 1px solid color-mix(in srgb, var(--m-renderer-color, var(--mt-muted)) 66%, transparent);
  padding: var(--m-renderer-badge-pad, 2px);
  border-radius: var(--m-renderer-badge-radius, 50%);
}
/* Phase 19 Plan 05d — EventBadges text-fallback inner span. The pre-
 * migration JSX wrote `padding: isNamed ? 0 : '0 4px'`. Both values are
 * shape-keyed (pill vs round), so the custom property carries the
 * resolved shorthand. */
.manifold-renderer-badge-text {
  padding: var(--m-renderer-badge-text-pad, 0);
}

/* Phase 19 Plan 05d — ListRenderer header + row + button primitives.
 * ListRenderer is the most complex renderer in the status/list family
 * (23 inline-style sites pre-migration). It carries:
 *   - Optional header row (style.header=true): hairline bottom border,
 *     muted uppercase text, runtime-padded
 *   - Per-row tag (<div> or <a> depending on style.rowLink): clickable
 *     rows get a subtle accent backdrop on :hover and :focus, replacing
 *     the imperative onMouseEnter/Leave/Focus/Blur background mutation
 *     the pre-migration JSX used
 *   - Per-field span: state-driven text-align + per-field color via
 *     --m-renderer-color
 *   - Load-more button: dashed accent border + accent fg, muted state
 *     when exhausted (:disabled in CSS)
 *
 * The hover/focus declarative selectors keep the accent-tint at the
 * same 7%/9% color-mix the pre-migration hex-suffix trick produced
 * (${accent}12 ≈ 7%, ${accent}18 ≈ 9%). */
.manifold-renderer-list-header {
  border-bottom: 1px solid var(--mt-border);
  padding-bottom: var(--m-renderer-list-header-pb, 2px);
  margin-bottom: var(--m-renderer-list-header-mb, 2px);
  color: var(--mt-muted);
}
/* "no items", "No recent detections match your filter." — a sentence, so it
   asks for Body (#140). It sits INSIDE the list grid, which is set in the
   readings face to keep the columns aligned, so without this it inherited a
   table's typeface for a line of prose. It is the only child when it shows,
   so nothing needs to line up with it. Found by probing the deployed page
   rather than by reading the JSX: the element has no font of its own, so no
   grep for a font role would have turned it up. */
.manifold-renderer-list-empty {
  color: var(--mt-muted);
  padding: 4px 0;
  font-family: var(--m-font-sans);
}
.manifold-renderer-list-row {
  padding-top: var(--m-renderer-list-row-pt, 0);
  border-radius: var(--m-renderer-list-row-radius, 0);
  color: inherit;
}
.manifold-renderer-list-row[data-clickable="true"]:hover {
  background: color-mix(in srgb, var(--mt-accent) 7%, transparent);
}
.manifold-renderer-list-row[data-clickable="true"]:focus-visible {
  background: color-mix(in srgb, var(--mt-accent) 9%, transparent);
}
/* Phase 19 Plan 05d — ListRenderer load-more button. dashed-accent ring
 * for the active state; muted hairline when exhausted (publisher / paged
 * source has no older events). :disabled is the natively-correct hook —
 * the button element's `disabled` attribute is what the pre-migration
 * JSX was already setting. */
.manifold-renderer-list-loadmore {
  /* Phase 91.1: grid-column + justify-self make the button full-width in the
   * parent grid context; align-self:flex-start keeps it left in flex contexts.
   * Grid properties are ignored when the parent is not a grid container. */
  grid-column: 1 / -1;
  justify-self: start;
  align-self: flex-start;
  margin-top: 4px;
  padding: 3px 10px;
  background: transparent;
  color: var(--mt-accent);
  border: 1px dashed color-mix(in srgb, var(--mt-accent) 40%, transparent);
  border-radius: var(--m-radius-sm, 4px);
  cursor: pointer;
}
.manifold-renderer-list-loadmore:disabled {
  color: var(--mt-muted);
  border-color: var(--mt-border);
  cursor: default;
}
/* Phase 112 Plan 03 — maximize button on list/table cards (D-06).
 *
 * The button sits inside the .manifold-renderer-list-grid (a CSS grid
 * container). position:absolute takes it out of flow and anchors it to the
 * nearest positioned ancestor — which must be .manifold-renderer-list-grid
 * itself. The position:relative rule below provides that anchor.
 *
 * grid-column:unset keeps the grid column template from sizing the button
 * (absolutely-positioned grid children are still "in" the grid for sizing
 * purposes without this). z-index:2 floats it above row content.
 *
 * Reveal mechanic: opacity:0 by default → opacity:1 on [data-hover="true"].
 * data-hover is set by JSX via onPointerEnter/onPointerLeave (fires for
 * both mouse and touch — D-06: touch-reachable, not stranded behind :hover).
 * It is also kept true while the modal is open (maximizeOpen) so the user
 * can see what triggered the panel.
 *
 * All values use existing theme tokens — no hardcoded brand colors. */
.manifold-renderer-list-grid {
  position: relative;
}
.manifold-renderer-list-maximize {
  position: absolute;
  top: 4px;
  right: 8px;
  background: transparent;
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 30%, transparent);
  border-radius: var(--m-radius-sm, 4px);
  cursor: pointer;
  padding: 2px 6px;
  opacity: 0;
  transition: opacity 180ms;
  /* Keep out of the CSS grid flow (absolute children still participate in
   * column sizing without this — grid-column:unset removes that effect): */
  grid-column: unset;
  z-index: 2;
}
.manifold-renderer-list-maximize[data-hover="true"] {
  opacity: 1;
}
/* Phase 167 (VIEW-01) — the unified top-left explode button. Byte-for-byte
 * mirror of .manifold-renderer-list-maximize above, EXCEPT right:8px
 * becomes left:8px (opposite corner — D-02, keeps clear of the chart's
 * built-in top-right range control and the view-as adopt button, both
 * top-right). Do not reuse or restyle .manifold-renderer-list-maximize
 * itself; this is a separate, single, card-wrapper-level control. */
.manifold-card-explode {
  position: absolute;
  top: 4px;
  left: 8px;
  background: transparent;
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 30%, transparent);
  border-radius: var(--m-radius-sm, 4px);
  cursor: pointer;
  padding: 2px 6px;
  opacity: 0;
  transition: opacity 180ms;
  grid-column: unset;
  /* Above the whole card layer stack (surface < wm < content < frame border).
     At the old value the button tied with .cb-card-layer--content and lost on
     DOM order, so the content layer swallowed real clicks (2026-07-08 fix). */
  z-index: 4;
}
.manifold-card-explode[data-hover="true"] {
  opacity: 1;
}
/* EXPLODE-03 (Phase 190 Plan 03) — the 8 non-default corner overrides for
 * the explode button's chosen data-explode-pos (see sanitizeExplodeIconPos
 * in card-builder.jsx — the only 9 values that can ever reach this
 * selector). The base rule above IS the 'top-left' position and stays the
 * default (no attribute needed for it — an unset/old card renders exactly
 * as before). Each override clears the base rule's opposite edges (setting
 * them to auto) before pinning its own edge(s), and applies a translate for
 * the middle/center positions so the button's visual box stays centered on
 * that edge/spot the same way the alignment idiom's ALIGN_MAP corners do. */
.manifold-card-explode[data-explode-pos="top-center"] {
  left: 50%;
  right: auto;
  transform: translateX(-50%);
}
.manifold-card-explode[data-explode-pos="top-right"] {
  left: auto;
  right: 8px;
}
.manifold-card-explode[data-explode-pos="middle-left"] {
  top: 50%;
  transform: translateY(-50%);
}
.manifold-card-explode[data-explode-pos="middle-center"] {
  top: 50%;
  left: 50%;
  right: auto;
  transform: translate(-50%, -50%);
}
.manifold-card-explode[data-explode-pos="middle-right"] {
  top: 50%;
  left: auto;
  right: 8px;
  transform: translateY(-50%);
}
.manifold-card-explode[data-explode-pos="bottom-left"] {
  top: auto;
  bottom: 4px;
}
.manifold-card-explode[data-explode-pos="bottom-center"] {
  top: auto;
  bottom: 4px;
  left: 50%;
  right: auto;
  transform: translateX(-50%);
}
.manifold-card-explode[data-explode-pos="bottom-right"] {
  top: auto;
  bottom: 4px;
  left: auto;
  right: 8px;
}
/* Phase 91 Plan 04 — sortable list-header affordance.
 * Applied to individual header span elements when style.sortable is on.
 * cursor:pointer signals clickability; hover tint via color-mix accent
 * (same 7% tint used by clickable rows above). The sort arrow itself uses
 * .manifold-renderer-list-sort-indicator below. */
.manifold-renderer-list-header-sortable {
  cursor: pointer;
  user-select: none;
}
.manifold-renderer-list-header-sortable:hover {
  color: color-mix(in srgb, var(--mt-accent) 70%, var(--mt-muted));
}
/* Sort direction indicator — arrow glyph (▲/▼/▾) shown in the active
 * sort column (accent) or all sortable columns when not yet sorted (muted).
 * Color driven by --m-renderer-color set per-instance in JSX (same
 * pattern as .manifold-renderer-action-sort on TableActionResultRenderer). */
.manifold-renderer-list-sort-indicator {
  color: var(--m-renderer-color, var(--mt-muted));
  font-size: 9px;
  margin-left: 3px;
  flex-shrink: 0;
}
/* Phase 91.1 — CSS subgrid parent container for list/table cards.
 * The scroll-body <div> gains this class + a data-gridlines attribute
 * (none/vertical/horizontal/both) that drives the rules below.
 * Scoped under .manifold-renderer-list-grid so these rules don't leak
 * to other renderers. Color is var(--mt-border) — same token the old
 * inline separators borderTop used, so existing cards with separators:true
 * (effectiveGridlines='horizontal') are pixel-identical post-upgrade. */

/* Full-width grid items: load-more button + envelope rows always span all
 * tracks. load-more keeps its left-aligned appearance via justify-self:start
 * (align-self:flex-start was a flex-only prop; this is the grid equivalent). */
.manifold-renderer-list-grid .manifold-renderer-list-loadmore {
  grid-column: 1 / -1;
  justify-self: start;
}
.manifold-renderer-list-grid .manifold-renderer-action-row[data-envelope] {
  grid-column: 1 / -1;
}

/* Horizontal gridlines — border-top on data rows that follow another data row.
 * Adjacent-sibling selector skips the first row (no border above the first
 * item), matching the old separators && idx > 0 behavior exactly (D-06). */
.manifold-renderer-list-grid[data-gridlines="horizontal"] .manifold-renderer-list-row + .manifold-renderer-list-row,
.manifold-renderer-list-grid[data-gridlines="both"] .manifold-renderer-list-row + .manifold-renderer-list-row {
  border-top: 1px solid var(--mt-border);
}

/* Vertical gridlines — right border on every cell except the last in each
 * row + header. padding-right keeps text from touching the line. */
.manifold-renderer-list-grid[data-gridlines="vertical"] .manifold-renderer-list-row > span:not(:last-child),
.manifold-renderer-list-grid[data-gridlines="both"] .manifold-renderer-list-row > span:not(:last-child),
.manifold-renderer-list-grid[data-gridlines="vertical"] .manifold-renderer-list-header > span:not(:last-child),
.manifold-renderer-list-grid[data-gridlines="both"] .manifold-renderer-list-header > span:not(:last-child) {
  border-right: 1px solid var(--mt-border);
  padding-right: 6px;
}

/* FRIG-02 Task 4 — scrollable detection-list body.
 * Applied alongside .manifold-renderer-list-grid (same element) so the row
 * grid remains a CSS grid container — adding a wrapper div would break the
 * grid-column:1/-1 spans used by header rows, load-more buttons, and
 * envelope rows. max-height:100% lets the card's flex layout bound the
 * height; overflow-y:auto adds the scrollbar only when rows exceed that
 * bound. Named cb-list-scroll to match the cb-frigate-* class convention. */
.cb-list-scroll {
  max-height: 100%;
  overflow-y: auto;
}

/* Phase 96 Plan 04 (POLISH-05) — themed scrollbar on the list/table card
 * scroll container. The JSX sets overflow:auto on .manifold-renderer-list-grid
 * so the OS-default scrollbar shows when rows overflow. Style it with theme
 * tokens so it follows theme switches — never hardcoded colors.
 *
 * WebKit/Chromium: ::-webkit-scrollbar family (width, track, thumb, hover).
 * Firefox/non-WebKit: scrollbar-width:thin + scrollbar-color shorthand.
 *
 * Token choices — matches the card surface and reads cleanly across themes:
 *   track  → --mt-bg-elev  (subtle, just slightly lighter than card bg)
 *   thumb  → --mt-border-strong  (visible but not loud; same token as grid dividers)
 *   hover  → --mt-dim  (warmer, shows interactivity without jarring contrast)
 * Literal fallbacks match the default-theme values; themes override via token. */
.manifold-renderer-list-grid {
  scrollbar-width: thin;
  scrollbar-color: var(--mt-border-strong, #353c47) var(--mt-bg-elev, #1d2128);
}
.manifold-renderer-list-grid::-webkit-scrollbar {
  width: 4px;
  height: 4px;
}
.manifold-renderer-list-grid::-webkit-scrollbar-track {
  background: var(--mt-bg-elev, #1d2128);
}
.manifold-renderer-list-grid::-webkit-scrollbar-thumb {
  background: var(--mt-border-strong, #353c47);
  border-radius: 2px;
}
.manifold-renderer-list-grid::-webkit-scrollbar-thumb:hover {
  background: var(--mt-dim, #8b8985);
}

/* Phase 91 Plan 04 — inline progress bar cell (format: 'progress').
 * Track is a relative container; fill is absolutely-positioned so it
 * doesn't push text. The percentage label sits above the fill (z-index:1)
 * and is centered. Only the dynamic `width: X%` is set inline by JSX;
 * all aesthetic constants live here. */
.manifold-progress-bar-cell {
  position: relative;
  width: 100%;
  height: 1.1em;
  background: color-mix(in srgb, var(--mt-accent) 12%, transparent);
  border-radius: 2px;
  overflow: hidden;
  flex-shrink: 0;
}
.manifold-progress-bar-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: color-mix(in srgb, var(--mt-accent) 55%, transparent);
  border-radius: 2px;
  transition: width 0.3s ease;
}
.manifold-progress-bar-label {
  position: relative;
  z-index: 1;
  display: block;
  text-align: center;
  font-size: 0.9em;
  color: var(--mt-fg);
  white-space: nowrap;
  overflow: hidden;
}
/* Phase 19 Plan 05d — dynamic text-align escape hatch. Drives
 *   text-align: var(--m-text-align)
 * from a per-element custom property so field-driven alignment (e.g.
 * `field.align: 'right'` per ListRenderer column) survives without an
 * inline textAlign key. */
.manifold-text-align-dyn { text-align: var(--m-text-align, left); }

/* Phase 19 Plan 05e — BundledIcon SVG color. The pre-migration JSX wrote
 * `style={{color: color || 'currentColor'}}` on the outer <svg> element so
 * inner attrs using currentColor (e.g. multi-element weather icons) would
 * resolve to the outer wrapper's color. Migrating to a class that reads
 * `var(--m-renderer-color, currentColor)` preserves the same fallback
 * semantics: when the caller passes a `color` prop, the JSX sets the var
 * on the inline style (state-driven escape hatch); when the caller omits
 * it, the class falls back to `currentColor` which inherits from the
 * parent text-color cascade. Used by SvgRenderer's icon mode AND every
 * icon-library picker thumbnail across the gear → icon-picker UI. */
.manifold-bundled-icon { color: var(--m-renderer-color, currentColor); }

/* Phase 19 Plan 05e — action-result family shared vocabulary.
 *
 * The four action-result renderers (List / Table / Image / Text) display
 * the response of clicking a service-action button. Pre-migration each
 * renderer baked the same handful of visual primitives inline:
 * monospace font, ellipsis-truncate row text, runtime-sized chrome,
 * accent-vs-destructive button color signal. Centralizing the vocabulary
 * here means the four renderers compose the same classes instead of
 * repeating the same inline-style trios. */

/* Font-family escape hatches — the action-result family pre-migration
 * wrote `fontFamily: '"JetBrains Mono",monospace'` (~12 sites) and
 * `fontFamily: 'var(--m-font-sans)'` (1 site) in
 * inline style. The theme token (--m-font-mono / --m-font-sans) is
 * already defined in :root; the escape-hatch class just routes the
 * inline `fontFamily` key off the audit-watched surface with the same
 * fallback ladder the pre-migration JSX carried.
 *
 * Two classes (not one) because the action-result renderer family has
 * TWO distinct font choices: monospace for tabular/list data + text
 * blocks marked `monospace: true`, sans-serif for narrative text blocks.
 * Tagging both as "font-mono" is wrong; tagging both as "font" is
 * ambiguous.
 *
 * #140 added the third. There is one class per ROLE now, and the renderer
 * picks between them on what the text MEANS — fetched data and table
 * columns on mono, prose and controls on sans, the uppercase captions that
 * name a thing on display. Before #140 forty-four renderer elements wore
 * `-font-mono` regardless, which is half of why the Headings control moved
 * nothing and the Numbers control moved three words for every number. The
 * header block in card-builder-renderers.jsx is the full rule. */
.manifold-renderer-font-mono { font-family: var(--m-font-mono); }
.manifold-renderer-font-sans { font-family: var(--m-font-sans); }
.manifold-renderer-font-display { font-family: var(--m-font-display); }

/* Truncate-with-ellipsis — three CSS properties (overflow, text-overflow,
 * white-space) always set together. Pre-migration the trio repeated
 * across every cell + row span in the action-list/table renderers (8+
 * sites). Bundling reads as one visual primitive ("truncate this cell")
 * and reduces inline-style churn. */
.manifold-renderer-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Action-list outer wrap. The pre-migration JSX wrote:
 *   { width:'100%', height:'100%', display:'flex', flexDirection:'column',
 *     gap: rowGap, fontFamily:'"JetBrains Mono",monospace', fontSize,
 *     padding: 'var(--m-pad-md, 10px)' }
 * The rowGap and fontSize are state-driven (compact mode reduces both);
 * everything else is structural. Routing rowGap + fontSize through CSS
 * custom properties moves the audit-watched keys off the inline-style
 * surface. The class composes width/height/display/flex-direction/padding
 * and reads the runtime gap + fontSize from per-element vars. */
.manifold-renderer-action-list {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--m-renderer-action-list-gap, 4px);
  font-size: var(--m-renderer-action-list-fs, 11px);
  padding: var(--m-pad-md, 10px);
}

/* Action-result row. Used by both ListActionResultRenderer and
 * _EnvelopeRow (its envelope-shape sibling). Pre-migration:
 *   { display:'flex', alignItems:'baseline', gap: <state>,
 *     borderTop: separators && idx > 0 ? '1px solid var(...)' : 'none',
 *     paddingTop: separators && idx > 0 ? Math.max(2, rowGap) : 0,
 *     minWidth: 0 }
 * Display + alignment + minWidth are structural; gap + borderTop + pt
 * are state-driven (vary with separators flag and row position). The
 * class composes the structural keys and reads the state-driven ones
 * from custom properties. borderTop carries an `0` fallback so rows
 * without separators read as `border-top: 0 solid transparent`. */
.manifold-renderer-action-row {
  display: flex;
  align-items: baseline;
  min-width: 0;
  gap: var(--m-renderer-action-row-gap, 6px);
  border-top: var(--m-renderer-action-row-border-top, 0 solid transparent);
  padding-top: var(--m-renderer-action-row-pt, 0);
}

/* Envelope row "type chip" — small bordered pill carrying the event type
 * (e.g. "grab", "import"). Pre-migration:
 *   { fontSize: fontSize - 1, padding:'1px 5px', borderRadius:3,
 *     border: '1px solid ${typeColor}55', color: typeColor,
 *     flexShrink:0, lineHeight:'14px', fontFamily:'"JetBrains Mono",monospace' }
 * typeColor is `var(--m-accent, #7aa8f0)` — a single accent. Border
 * carries 33% tint via color-mix; fontSize is runtime. The flex-shrink
 * + line-height + radius + padding stay class-baked because they don't
 * vary with state. */
.manifold-renderer-envelope-chip {
  padding: 1px 5px;
  border-radius: 3px;
  border: 1px solid color-mix(in srgb, var(--m-renderer-color, var(--mt-accent)) 33%, transparent);
  color: var(--m-renderer-color, var(--mt-accent));
  flex-shrink: 0;
  line-height: 14px;
  font-size: var(--m-renderer-chip-fs, 10px);
}

/* Action-row button — the destructive-vs-accent affordance attached to
 * each row inside list/table action results. Pre-migration:
 *   { fontSize: 10-11, padding:'2px 8px',
 *     color: destructive ? var(--m-error) : var(--m-accent),
 *     background:'transparent',
 *     border: `1px solid ${destructive ? 'var(--m-error)' : 'var(--m-accent)'}55`,
 *     borderRadius:3,
 *     cursor: rowPending ? 'wait' : 'pointer' }
 * The destructive-vs-accent color is state-driven; everything else is
 * static. Routing the color through --m-renderer-color (set by the JSX
 * to var(--m-error) for destructive rows, var(--m-accent) otherwise)
 * preserves the per-row semantic while moving the inline color/border
 * keys off the audit surface. cursor:wait when rowPending stays inline
 * via a separate hook OR via a `data-pending` attr selector; we use
 * the attr-selector pattern so the cursor key moves off too. */
.manifold-renderer-action-rowbtn {
  background: transparent;
  color: var(--m-renderer-color, var(--mt-accent));
  border: 1px solid color-mix(in srgb, var(--m-renderer-color, var(--mt-accent)) 33%, transparent);
  border-radius: 3px;
  padding: 2px 8px;
  cursor: pointer;
}
.manifold-renderer-action-rowbtn[data-pending="true"] { cursor: wait; }
.manifold-renderer-action-rowbtn:disabled {
  cursor: wait;
}

/* Action-list dashed-accent load-more button. Same posture as
 * .manifold-renderer-list-loadmore but with a different visual budget
 * (4px margin-top vs ListRenderer's variants, smaller padding). The
 * fontSize is state-driven (compact mode shrinks it). */
.manifold-renderer-action-loadmore {
  align-self: flex-start;
  margin-top: 4px;
  padding: 3px 10px;
  background: transparent;
  color: var(--mt-accent);
  border: 1px dashed var(--mt-accent);
  border-radius: var(--m-radius-sm, 4px);
  cursor: pointer;
  font-size: var(--m-renderer-action-loadmore-fs, 10px);
}

/* Action-table outer wrap. Pre-migration:
 *   { width:'100%', height:'100%', overflow: sticky ? 'auto' : 'visible',
 *     fontFamily:'"JetBrains Mono",monospace', fontSize:11,
 *     padding: 'var(--m-pad-sm, 6px)' }
 * overflow varies with sticky flag; everything else is structural. We
 * use a `data-sticky="true"` attr selector for the auto-overflow variant
 * so both inline-style keys (overflow, font-family, font-size, padding)
 * move off the surface. */
.manifold-renderer-action-table {
  width: 100%;
  height: 100%;
  overflow: visible;
  font-size: 11px;
  padding: var(--m-pad-sm, 6px);
}
.manifold-renderer-action-table[data-sticky="true"] { overflow: auto; }

/* Action-table inner grid. Pre-migration:
 *   { display:'grid', gridTemplateColumns: <runtime>, rowGap:0,
 *     columnGap:8, minWidth:'100%' }
 * gridTemplateColumns is fully runtime (varies with column count + widths)
 * so it stays inline via a custom property. */
.manifold-renderer-action-grid {
  display: grid;
  row-gap: 0;
  column-gap: 8px;
  min-width: 100%;
  grid-template-columns: var(--m-renderer-action-grid-cols, 1fr);
}

/* Action-table header cell. Pre-migration:
 *   { position: sticky ? 'sticky' : 'static', top: 0,
 *     background:'var(--mt-bg-elev, var(--mt-bg))',
 *     color:'var(--m-text-muted, var(--mt-muted))',
 *     fontSize:'var(--mt-text-sm-size, 10px)',
 *     fontWeight:700, letterSpacing:0.4, textTransform:'uppercase',
 *     padding:'4px 6px',
 *     borderBottom: '1px solid var(--m-border, var(--mt-border))',
 *     cursor: col.sortable ? 'pointer' : 'default',
 *     display:'flex', alignItems:'center', gap:4,
 *     textAlign: col.align || 'left' }
 * position varies with sticky; cursor varies with sortable; textAlign
 * is runtime per column. Bundling the bulk of properties + routing the
 * three state-driven ones via attr selectors + --m-text-align var. */
.manifold-renderer-action-thead {
  position: static;
  top: 0;
  background: var(--mt-bg-elev, var(--mt-bg));
  color: var(--mt-muted);
  font-size: var(--mt-text-sm-size, 10px);
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  /* A column header, so it follows Headings (#140) — the same as ListRenderer's
     and the exploded list's. It has to say so HERE rather than inherit, because
     the .manifold-renderer-action-table wrapper around it stays on the readings
     face to keep the body cells in their columns. Without this line a list
     card's headers restyle on a Headings change and an action table's do not,
     which is the reach gap #140 exists to close. */
  font-family: var(--m-font-display);
  padding: var(--m-pad-xs, 4px) calc(var(--m-pad-xs, 4px) * 1.5);
  border-bottom: 1px solid var(--mt-border);
  cursor: default;
  display: flex;
  align-items: center;
  gap: 4px;
}
.manifold-renderer-action-thead[data-sticky="true"] { position: sticky; }
.manifold-renderer-action-thead[data-sortable="true"] { cursor: pointer; }

/* Action-table sort arrow — the small ▲/▼/▾ glyph in the header cell.
 * Pre-migration the color flips between accent (this column is sorted)
 * and muted (not sorted) via a JSX ternary inline. The arrow's color
 * routes through --m-renderer-color (set by the JSX) so the class only
 * supplies the fontSize + the var consumption. */
.manifold-renderer-action-sort {
  color: var(--m-renderer-color, var(--mt-muted));
  font-size: 9px;
}

/* Action-table data cell. Pre-migration:
 *   { padding:'4px 6px', color:'var(--m-text, var(--mt-fg))',
 *     background: idx % 2 ? 'var(--mt-bg-elev, transparent)' : 'transparent',
 *     overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap',
 *     textAlign: col.align || 'left' }
 * Background is state-driven (zebra-striping); textAlign per column.
 * Routes zebra bg through `data-zebra="true"` attr selector and reuses
 * `.manifold-renderer-truncate` + `.manifold-text-align-dyn` for the
 * overflow trio + alignment respectively.
 *
 * #105: the four paddings in this table (header, data cell, row-action cell,
 * header spacer) ride the Spacing ramp instead of the 4px/3px/6px literals
 * they were. --m-pad-xs is 4px at the Cosy notch, so each calc reproduces its
 * old value to the pixel and moves only when somebody moves the knob — which
 * is what "a table rides the Spacing ramp" means: Tight is a denser table, and
 * nobody had to set a row height to get it. The zebra fill above already
 * resolves a role (--mt-bg-elev) rather than a hardcoded tint, so it needed
 * nothing. */
.manifold-renderer-action-cell {
  padding: var(--m-pad-xs, 4px) calc(var(--m-pad-xs, 4px) * 1.5);
  color: var(--mt-fg);
  background: transparent;
}
.manifold-renderer-action-cell[data-zebra="true"] { background: var(--mt-bg-elev, transparent); }

/* Action-table row-action cell — the right-side column carrying the
 * per-row action buttons. Same zebra-striping shape as
 * .manifold-renderer-action-cell but with inline-flex + tight gap so the
 * button strip lays out horizontally. */
.manifold-renderer-action-rowact-cell {
  padding: calc(var(--m-pad-xs, 4px) * 0.75) calc(var(--m-pad-xs, 4px) * 1.5);
  display: inline-flex;
  gap: var(--m-pad-xs, 4px);
  background: transparent;
}
.manifold-renderer-action-rowact-cell[data-zebra="true"] { background: var(--mt-bg-elev, transparent); }

/* Action-table header spacer cell — the empty header above the
 * row-action column. Same sticky + bg + border-bottom as
 * .manifold-renderer-action-thead but without the typographic chrome
 * (this cell carries no text). */
.manifold-renderer-action-thead-spacer {
  position: static;
  top: 0;
  background: var(--mt-bg-elev, var(--mt-bg));
  border-bottom: 1px solid var(--mt-border);
  padding: var(--m-pad-xs, 4px) calc(var(--m-pad-xs, 4px) * 1.5);
}
.manifold-renderer-action-thead-spacer[data-sticky="true"] { position: sticky; }

/* Action-image loading placeholder. Pre-migration:
 *   { aspectRatio:'16/9', background:'var(--mt-bg-elev, var(--mt-bg))',
 *     display:'flex', alignItems:'center', justifyContent:'center',
 *     color:'var(--m-text-muted, var(--mt-muted))',
 *     fontSize:'var(--mt-text-sm-size, 11px)',
 *     border:'1px solid var(--m-border, var(--mt-border))',
 *     borderRadius:'var(--m-radius-md, 6px)' }
 * All static — bundled into one class. The 16/9 aspect ratio + center
 * alignment + bg-elev + muted text reads as one visual primitive
 * ("loading thumbnail in a 16:9 frame"). */
.manifold-renderer-action-image-loading {
  aspect-ratio: 16 / 9;
  background: var(--mt-bg-elev, var(--mt-bg));
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--mt-muted);
  font-size: var(--mt-text-sm-size, 11px);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-md, 6px);
}

/* Action-image frame — the displayed-image container. Pre-migration:
 *   { display:'flex', justifyContent:'center', alignItems:'center',
 *     background:'var(--m-bg, var(--mt-bg))',
 *     border:'1px solid var(--m-border, var(--mt-border))',
 *     padding:'var(--m-pad-sm, 6px)',
 *     borderRadius:'var(--m-radius-md, 6px)',
 *     width:'100%', height:'100%' } */
.manifold-renderer-action-image-frame {
  display: flex;
  justify-content: center;
  align-items: center;
  background: var(--mt-bg);
  border: 1px solid var(--mt-border);
  padding: var(--m-pad-sm, 6px);
  border-radius: var(--m-radius-md, 6px);
  width: 100%;
  height: 100%;
}

/* Action-image <img> tag — the actual image element. Pre-migration:
 *   { maxWidth:'100%', maxHeight:'min(70vh, 480px)', objectFit:'contain' }
 * All static. */
.manifold-renderer-action-image-img {
  max-width: 100%;
  max-height: min(70vh, 480px);
  object-fit: contain;
}

/* Action-text body — the pre-wrap text block. Pre-migration:
 *   { whiteSpace:'pre-wrap',
 *     fontSize:'var(--mt-text-md-size, 12px)',
 *     lineHeight:1.5,
 *     color:'var(--m-text, var(--mt-fg))',
 *     padding:'var(--m-pad-md, 10px)',
 *     fontFamily: <state — mono if response.monospace, else sans>,
 *     maxHeight: overflow ? '60vh' : 'none',
 *     overflow: overflow ? 'auto' : 'visible',
 *     width:'100%' }
 * font-family is state-driven (monospace flag); maxHeight + overflow
 * are state-driven (lineCount > 80). The class bundles the static keys
 * and the JSX composes either `.manifold-renderer-font-mono` or
 * `.manifold-renderer-font-sans` on top, plus a `data-overflow="true"`
 * attr-selector variant for the scroll-clip path. */
.manifold-renderer-action-text {
  white-space: pre-wrap;
  font-size: var(--mt-text-md-size, 12px);
  line-height: 1.5;
  color: var(--mt-fg);
  padding: var(--m-pad-md, 10px);
  width: 100%;
  max-height: none;
  overflow: visible;
}
.manifold-renderer-action-text[data-overflow="true"] {
  max-height: 60vh;
  overflow: auto;
}

/* Action-text empty placeholder. Pre-migration:
 *   { color:'var(--m-text-muted, var(--mt-muted))',
 *     fontStyle:'italic',
 *     padding:'var(--m-pad-md, 10px)',
 *     fontSize:'var(--mt-text-md-size, 12px)' } */
.manifold-renderer-action-text-empty {
  color: var(--mt-muted);
  font-style: italic;
  padding: var(--m-pad-md, 10px);
  font-size: var(--mt-text-md-size, 12px);
}

/* Action-list / action-table empty placeholder. Used by both the list
 * and table action-result renderers when their items[] is empty.
 * Center-aligned muted text in a generous padding bucket. */
.manifold-renderer-action-list-empty {
  color: var(--mt-muted);
  padding: var(--m-pad-lg, 12px);
  text-align: center;
  font-size: var(--mt-text-sm-size, 11px);
}

/* Phase 19 Plan 05d — LogStreamRenderer load-more button. Shape diverges
 * from ListRenderer's dashed-accent button: LogStream's variant is a
 * borderless transparent accent-text affordance, more like a textual
 * "+ Load more" link than a chunky chip. Padding is symmetrical-vertical
 * only ('4px 0'). */
.manifold-renderer-logstream-loadmore {
  background: transparent;
  border: none;
  color: var(--mt-accent);
  padding: 4px 0;
  text-align: left;
  cursor: pointer;
}

/* Phase 19 Plan 05f — service-family escape-hatch classes (helpers + 4
 * renderers).  Authoring rules same as 19-05a..e:
 *   - Aesthetic values route through `--mt-*` tokens (theme-skinnable) or
 *     `--m-renderer-*` custom properties set inline (state-driven values
 *     that the audit script no longer flags as FORBIDDEN).
 *   - Per-class differentiation that the runtime computes (status color,
 *     per-detection-class bbox color, per-service brand color) flows
 *     through `--m-renderer-color` / `--m-renderer-brand` etc.; the class
 *     consumes the prop once for foreground and once for `color-mix(...)`
 *     tinted background + border (replaces the pre-migration `${c}15` /
 *     `${c}66` hex-suffix tricks, which only happened to work because both
 *     sides were 6-digit hex resolved at runtime).
 * --------------------------------------------------------------------- */

/* ServiceMethodButton — three chrome modes (default pill / minimal text /
 * invisible overlay), all routing the state-driven status color through
 * `--m-renderer-color`.  `data-pending="true"` flips cursor to `wait`;
 * `data-gated="true"` greys the button to communicate "this verb is
 * locked for your role" (preserves the pre-migration `opacity:0.55 +
 * cursor:not-allowed` semantic — losing it would make a sensitive verb
 * look identical to an active one, a real safety regression per
 * feedback_no_shell_via_browser). */
.manifold-renderer-svc-btn {
  font-family: var(--m-font-mono);
  font-size: var(--m-renderer-value-size);
  font-weight: var(--mt-weight-semibold);
  color: var(--m-renderer-color);
  background: color-mix(in srgb, var(--m-renderer-color) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--m-renderer-color) 40%, transparent);
  border-radius: 5px;
  padding: 4px 10px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.manifold-renderer-svc-btn-minimal {
  font-family: var(--m-font-mono);
  font-size: var(--m-renderer-value-size);
  font-weight: var(--mt-weight-semibold);
  color: var(--m-renderer-color);
  background: transparent;
  border: none;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: color 0.15s;
}
.manifold-renderer-svc-btn-invisible {
  position: absolute;
  inset: 0;
  background: transparent;
  border: none;
  padding: 0;
  display: block;
  cursor: pointer;
}
.manifold-renderer-svc-btn[data-pending="true"],
.manifold-renderer-svc-btn-minimal[data-pending="true"],
.manifold-renderer-svc-btn-invisible[data-pending="true"] { cursor: wait; }
.manifold-renderer-svc-btn[data-gated="true"],
.manifold-renderer-svc-btn-minimal[data-gated="true"],
.manifold-renderer-svc-btn[data-gated="true"]:disabled,
.manifold-renderer-svc-btn-minimal[data-gated="true"]:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* ServiceNavigateLink — three chrome modes mirror the button helper's
 * shape but as <a> anchors (text-decoration:none, line-height:1).
 * Sensitive-vs-routine accent flows through --m-renderer-color (set
 * inline; sensitive uses t.warn, routine uses t.accent or style.color
 * override). Same color-mix tint pattern for the pill background +
 * border. */
.manifold-renderer-svc-nav {
  font-family: var(--m-font-mono);
  font-size: var(--m-renderer-value-size);
  font-weight: var(--mt-weight-semibold);
  text-decoration: none;
  color: var(--m-renderer-color);
  background: color-mix(in srgb, var(--m-renderer-color) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--m-renderer-color) 25%, transparent);
  border-radius: 5px;
  padding: 4px 10px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  line-height: 1;
}
.manifold-renderer-svc-nav-minimal {
  font-family: var(--m-font-mono);
  font-size: var(--m-renderer-value-size);
  font-weight: var(--mt-weight-semibold);
  text-decoration: none;
  color: var(--m-renderer-color);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  line-height: 1;
  cursor: pointer;
}
.manifold-renderer-svc-nav-invisible {
  position: absolute;
  inset: 0;
  display: block;
  background: transparent;
  text-decoration: none;
  cursor: pointer;
}

/* ServiceNavigateLink inner spans: trailing `↗` (subdued, opacity 0.85)
 * and label text with optional ellipsis trio.  The label-trunc variant
 * pairs with .manifold-renderer-truncate (already shipped 19-05e); the
 * non-truncating variant is the default. */
.manifold-renderer-svc-nav-arrow {
  font-size: var(--m-renderer-arrow-size);
  opacity: 0.85;
}

/* ServiceAuthorityIcon — compact owner-only authority indicator inside
 * ServicePlacardRenderer's action cluster. Tiny padlock glyph (🔒 or
 * 🔓) that doubles as state indicator + toggle button.
 *
 * SAFETY-CRITICAL VISUAL DIFFERENTIATION (per feedback_no_shell_via_browser):
 *   - data-overridden="true"  -> opacity:1 + accent-tinted drop-shadow
 *     halo. Signals "owner has actively pinned this gate to a non-default
 *     value." Visible chrome reads as a deliberate choice.
 *   - data-overridden absent  -> opacity:0.55 + no halo. Reads as
 *     "catalog default; click to override."  Losing the visual gap would
 *     erase the at-a-glance signal of which services have an explicit
 *     owner override active — a real safety regression.
 *
 *   Click-pending state (data-pending) flips cursor to `wait` without
 *   disturbing the overridden halo.  The locked emoji vs unlocked emoji
 *   (🔒 vs 🔓) is text content in the JSX; the CSS only carries the
 *   differentiated chrome around it. */
.manifold-renderer-svc-authority-icon {
  background: transparent;
  border: none;
  padding: 0 4px;
  margin-left: 2px;
  cursor: pointer;
  font-size: 11px;
  line-height: 1;
  opacity: 0.55;
  filter: none;
  transition: opacity 180ms, filter 180ms;
}
.manifold-renderer-svc-authority-icon[data-overridden="true"] {
  opacity: 1;
  filter: drop-shadow(0 0 4px color-mix(in srgb, var(--mt-accent) 40%, transparent));
}
.manifold-renderer-svc-authority-icon[data-pending="true"] { cursor: wait; }
.manifold-renderer-svc-authority-icon:disabled { cursor: wait; }

/* ServiceAuthorityToggle — owner-only meta-control rendered beneath a
 * ServiceActionsRenderer cluster.  One-line monospace strip:
 *   "authority: admin ·overridden  [ delegate to admins ]"
 *
 * Visual semantics (same safety-critical posture as ServiceAuthorityIcon):
 *   - The "·overridden" marker (accent color, ml 4px) is the at-a-glance
 *     signal that the owner has pinned this service's gate.  It paints
 *     only when overridden is true; absence means "catalog default."
 *   - The flip button color flows through --m-renderer-color (warn by
 *     default, live on success flash, error on failure flash) + a
 *     color-mix 40% tint on the border, replacing the pre-migration
 *     `${color}66` hex-suffix trick. */
.manifold-renderer-svc-authority-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 4px;
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-dim);
  opacity: 0.78;
}
.manifold-renderer-svc-authority-current {
  color: var(--mt-muted);
  font-weight: var(--mt-weight-semibold);
}
.manifold-renderer-svc-authority-overridden-marker {
  color: var(--mt-accent);
  margin-left: 4px;
}
.manifold-renderer-svc-authority-flip-btn {
  font-family: inherit;
  font-size: inherit;
  color: var(--m-renderer-color, var(--mt-warn));
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--m-renderer-color, var(--mt-warn)) 40%, transparent);
  border-radius: 3px;
  padding: 1px 6px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.manifold-renderer-svc-authority-flip-btn[data-pending="true"] { cursor: wait; }
.manifold-renderer-svc-authority-flip-btn:disabled { cursor: wait; }

/* EmptyCardBody — shared "no data yet" shell for Donut / StackedBarsTime
 * / Distribution renderers and any future renderer that wants the same
 * empty-state chrome.  Two classes:
 *   .manifold-renderer-empty-card-body  — outer flex column, center
 *     align, padding 10px 12px by default (suppressed by data-nopad="true"
 *     so renderers passing noPad get edge-to-edge layout).
 *   .manifold-renderer-empty-card-msg   — the small muted mono message
 *     (font-size 11px) describing why the card is empty.  Composes
 *     with .manifold-renderer-font-mono (shipped 19-05e) for the
 *     JetBrains Mono fallback chain — no need to coin a new family. */
.manifold-renderer-empty-card-body {
  width: 100%;
  height: 100%;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.manifold-renderer-empty-card-body[data-nopad="true"] { padding: 0; }
.manifold-renderer-empty-card-msg {
  color: var(--mt-muted);
  font-size: 11px;
}

/* ServicePlacardRenderer — rack-placard row: brand stripe + glowing
 * status LED on the left, brand icon, name+desc+group stack, optional
 * critical badge, launch arrow, action button cluster.  Designed for
 * use inside a service-repeat card where each repeat-iteration produces
 * one placard.  Karl 2026-05-09.
 *
 * State-driven values that flow through custom properties:
 *   --m-brand           — service brand color (svc.brand || t.accent).
 *                         Consumed by .placard outer wrap for the
 *                         left-edge gradient wash, .placard-stripe for
 *                         the colored bar, .placard-icon's BundledIcon
 *                         tint, and .placard-group-chip for the chip
 *                         outline / fill / text.
 *   --m-renderer-color  — LED status color (resolved from stateValue
 *                         via _serviceStateColor).  Halo + border use
 *                         color-mix tints of this prop.
 *   data-hover="true"   — set on every element whose chrome shifts on
 *                         hover (gradient brightness, stripe halo,
 *                         launch arrow color/position, actions cluster
 *                         opacity, critical badge opacity).
 *   data-state="ok|warn|fault" — drives the LED's animation + halo
 *                         (ok: pulsing accent halo; warn: pulsing
 *                         warn halo; fault: static dim halo; other:
 *                         no halo or animation).  See keyframes
 *                         manifold-led-pulse / -warn already shipped.
 *   data-fault="true"   — on the icon box, applies grayscale(1)
 *                         opacity(0.35) so a failed service icon reads
 *                         as visually disabled.
 *
 * Why per-state attr instead of N classes: the JSX already computes
 * isOk / isWarn / isFault as booleans; a single data-state attribute
 * carrying one of {ok, warn, fault, other} keeps the JSX wiring trivial
 * and the CSS rules adjacent in this block. */
.manifold-renderer-svc-placard {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  gap: 14px;
  padding-left: 22px;
  padding-right: 16px;
  padding-top: 10px;
  padding-bottom: 10px;
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--m-brand) 10%, transparent) 0%,
      transparent 38%),
    linear-gradient(155deg, var(--mt-bg), var(--mt-muted));
  background-blend-mode: normal;
  border-top: 1px solid var(--mt-border);
  border-bottom: 1px solid var(--mt-border);
  border-right: 1px solid var(--mt-border);
  cursor: default;
  transition: background 180ms ease-out;
  isolation: isolate;
}
.manifold-renderer-svc-placard[data-hover="true"] {
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--m-brand) 20%, transparent) 0%,
      transparent 38%),
    linear-gradient(155deg, var(--mt-bg), var(--mt-muted));
}
.manifold-renderer-svc-placard-stripe {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--m-brand);
  transition: box-shadow 200ms ease-out;
}
.manifold-renderer-svc-placard-stripe[data-hover="true"] {
  box-shadow:
    2px 0 18px 0 color-mix(in srgb, var(--m-brand) 47%, transparent),
    2px 0  6px 0 color-mix(in srgb, var(--m-brand) 67%, transparent);
}
.manifold-renderer-svc-placard-led {
  position: absolute;
  left: 7px; top: 50%;
  transform: translateY(-50%);
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--m-renderer-color);
  border: 1.5px solid var(--mt-bg);
  z-index: 2;
}
.manifold-renderer-svc-placard-led[data-state="ok"] {
  box-shadow: 0 0 8px var(--m-renderer-color);
  animation: manifold-led-pulse 2.5s ease-in-out infinite;
}
.manifold-renderer-svc-placard-led[data-state="warn"] {
  animation: manifold-led-pulse-warn 1.2s ease-in-out infinite;
}
.manifold-renderer-svc-placard-led[data-state="fault"] {
  box-shadow: 0 0 6px var(--m-renderer-color);
}
.manifold-renderer-svc-placard-icon-box {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: none;
  transition: filter 180ms;
}
.manifold-renderer-svc-placard-icon-box[data-fault="true"] {
  filter: grayscale(1) opacity(0.35);
}
.manifold-renderer-svc-placard-icon-img {
  display: block;
  max-width: 100%;
  max-height: 100%;
}
.manifold-renderer-svc-placard-text-stack {
  flex: 1 1 0%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.manifold-renderer-svc-placard-name {
  font-family: var(--m-font-sans);
  font-weight: var(--mt-weight-semibold);
  font-size: calc(15px * var(--m-renderer-text-scale, 1));
  letter-spacing: -0.01em;
  color: var(--mt-fg);
  line-height: 1.15;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.manifold-renderer-svc-placard-desc {
  font-family: var(--m-font-mono);
  font-size: calc(11px * var(--m-renderer-text-scale, 1));
  color: var(--mt-dim);
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.manifold-renderer-svc-placard-meta-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
}
.manifold-renderer-svc-placard-group-chip {
  font-family: var(--m-font-mono);
  font-size: calc(9px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--m-brand);
  background: color-mix(in srgb, var(--m-brand) 12%, transparent);
  padding: 1px 6px;
  border-radius: 3px;
  border: 1px solid color-mix(in srgb, var(--m-brand) 33%, transparent);
}
.manifold-renderer-svc-placard-critical {
  font-family: var(--m-font-mono);
  font-size: calc(9px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.06em;
  color: var(--mt-warn);
  opacity: 0.7;
  transition: opacity 180ms;
}
.manifold-renderer-svc-placard-critical[data-hover="true"] { opacity: 1; }
.manifold-renderer-svc-placard-launch {
  flex: 0 0 auto;
  width: 30px; height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--mt-dim);
  font-size: calc(16px * var(--m-renderer-text-scale, 1));
  line-height: 1;
  text-decoration: none;
  transition: color 200ms, transform 200ms;
  transform: translate(0, 0);
}
.manifold-renderer-svc-placard-launch[data-hover="true"] {
  color: var(--m-brand);
  transform: translate(2px, -2px);
}
.manifold-renderer-svc-placard-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 4px;
  opacity: 0.78;
  transition: opacity 180ms;
}
.manifold-renderer-svc-placard-actions[data-hover="true"] { opacity: 1; }

/* ── Phase 84: Services Tab — status pill ──────────────────────────────────
 * Shared pill atom used by both Services-tab rows (Phase 84) and
 * internal-daemon rows (Phase 86). All colours reference existing --m-*
 * / --mt-* tokens — no net-new token added.  Semi-transparent fills are
 * computed via color-mix() at render time, not stored as new tokens.
 *
 * Five modifier classes, one per real API state from /admin/services/status:
 *   running   —  green fill + dot halo  (--m-success)
 *   degraded  —  amber fill + dot halo  (--m-warning)
 *   failed    —  red fill + dot halo    (--m-error)
 *   stopped   —  muted surface fill, grey dot  (--mt-bg-elev / --m-text-faint)
 *   unknown   —  transparent + dashed border, hollow dot  (--mt-border)
 *
 * Security: the cls value that feeds the --<state> suffix comes from the
 * SVC_PILL_STATES lookup table (JSX), never raw-interpolated from the API
 * payload — so an injected state string can only resolve to one of these
 * five known modifier classes or fall back to --unknown. */
.manifold-svc-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: var(--m-font-weight-semibold);
  line-height: 1;
  padding: 4px 9px 4px 7px;
  border-radius: var(--m-radius-full);
  border: 1px solid transparent;
  white-space: nowrap;
  letter-spacing: .01em;
  font-family: var(--m-font-sans);
}
.manifold-svc-pill__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
}

/* running — green-tinted fill, green dot with soft halo */
.manifold-svc-pill--running {
  color: #a7d39c;
  background: color-mix(in srgb, var(--m-success) 14%, transparent);
  border-color: color-mix(in srgb, var(--m-success) 30%, transparent);
}
.manifold-svc-pill--running .manifold-svc-pill__dot {
  background: var(--m-success);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--m-success) 18%, transparent);
}

/* degraded — amber-tinted fill, amber dot with halo */
.manifold-svc-pill--degraded {
  color: #e3b483;
  background: color-mix(in srgb, var(--m-warning) 14%, transparent);
  border-color: color-mix(in srgb, var(--m-warning) 32%, transparent);
}
.manifold-svc-pill--degraded .manifold-svc-pill__dot {
  background: var(--m-warning);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--m-warning) 16%, transparent);
}

/* failed — red-tinted fill (slightly stronger), red dot with halo */
.manifold-svc-pill--failed {
  color: #d98e87;
  background: color-mix(in srgb, var(--m-error) 16%, transparent);
  border-color: color-mix(in srgb, var(--m-error) 34%, transparent);
}
.manifold-svc-pill--failed .manifold-svc-pill__dot {
  background: var(--m-error);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--m-error) 18%, transparent);
}

/* stopped — muted surface fill, grey dot, no halo */
.manifold-svc-pill--stopped {
  color: var(--m-text-faint);
  background: var(--mt-bg-elev);
  border-color: var(--mt-border-strong);
}
.manifold-svc-pill--stopped .manifold-svc-pill__dot {
  background: var(--m-text-faint);
}

/* unknown — transparent bg, dashed border, hollow dot */
.manifold-svc-pill--unknown {
  color: var(--m-text-faint);
  background: transparent;
  border-color: var(--mt-border);
  border-style: dashed;
}
.manifold-svc-pill--unknown .manifold-svc-pill__dot {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--m-text-faint);
}
/* Phase 90: stale state — dimmed + slow pulse layered on top of running colors.
   The pill holds the last-known state word (label='Running'); dim + clock mark
   conveys aging without crying wolf (D-04 / SVCC-04).  Animation stops the
   instant the daemon resolves to a definitive state. */
.manifold-svc-pill--stale {
  opacity: 0.55;
  animation: manifold-led-pulse-stale 2.5s ease-in-out infinite;
}
.manifold-svc-pill--stale .manifold-svc-pill__dot {
  background: var(--m-success);   /* holds running color */
  box-shadow: none;                /* no halo — dim signals aging */
}
@keyframes manifold-led-pulse-stale {
  0%, 100% { opacity: 0.55; }
  50%       { opacity: 0.30; }
}
/* Clock mark placed next to last-seen text so stale rows are scannable */
.manifold-svc-daemon-row__stale-mark {
  color: var(--m-text-faint);
  font-size: 10px;
  margin-left: 4px;
}
/* ── end Phase 84: status pill ─────────────────────────────────────────── */

/* ── Phase 84: Services Tab — row + header ─────────────────────────────────
 * ServiceStatusRow (.manifold-svc-row*) and HealthSummaryHeader
 * (.manifold-svc-header*) CSS.  All colours from --m-* / --mt-* tokens —
 * no net-new token added, no <link> tag added (appended to the already-
 * loaded theme.css to avoid the index.html/dist-index.html.tmpl parity trap).
 *
 * Uniform row height (SVCS-01 "every card the same height in a real grid")
 * is enforced by a fixed min-height on .manifold-svc-row.  When the row is
 * used inside a CSS grid the grid's auto-rows can override this; the fixed
 * value is the floor so empty/minimal rows never collapse shorter. */

/* ── Row outer wrapper ── */
.manifold-svc-row {
  display: flex;
  align-items: center;
  gap: var(--m-pad-sm);           /* 8px between columns */
  min-height: 48px;               /* uniform height floor — SVCS-01 */
  padding: 6px var(--m-pad-sm);   /* 6px top/bottom, 8px sides */
  border-radius: var(--m-radius-md);
  transition: background 140ms;
}
.manifold-svc-row:hover {
  background: color-mix(in srgb, var(--m-brand) 6%, transparent);
}

/* ── Brand chip (32px icon box, tinted by --m-brand) ── */
.manifold-svc-row__icon {
  flex: none;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--m-radius-md);
  background: color-mix(in srgb, var(--m-brand, var(--mt-accent)) 14%, transparent);
  overflow: hidden;
}
.manifold-svc-row__icon-img {
  width: 32px;
  height: 32px;
  object-fit: contain;
  display: block;
}

/* ── Text stack: name + description subtitle ── */
.manifold-svc-row__text {
  flex: 1 1 0;
  min-width: 0;                   /* allow text to shrink+ellipsis */
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.manifold-svc-row__name {
  font-size: 13px;
  font-weight: var(--m-font-weight-semibold);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.manifold-svc-row__sub {
  font-size: 10.5px;
  color: var(--m-text-faint);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Mono meta line: "up — · v— · Xm ago" (right-aligned, faint) ── */
.manifold-svc-row__meta {
  flex: none;
  font-family: var(--m-font-mono);
  font-size: 10.5px;
  color: var(--m-text-faint);
  white-space: nowrap;
  margin-left: auto;              /* push toward the right before the pill */
}

/* ── Actions placeholder: Phase 85 injects Open↗ + kebab here ── */
.manifold-svc-row__actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;                   /* collapses to zero when empty */
}

/* ── Health-summary header ── */
.manifold-svc-header {
  display: flex;
  align-items: center;
  gap: var(--m-pad-md);           /* 14px between count / legend / bar */
  padding: var(--m-pad-sm) 0 var(--m-pad-sm);
  flex-wrap: wrap;
}

/* Headline: "18 services" — the large leading count */
.manifold-svc-header__count {
  font-size: 15px;
  font-weight: var(--m-font-weight-semibold);
  white-space: nowrap;
}
.manifold-svc-header__count span {
  font-weight: var(--m-font-weight-regular);
  color: var(--m-text-faint);
}

/* Legend: colored swatch dot + "N label" per present state */
.manifold-svc-header__legend {
  display: flex;
  align-items: center;
  gap: var(--m-pad-sm);
  flex-wrap: wrap;
}
.manifold-svc-header__legend span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--m-text-faint);
  white-space: nowrap;
}
.manifold-svc-header__legend span i {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background: var(--svc-dot-color, var(--m-text-faint));
}

/* Proportional health bar: clipped flex row of fixed-height segments */
.manifold-svc-header__bar {
  flex: 1 1 120px;                /* grows to fill remaining header space */
  min-width: 80px;
  height: 4px;
  display: flex;
  overflow: hidden;
  border-radius: var(--m-radius-sm);
  background: var(--mt-bg-elev);  /* track color behind segments */
}
.manifold-svc-header__bar i {
  display: block;
  height: 4px;
  flex: none;                     /* width set inline as a percentage */
  background: var(--svc-dot-color, var(--m-text-faint));
}
/* ── end Phase 84: row + header ────────────────────────────────────────── */

/* ── Phase 84 Plan 04: Services Tab — category groups ─────────────────────
 * CategoryGroup (.manifold-svc-group*) and the ServicesTabRenderer wrapper.
 * All colours from --m-* / --mt-* tokens — no hardcoded hex.
 * No <link> tag added — CSS appended to already-loaded theme.css
 * (avoids dist-index.html.tmpl parity trap, feedback_manifold_dist_template_parity).
 * ────────────────────────────────────────────────────────────────────────── */

/* Tab-level wrapper — owns the vertical rhythm between header and groups.   */
.manifold-svc-tab {
  display: flex;
  flex-direction: column;
  gap: var(--mt-space-lg);
  padding: var(--mt-space-md) var(--mt-space-lg);
  overflow-y: auto;
  height: 100%;
  box-sizing: border-box;
}

/* Category group container — top margin handled by parent gap above.        */
.manifold-svc-group {
  display: flex;
  flex-direction: column;
}

/* Clickable group header row.                                                */
.manifold-svc-group__head {
  display: flex;
  align-items: center;
  gap: var(--mt-space-sm);
  padding: 0 2px var(--mt-space-sm);
  cursor: pointer;
  user-select: none;
  outline: none;                     /* focus ring via :focus-visible below   */
}
.manifold-svc-group__head:focus-visible {
  outline: 2px solid var(--mt-accent);
  border-radius: var(--m-radius-sm);
}

/* Chevron rotates 90° when the group is collapsed.                          */
.manifold-svc-group__chev {
  color: var(--m-text-faint);
  font-size: 9px;
  line-height: 1;
  transition: transform var(--m-t-fast, 120ms ease);
  display: inline-block;
}
.manifold-svc-group__chev--collapsed {
  transform: rotate(-90deg);
}

/* Category display name — uppercase, display font, muted.                   */
.manifold-svc-group__name {
  font-family: var(--m-font-display);
  font-weight: var(--mt-weight-semibold);
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--m-text-muted);
  white-space: nowrap;
}

/* Service count in monospace, faint.                                         */
.manifold-svc-group__count {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--m-text-faint);
  white-space: nowrap;
}

/* Flex-grow spacer rule between count and dots.                              */
.manifold-svc-group__line {
  flex: 1 1 0;
  height: 1px;
  background: var(--m-border);
  align-self: center;
}

/* Worst-first status dot run. Each dot is a 6px circle; color set inline.   */
.manifold-svc-group__dots {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: none;
}
.manifold-svc-group__dots i {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: none;
  background: var(--svc-dot-color, var(--m-text-faint));
}

/* Rows container — no extra padding; ServiceStatusRow provides its own.     */
.manifold-svc-group__rows {
  display: flex;
  flex-direction: column;
}
/* ── end Phase 84 Plan 04: category groups ──────────────────────────────── */

/* ── Phase 85: intent actions + kebab ─────────────────────────────────────
 *
 * 1. .manifold-button.warning — amber confirm-button variant.
 *    Mirrors .manifold-button.danger (line ~1407) with var(--m-warning)
 *    instead of var(--m-error).  Selected when tone:'warning' is passed to
 *    window.ManifoldConfirm.confirm() (D-03 / UI-SPEC §96: amber, never red,
 *    for disruptive-but-reversible service verbs).
 *
 * 2. .manifold-svc-row__open — "Open ↗" primary action anchor.
 *
 * 3. .manifold-svc-row__kebab-btn — ⋮ button that opens the dropdown.
 *
 * 4. .manifold-svc-kebab-menu + __item + __item--disabled — dropdown panel
 *    and its items.  z-index: 200 matches the existing modal z-index
 *    convention.  No hardcoded hex — all values use --m-* / --mt-* tokens.
 *
 * No <link>/<script> tag added — theme.css is already loaded (dist-template
 * parity rule: feedback_manifold_dist_template_parity).
 * ─────────────────────────────────────────────────────────────────────────*/

/* Amber confirm-button variant ─────────────────────────────────────────── */
.manifold-button.warning {
  background:    var(--m-warning);
  border-color:  var(--m-warning);
  color:         var(--m-bg);
}
.manifold-button.warning:hover:not(:disabled) {
  background:   color-mix(in srgb, var(--m-warning) 85%, var(--m-bg) 15%);
  border-color: color-mix(in srgb, var(--m-warning) 85%, var(--m-bg) 15%);
  color:        var(--m-bg);
}

/* "Open ↗" primary action link ─────────────────────────────────────────── */
.manifold-svc-row__open {
  font-family:     var(--m-font-sans);
  font-size:       12px;
  font-weight: var(--mt-weight-regular);
  line-height:     1;
  color:           var(--m-text-muted);
  text-decoration: none;
  padding:         4px 6px;
  border-radius:   var(--m-radius-sm);
  white-space:     nowrap;
  flex-shrink:     0;
}
.manifold-svc-row__open:hover {
  color:           var(--mt-fg);
  text-decoration: underline;
  text-decoration-color: var(--mt-accent);
}

/* Kebab ⋮ button ──────────────────────────────────────────────────────── */
.manifold-svc-row__kebab-btn {
  display:         flex;
  align-items:     center;
  justify-content: center;
  width:           28px;
  height:          28px;
  font-size:       14px;
  background:      transparent;
  border:          none;
  border-radius:   var(--m-radius-md);
  color:           var(--m-text-faint);
  cursor:          pointer;
  flex-shrink:     0;
  padding:         0;
  line-height:     1;
  transition:      background 120ms ease, color 120ms ease;
}
.manifold-svc-row__kebab-btn:hover,
.manifold-svc-row__kebab-btn[aria-expanded="true"] {
  background: var(--mt-bg-elev);
  color:      var(--mt-fg);
}

/* Kebab dropdown panel + items ─────────────────────────────────────────── */
.manifold-svc-kebab-wrap {
  position: relative;
  flex-shrink: 0;
}
.manifold-svc-kebab-menu {
  position:      absolute;
  right:         0;
  top:           calc(100% + 4px);
  z-index:       200;
  min-width:     160px;
  width:         max-content;
  background:    var(--mt-bg-elev);
  border:        1px solid var(--m-border-hi);
  border-radius: var(--m-radius-lg);
  box-shadow:    0 4px 16px color-mix(in srgb, var(--m-bg) 60%, transparent);
  overflow:      hidden;
  display:       flex;
  flex-direction: column;
}
.manifold-svc-kebab-menu__item {
  display:         block;
  width:           100%;
  padding:         8px 14px;
  font-family:     var(--m-font-sans);
  font-size:       13px;
  font-weight: var(--mt-weight-regular);
  line-height:     1.2;
  color:           var(--mt-fg);
  background:      transparent;
  border:          none;
  text-align:      left;
  cursor:          pointer;
  transition:      background 80ms ease;
}
.manifold-svc-kebab-menu__item:hover:not(.manifold-svc-kebab-menu__item--disabled) {
  background: color-mix(in srgb, var(--mt-accent) 10%, transparent);
}
.manifold-svc-kebab-menu__item--disabled {
  opacity:        0.4;
  cursor:         not-allowed;
}
/* ── end Phase 85: intent actions + kebab ─────────────────────────────── */

/* ── Phase 85 Plan 02: view toggle + table ─────────────────────────────────
 * Appended after Plan 01 classes per append-only rule.  All values use
 * CSS var tokens — no hardcoded hex.  No <link> / <script> tag added
 * (dist-template parity: theme.css is already loaded).
 *
 * Spacing grid exceptions (documented in 85-UI-SPEC.md §Spacing):
 *   gap: 6px  — .manifold-svc-view-toggle: matches existing ServiceActionsRenderer gap
 *   padding: 14px — table cell horizontal: inherits from --m-pad-md token
 * ─────────────────────────────────────────────────────────────────────────── */

/* ── View toggle: segmented Cards/Table control ── */
.manifold-svc-view-toggle {
  display:       flex;
  gap:           6px;
  border:        1px solid var(--m-border);
  border-radius: var(--m-radius-md);
  padding:       2px;
  margin-left:   auto;
  flex-shrink:   0;
}
.manifold-svc-view-toggle__btn {
  font-family:   var(--m-font-sans);
  font-size:     11px;
  line-height:   1;
  padding:       4px 10px;
  border:        none;
  border-radius: var(--m-radius-sm);
  background:    transparent;
  color:         var(--m-text-faint);
  cursor:        pointer;
  transition:    background 0.1s, color 0.1s;
}
.manifold-svc-view-toggle__btn--active {
  background: var(--mt-bg-elev);
  color:      var(--mt-fg);
}
.manifold-svc-view-toggle__btn:hover:not(.manifold-svc-view-toggle__btn--active) {
  color: var(--m-text-muted);
}

/* ── Table view ── */
.manifold-svc-table {
  width:           100%;
  border-collapse: collapse;
  font-family:     var(--m-font-sans);
}
.manifold-svc-table__head {
  /* thead wrapper — no extra styles needed; th carries background */
}
.manifold-svc-table__th {
  font-family:    var(--m-font-display);
  font-weight: var(--mt-weight-semibold);
  font-size:      11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color:          var(--m-text-muted);
  background:     var(--mt-bg-elev);
  padding:        8px var(--m-pad-md);
  border-bottom:  1px solid var(--m-border);
  text-align:     left;
  white-space:    nowrap;
}
.manifold-svc-table__th--sortable {
  cursor: pointer;
  user-select: none;
}
.manifold-svc-table__th--sortable:hover {
  color: var(--mt-fg);
}
.manifold-svc-table__th--sorted-asc,
.manifold-svc-table__th--sorted-desc {
  color: var(--mt-fg);
}
.manifold-svc-table__td {
  padding:       12px var(--m-pad-md);
  border-bottom: 1px solid color-mix(in srgb, var(--m-border) 20%, transparent);
  vertical-align: middle;
}
.manifold-svc-table__row:hover .manifold-svc-table__td {
  background: color-mix(in srgb, var(--m-brand) 6%, transparent);
}
/* Sort-direction arrow appended to the active column header. */
.manifold-svc-table__sort-ind {
  font-size:   10px;
  margin-left: 4px;
}
/* Host / Uptime / Version cells — faint 11px mono ("—" when no publisher). */
.manifold-svc-table__mono {
  font-family: var(--m-font-mono);
  font-size:   11px;
  color:       var(--m-text-faint);
}
/* ── end Phase 85 Plan 02: view toggle + table ──────────────────────────── */

/* ── Phase 85 Plan 03: empty + error states ─────────────────────────────────
 *
 * ServiceEmptyState: shown when catalog loads but zero non-manifold services exist.
 * ServiceErrorState: shown when catalog.error is truthy AND !== 'auth'.
 *
 * Both states center vertically and horizontally inside .manifold-svc-tab.
 * No hardcoded hex — tokens only.  No <link>/<script> edit needed (theme.css
 * is already loaded).  dist-index.html.tmpl parity maintained.
 */

/* Shared base: centered flex column filling the tab area */
.manifold-svc-empty,
.manifold-svc-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--m-pad-sm);
  padding: var(--m-pad-xl) var(--m-pad-lg);
  text-align: center;
}

/* ── Empty state ── */
.manifold-svc-empty__icon {
  font-size: 32px;
  line-height: 1;
  color: var(--m-text-faint);
  margin-bottom: var(--m-pad-xs);
}
.manifold-svc-empty__heading {
  font-family: var(--m-font-sans);
  font-size: 15px;
  font-weight: var(--mt-weight-semibold);
  line-height: 1.3;
  color: var(--mt-fg);
  margin: 0;
}
.manifold-svc-empty__body {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: var(--mt-weight-regular);
  line-height: 1.5;
  color: var(--m-text-muted);
  max-width: 280px;
  margin: 0;
}

/* ── Error state ── */
.manifold-svc-error__icon {
  font-size: 28px;
  line-height: 1;
  color: var(--m-error);
  margin-bottom: var(--m-pad-xs);
}
.manifold-svc-error__heading {
  font-family: var(--m-font-sans);
  font-size: 15px;
  font-weight: var(--mt-weight-semibold);
  line-height: 1.3;
  color: var(--mt-fg);
  margin: 0;
}
.manifold-svc-error__body {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: var(--mt-weight-regular);
  line-height: 1.5;
  color: var(--m-text-muted);
  max-width: 300px;
  margin: 0;
}
.manifold-svc-error__retry {
  margin-top: var(--m-pad-xs);
  font-family: var(--m-font-sans);
  font-size: 12px;
  font-weight: var(--mt-weight-regular);
  padding: 4px 14px;
  border: 1px solid color-mix(in srgb, var(--m-error) 50%, transparent);
  border-radius: var(--m-radius-md);
  color: var(--m-error);
  background: transparent;
  cursor: pointer;
}
.manifold-svc-error__retry:hover {
  background: color-mix(in srgb, var(--m-error) 10%, transparent);
}

/* ── end Phase 85 Plan 03: empty + error states ─────────────────────────── */

/* ServiceElementRenderer leaf primitives — one class per p.type branch
 * (state LED, color swatch, text, time, flag star). The
 * .manifold-renderer-svc-element-dot variant covers both the state LED
 * (border in --mt-bg) and the color swatch (border in --mt-border) via
 * a data-style attr selector — different border owners, same square
 * /round + state-color shape.
 *
 * Size flows through --m-renderer-size (set inline; runtime from
 * style.size with a per-type default).  Color flows through
 * --m-renderer-color (state branch: from _serviceStateColor; color
 * branch: from p.value; flag branch: from filled-ternary). */
.manifold-renderer-svc-element-dot {
  width: var(--m-renderer-size);
  height: var(--m-renderer-size);
  background: var(--m-renderer-color);
}
.manifold-renderer-svc-element-dot[data-shape="led"] {
  border-radius: 50%;
  border: 1.5px solid var(--mt-bg);
}
.manifold-renderer-svc-element-dot[data-shape="swatch"] {
  border-radius: 3px;
  border: 1px solid var(--mt-border);
}
/* Text element — single class composes with .manifold-renderer-font-mono
 * or .manifold-renderer-font-sans (depending on isName) for the family
 * fallback chain; data-is-name="true" flips weight 400→600 and color
 * muted→fg (preserves the pre-migration name-vs-other visual hierarchy).
 * data-no-wrap="true" composes with .manifold-renderer-truncate (already
 * shipped 19-05e) — kept as a separate class on the JSX since the
 * triple `overflow/text-overflow/white-space` lives in that class.
 * Font size flows through --m-renderer-value-size (set inline). */
.manifold-renderer-svc-element-text {
  font-size: var(--m-renderer-value-size);
  font-weight: var(--mt-weight-regular);
  color: var(--m-renderer-color, var(--mt-muted));
  line-height: 1.2;
  max-width: 100%;
}
.manifold-renderer-svc-element-text[data-is-name="true"] {
  font-weight: var(--mt-weight-semibold);
  color: var(--m-renderer-color, var(--mt-fg));
}
/* Flag star element — fontSize from --m-renderer-value-size; color
 * (filled=warn / unfilled=dim, or style.color override) from
 * --m-renderer-color. */
.manifold-renderer-svc-element-flag {
  font-size: var(--m-renderer-value-size);
  line-height: 1;
  color: var(--m-renderer-color);
  font-family: var(--m-font-mono);
  user-select: none;
}
/* Bare img inside the icon branch — display:block via a tiny class
 * so the inline style can drop. Not theme-skinnable; just keeping the
 * file's `<img style={...}>` surface clean. */
.manifold-renderer-svc-element-img { display: block; }

/* BboxOverlayRenderer — Frigate detection-box overlay painted on top of
 * a video widget.  Four classes:
 *
 *   .manifold-renderer-bbox-overlay   — outer absolute layer; own
 *     compositor (translateZ + will-change + contain:paint) so per-event
 *     mutations don't invalidate the underlying video layer.
 *   .manifold-renderer-bbox-box       — per-detection bordered
 *     rectangle.  Border + soft halo flow through --m-renderer-color +
 *     color-mix tints (33% halo) — the box's left/top/width/height
 *     positioning stays inline (audit ALLOWED_KEYS already permits
 *     those geometry properties).
 *   .manifold-renderer-bbox-label     — anchored label badge above each
 *     box.  Background = --m-renderer-color directly; foreground
 *     contrasts on var(--mt-bg).
 *   .manifold-renderer-bbox-indicator — corner "BBOX · N" chip showing
 *     the overlay is active even with zero current detections.  Same
 *     --m-renderer-color flow as the label.
 *
 * Per-box --m-renderer-color is set ON EACH BOX inline (not on the
 * overlay container).  Today every box receives the same stroke color
 * (style.color || 'warn' resolved through tokens), but per-detection-
 * class differentiation (person=red, car=blue, ...) can be added later
 * by changing the JSX value alone — no CSS restructure needed.  Per
 * spawn-instruction Rule 1: per-class color preservation is built-in
 * via --m-renderer-color as the per-box escape hatch. */
.manifold-renderer-bbox-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  transform: translateZ(0);
  will-change: transform;
  contain: paint;
}
.manifold-renderer-bbox-box {
  position: absolute;
  border: 2px solid var(--m-renderer-color);
  border-radius: 2px;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--mt-bg) 53%, transparent),
    0 0 8px color-mix(in srgb, var(--m-renderer-color) 33%, transparent);
  box-sizing: border-box;
}
.manifold-renderer-bbox-label {
  position: absolute;
  /* #107: the offset scales WITH the text, or the label grows down into the
   * box it labels. The -18px was sized for 9px text (a 14.6px tall chip with
   * 3.4px of clearance); at the `lg` notch the chip is 27.2px and overlapped
   * the box's top edge by 9.2px, at `xl` by 17.6px — measured, not reasoned.
   * Multiplying by the same scale keeps the clearance proportional, and at the
   * default notch this is exactly -18px again. */
  top: calc(-18px * var(--m-renderer-text-scale, 1));
  left: -2px;
  padding: 1px 5px;
  background: var(--m-renderer-color);
  color: var(--mt-bg);
  font-family: var(--m-font-mono);
  font-size: calc(9px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: 2px;
  white-space: nowrap;
}
.manifold-renderer-bbox-label-score {
  margin-left: 4px;
  opacity: 0.85;
}
.manifold-renderer-bbox-indicator {
  position: absolute;
  bottom: 4px;
  right: 4px;
  padding: 1px 5px;
  background: color-mix(in srgb, var(--m-renderer-color) 13%, transparent);
  color: var(--m-renderer-color);
  font-family: var(--m-font-mono);
  font-size: calc(9px * var(--m-renderer-text-scale, 1));
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: 2px;
  border: 1px solid color-mix(in srgb, var(--m-renderer-color) 33%, transparent);
  pointer-events: none;
}

/* ServiceActionsRenderer outer wrap — the inline-style block consumed
 * by the renderer's `<div style={wrap}>` site carries `padding: '4px 6px'`
 * (a FORBIDDEN_KEY) plus structural flex/width/height set by applyAlign.
 * This class owns the padding-only aesthetic decision; the applyAlign-
 * produced flex/justify/align positioning stays inline (those are
 * ALLOWED_KEYS — width/height/display/flexDirection/etc. are runtime-
 * computed positioning, not theme aesthetic). */
.manifold-renderer-svc-actions-wrap {
  padding: 4px 6px;
}

/* =========================================================================
 * Phase 19-05g — gauges + decorative + closing family escape-hatch classes.
 *
 * Covers RankedBars / Svg / Heatmap / Gauge / MultiGauge / EasyPie / Donut /
 * StackedBarsTime / Distribution / SparklineAnomaly renderers.
 *
 * Two recurring patterns from 19-05f apply here verbatim:
 *
 *   1. Per-instance --m-renderer-color escape hatch for state-driven and
 *      data-driven fills. Heatmap cells, gauge needles, donut slices,
 *      easyPie ring, distribution bars, rankedBars bars — all of them
 *      drive their primary visible color from runtime data (per-cell value,
 *      per-row threshold, per-slice category, per-band anomaly score).
 *      Setting --m-renderer-color INLINE per-element means every chart
 *      keeps per-data-point differentiation; the class only owns the
 *      consumption (background:var(...) or stroke:var(...)).
 *
 *   2. data-* attr selectors for boolean variants. RankedBars top-rank
 *      vs rest, MultiGauge ghost vs received, SparklineAnomaly P99 line
 *      vs muted — each splits one base class into two states with a single
 *      attribute toggle, avoiding class proliferation.
 * ========================================================================= */

/* ── RankedBarsRenderer ──────────────────────────────────────────────
 *
 * Empty-state placeholder. Italic mono small text inside the bars stack.
 * Pre-migration:
 *   { fontFamily:'"JetBrains Mono",monospace', fontSize:11,
 *     color:'var(--mt-muted)', fontStyle:'italic', padding:'6px 0' }
 * fontStyle:'italic' is not a FORBIDDEN_KEY, so it stays inline; the rest
 * collapses into one class composing .manifold-renderer-font-mono with
 * fontSize + color + padding baked in. */
.manifold-renderer-rankedbars-empty {
  font-size: 11px;
  color: var(--mt-muted);
  padding: 6px 0;
}

/* RankedBars label row. Pre-migration:
 *   { display:'flex', alignItems:'baseline', justifyContent:'space-between',
 *     fontFamily:'"JetBrains Mono",monospace', fontSize:11 }
 * display/alignItems/justifyContent are structural (already inline). Class
 * owns the font-size only; mono composes via .manifold-renderer-font-mono. */
.manifold-renderer-rankedbars-label-row {
  font-size: 11px;
}

/* RankedBars label-text span. Per-row label color flows through
 * --m-renderer-color. Pre-migration:
 *   { color: labelColor, overflow:'hidden', textOverflow:'ellipsis',
 *     whiteSpace:'nowrap' }
 * The truncate trio is .manifold-renderer-truncate (Phase 19-05e). Color
 * routes through the standard per-instance prop. */
.manifold-renderer-rankedbars-label-text {
  color: var(--m-renderer-color, var(--mt-fg));
  /* Honors the named size menus, via a NESTED FALLBACK (Phase 227, D-06):
   *   1. --m-renderer-barlabel-label-size — written by rankedBars ONLY, from
   *      style.labelSize, as the token's own pixel value (`lg` = 18px).
   *   2. --m-renderer-barlabel-size — written by barChart (from its 14px base)
   *      and, on rankedBars, by style.textSize.
   *   3. inherit — the renderer's own default.
   * The nesting is what keeps barChart unchanged: it never writes the label
   * variable, so on a barChart span step 1 is absent and this resolves to
   * exactly what it resolved to before. On rankedBars it is what finally lets
   * the row labels follow labelSize — the setting Karl chose that did nothing.
   * Shared by rankedBars (inside .manifold-renderer-rankedbars-label-row →
   * inherits 11px) AND barChart (plain wrapper → inherits 14px). */
  font-size: var(--m-renderer-barlabel-label-size, var(--m-renderer-barlabel-size, inherit));
}

/* "(no data yet)" suffix span on unreceived rows. Pre-migration:
 *   { color:'var(--mt-dim)', marginLeft:4, fontSize:10 } */
.manifold-renderer-rankedbars-no-data {
  color: var(--mt-dim);
  margin-left: 4px;
  font-size: 10px;
}

/* RankedBars primary value (right-aligned number). Pre-migration:
 *   { color:'var(--mt-fg)', fontWeight:600, fontVariantNumeric:'tabular-nums' }
 * fontVariantNumeric not FORBIDDEN — stays inline. Color + weight via class. */
/* #208: the number asks the theme's Row value part, keeping --mt-fg as the
 * stock so an untouched dashboard does not move. It is a CSS rule rather than
 * a partRef() call because this value is painted from the stylesheet — the
 * label two rules up goes through partRef in JSX. Two edit shapes, one
 * contract; the walk in #209 measures both the same way. */
.manifold-renderer-rankedbars-val {
  color: var(--mt-part-rowvalue, var(--mt-fg));
  font-weight: var(--mt-weight-semibold);
  /* Same text-size routing as -label-text above. `inherit` fallback keeps
   * rankedBars (11px) and barChart (14px) pixel-identical when unset. */
  font-size: var(--m-renderer-barlabel-size, inherit);
}

/* RankedBars unit suffix span (right of primary value). Pre-migration:
 *   { color:'var(--mt-muted)', fontSize:10, fontWeight:400, marginLeft:3 } */
/* #208: the suffix asks the theme's Unit / suffix part. The STOCK moves too,
 * `muted` -> `dim`, which is the one place this change does not keep what the
 * site painted before. Karl's acceptance test from the walk that produced the
 * ticket: on Downstairs Power the `W` should be the same colour as `MAINS`.
 * The parts registry already agrees — `rowlabel` and `unit` are both `dim` —
 * so this is #185's rule again: when a site and the registry disagree about a
 * part's colour, the site moves, because the registry's name is the one a
 * person reads in the wizard. Leaving `muted` here would have made the part
 * reachable while the untouched default still failed the eye test. */
.manifold-renderer-rankedbars-unit {
  color: var(--mt-part-unit, var(--mt-dim));
  font-size: 10px;
  font-weight: var(--mt-weight-regular);
  margin-left: 3px;
}

/* RankedBars bar track. Pre-migration:
 *   { height: barHeight, background:'var(--mt-bg-deep)',
 *     borderRadius: Math.round(barHeight/2), overflow:'hidden' }
 * height + borderRadius are runtime (barHeight is style-configurable). Route
 * borderRadius through --m-rankedbars-radius so the audit-FORBIDDEN
 * borderRadius key moves off. height stays inline (NOT in FORBIDDEN_KEYS). */
.manifold-renderer-rankedbars-track {
  background: var(--mt-bg-deep);
  border-radius: var(--m-rankedbars-radius, 3px);
  overflow: hidden;
}

/* RankedBars bar fill. Pre-migration:
 *   { width: `${pct}%`, height:'100%',
 *     background: rank === 0 ? x.color : `${x.color}80`,
 *     borderRadius: Math.round(barHeight/2) }
 * width + height are runtime layout (stay inline). background per-row uses
 * --m-renderer-color SET PER BAR; data-top="true" on the rank-0 bar gets
 * full alpha, others 50% via color-mix. Replaces the `${x.color}80`
 * hex-suffix trick (only worked when x.color was 6-digit hex). */
.manifold-renderer-rankedbars-fill {
  background: color-mix(in srgb, var(--m-renderer-color, var(--mt-accent)) 50%, transparent);
  border-radius: var(--m-rankedbars-radius, 3px);
}
.manifold-renderer-rankedbars-fill[data-top="true"] {
  background: var(--m-renderer-color, var(--mt-accent));
}

/* BarChartMultiRenderer column fill — a solid vertical column in the series
 * color, rounded only at the top. Color arrives via the same --m-renderer-color
 * var the ranked bars use, so a per-series color override or a theme switch
 * restyles the columns for free. Kept as a class (not inline) so the
 * no-inline-aesthetic-styles rule passes and the radius/background live in one place. */
.manifold-renderer-barchart-fill {
  background: var(--m-renderer-color, var(--mt-accent));
  border-radius: 3px 3px 0 0;
}

/* Horizontal layout of the same bar chart (style.horizontal): a row bar that
 * grows left→right, rounded only on the trailing (right) edge. Same color var
 * as the vertical fill so theme switch + per-series override apply identically. */
.manifold-renderer-barchart-fill-h {
  background: var(--m-renderer-color, var(--mt-accent));
  border-radius: 0 3px 3px 0;
}

/* ── SvgRenderer ─────────────────────────────────────────────────────
 *
 * Error / no-source fallback span. Pre-migration:
 *   { fontFamily:'"JetBrains Mono",monospace', fontSize:11,
 *     color:'var(--mt-muted)', fontStyle:'italic', textAlign:'center' }
 * fontStyle stays inline (not FORBIDDEN). Class owns fontSize + color +
 * textAlign. Mono composes via .manifold-renderer-font-mono. */
.manifold-renderer-svg-fallback {
  font-size: 11px;
  color: var(--mt-muted);
  text-align: center;
}

/* ── HeatmapRenderer ─────────────────────────────────────────────────
 *
 * Heatmap data drives PER-CELL color (the whole point of a heatmap). Per
 * Karl spawn-instruction: do NOT collapse cells into one color — that
 * turns a heatmap into a flat grid. Each cell sets --m-renderer-color
 * INLINE per <div>, the class consumes it. Today every cell uses
 * heatColor(val) which lerps through stops; the wiring per-cell is what
 * preserves the differentiation through theme switches.
 *
 * Grid wrapper. Pre-migration:
 *   { flex:1, display:'flex', flexDirection:'column', gap:1,
 *     fontFamily:'"JetBrains Mono",monospace', fontSize:10,
 *     minHeight:0, overflow:'hidden' }
 * Flex layout stays inline. Class owns font-size + composes font-mono. */
.manifold-renderer-heatmap-grid {
  /* POLISH-02: textSize-aware. JSX sets --m-renderer-heatmap-grid-size
   * only when style.textSize is chosen; unset leaves the 10px default. */
  font-size: var(--m-renderer-heatmap-grid-size, 10px);
}

/* Heatmap label column (left side, mono right-aligned). Pre-migration:
 *   { width: labelWidth, color:'var(--mt-dim)', textAlign:'right',
 *     flexShrink:0, overflow:'hidden', textOverflow:'ellipsis',
 *     whiteSpace:'nowrap' }
 * width stays inline (runtime labelWidth). Class owns color + textAlign.
 * Truncate trio composes via .manifold-renderer-truncate. */
/* #210: the row label down the left of a heatmap asks the theme's Row label
 * part. --mt-dim stays as the fallback, so an untouched card is unchanged. */
.manifold-renderer-heatmap-label {
  color: var(--mt-part-rowlabel, var(--mt-dim));
  text-align: right;
}

/* Heatmap unreceived-row " (no data yet)" suffix. Pre-migration:
 *   { marginLeft:4 } */
.manifold-renderer-heatmap-no-data {
  margin-left: 4px;
}

/* Heatmap row body — the stripe of cells. Pre-migration:
 *   { flex:1, display:'flex', height: rowHeight - 2,
 *     background:'var(--mt-bg-deep)', borderRadius:1 }
 * flex + display + height stay inline (runtime rowHeight). Class owns the
 * track background + 1px radius. */
.manifold-renderer-heatmap-row-track {
  background: var(--mt-bg-deep);
  border-radius: 1px;
}

/* Heatmap unreceived-row striped placeholder. Pre-migration:
 *   { flex:1, height: rowHeight - 2,
 *     background:'repeating-linear-gradient(90deg, transparent 0,
 *       transparent 4px, var(--mt-dim) 4px, var(--mt-dim) 5px)',
 *     borderRadius:1 }
 * flex + height stay inline. Class bundles the diagonal-stripe gradient
 * + 1px radius. */
.manifold-renderer-heatmap-row-ghost {
  background: repeating-linear-gradient(
    90deg,
    transparent 0,
    transparent 4px,
    var(--mt-dim) 4px,
    var(--mt-dim) 5px
  );
  border-radius: 1px;
}

/* Heatmap per-cell. Pre-migration:
 *   { flex:1, minWidth:1, background: heatColor(v) }
 * flex/minWidth stay inline. PER-CELL color via --m-renderer-color (set
 * inline by JSX from heatColor(v) for every individual cell — this is the
 * per-cell differentiation Karl flagged as critical: do NOT collapse). */
.manifold-renderer-heatmap-cell {
  background: var(--m-renderer-color, var(--mt-muted));
}

/* Heatmap "missing" tile — Phase 38 Plan 05 Task 1.
 *
 * Why: a heatmap row bound by a stable label (drive serial) whose drive
 * is no longer present in Netdata (pulled, dead, or not yet visible
 * because the resolver hasn't returned) paints this tile instead of the
 * value-coloured cell strip. SPEC line 47 binding: the SAME tile covers
 * "drive truly gone" and "drive not yet visible" — no separate spinner,
 * no loading state. The user reads one shape: "missing".
 *
 * Design choices:
 *   - background: --mt-bg-deep (the same flat backdrop the value track
 *     uses) with --mt-muted at 0.18 alpha layered on top. Reads dimmer
 *     than the value track, so a row of missing cells is visually
 *     distinct from a row of all-low-value cells, but doesn't shout.
 *   - color: --mt-dim. "missing" is informational, not an alarm — the
 *     user already knows something's wrong because the row is grey.
 *   - centered text via flex; tabular-nums keeps "missing" sitting on
 *     the same baseline as the right-edge latest-value column.
 *   - 1px radius matches the surrounding .manifold-renderer-heatmap-row-track
 *     so the missing tile slots in where a value strip would. */
.cb-cell-missing,
.manifold-renderer-heatmap-missing {
  background:
    linear-gradient(var(--mt-muted-overlay, rgba(128, 128, 128, 0.18)),
                    var(--mt-muted-overlay, rgba(128, 128, 128, 0.18))),
    var(--mt-bg-deep);
  color: var(--mt-dim);
  border-radius: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-family: var(--m-font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.05em;
  text-transform: lowercase;
  user-select: none;
}

/* Heatmap right-side latest-value label. Pre-migration:
 *   { ...mfont, fontSize:9, color: latest != null ? var(--mt-fg) : var(--mt-muted),
 *     width:36, textAlign:'right', flexShrink:0,
 *     fontVariantNumeric:'tabular-nums', fontWeight:600 }
 * width/flexShrink stay inline. Class owns font-size + textAlign + weight.
 * Color: --m-renderer-color set inline by JSX (fg for received-with-value,
 * muted/dim otherwise). */
.manifold-renderer-heatmap-latest {
  font-size: 9px;
  color: var(--m-renderer-color, var(--mt-muted));
  text-align: right;
  font-weight: var(--mt-weight-semibold);
}

/* Heatmap legend strip (gradient bar). Pre-migration:
 *   { width:8, background:`linear-gradient(to top, ${gradientStops})`,
 *     border:'1px solid var(--mt-border)', borderRadius:2 }
 * width stays inline. background is a runtime gradient (stops depend on
 * user's heatmapColors style) — route through --m-heatmap-legend-bg as a
 * runtime CSS prop. Class owns border + radius. */
.manifold-renderer-heatmap-legend-bar {
  background: var(--m-heatmap-legend-bg, var(--mt-bg-deep));
  border: 1px solid var(--mt-border);
  border-radius: 2px;
}

/* Heatmap legend tick column (min/mid/max numeric markers). Pre-migration:
 *   { display:'flex', flexDirection:'column', justifyContent:'space-between',
 *     fontFamily:'"JetBrains Mono",monospace', fontSize:9,
 *     color:'var(--mt-muted)', fontVariantNumeric:'tabular-nums' }
 * Flex layout stays inline. Class owns font-size + color; mono composes. */
.manifold-renderer-heatmap-legend-ticks {
  /* POLISH-02: textSize-aware. JSX sets --m-renderer-heatmap-tick-size
   * only when style.textSize is chosen; unset leaves the 9px default. */
  font-size: var(--m-renderer-heatmap-tick-size, 9px);
  color: var(--mt-muted);
}

/* Heatmap hover tooltip. Pre-migration:
 *   { position:'fixed', top, left,
 *     fontFamily:'var(--m-font-mono)', fontSize:10, color:'var(--mt-fg)',
 *     background:`color-mix(in srgb, var(--mt-bg-deep) 93%, transparent)`,
 *     padding:'2px 6px', borderRadius:3,
 *     border:'1px solid var(--mt-border)', pointerEvents:'none',
 *     fontVariantNumeric:'tabular-nums', zIndex:9999, whiteSpace:'nowrap' }
 * position/top/left/zIndex stay inline (runtime cursor pos). Class owns
 * the visual chrome — same shape as .manifold-chart-tooltip but with a
 * font-size override (10 vs the chart tooltip's default). */
.manifold-renderer-heatmap-tooltip {
  font-size: 10px;
  color: var(--mt-fg);
  background: color-mix(in srgb, var(--mt-bg-deep) 93%, transparent);
  padding: 2px 6px;
  border-radius: 3px;
  border: 1px solid var(--mt-border);
  pointer-events: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Heatmap tooltip label span. Pre-migration:
 *   { color:'var(--mt-muted)' } */
.manifold-renderer-heatmap-tooltip-label {
  color: var(--mt-muted);
}

/* Heatmap tooltip value-bold span. Pre-migration:
 *   { fontWeight:700 } */
.manifold-renderer-heatmap-tooltip-val {
  font-weight: var(--mt-weight-bold);
}

/* Heatmap tooltip "t-N" tail span. Pre-migration:
 *   { color:'var(--mt-muted)', marginLeft:6 } */
.manifold-renderer-heatmap-tooltip-tail {
  color: var(--mt-muted);
  margin-left: 6px;
}

/* ── Gauge family (Gauge / MultiGauge / EasyPie / Donut) ─────────────
 *
 * All four renderers draw arcs whose stroke colors are state-driven
 * (threshold rules: live/warn/error/accent). Today the SVG tag receives
 * stroke={valColor} directly, not style={{color:valColor}}, so the audit
 * doesn't see them — but the few inline-style sites that DO carry
 * FORBIDDEN_KEYS are the surrounding wrappers and labels.
 *
 * Gauge outer-wrap padding-only. Pre-migration:
 *   { width:'100%', height:'100%', padding: noPad ? 0 : '10px 12px',
 *     opacity, display:'flex', flexDirection:'column' }
 * width/height/opacity/display/flex stay inline. padding routes through
 * data-nopad attr selector (same shape as .manifold-renderer-empty-card-body
 * from 19-05f). */
.manifold-renderer-gauge-wrap {
  padding: 10px 12px;
}
.manifold-renderer-gauge-wrap[data-nopad="true"] {
  padding: 0;
}

/* MultiGauge sub-gauge label header (small uppercase ID above each arc).
 * Pre-migration:
 *   { fontFamily:'"JetBrains Mono",monospace', fontSize:9,
 *     color:'var(--mt-muted)', letterSpacing:0.4, textTransform:'uppercase',
 *     fontWeight:600, marginBottom:0, whiteSpace:'nowrap' }
 * letterSpacing/textTransform/whiteSpace stay inline (not FORBIDDEN).
 * Class owns font-size + color + weight + margin-bottom. Mono composes. */
/* #210: the caption under each dial is a row label. Keeps --mt-muted, which
 * is what this site painted — the registry records `dim` for the part, and
 * #164's rule is that the STOCK stays whatever the site already used. */
.manifold-renderer-multigauge-label {
  font-size: 9px;
  color: var(--mt-part-rowlabel, var(--mt-muted));
  font-weight: var(--mt-weight-semibold);
  margin-bottom: 0;
}

/* Dial unit suffix — the "%" or "GB" beside a gauge's number (#75).
 *
 * Pre-migration this was `fontWeight="300"` as an SVG presentation attribute on
 * two <tspan>s, which is outside the reach of the theme's Weight knob AND of a
 * card's own Text weight. It is written as an OFFSET rather than as the nearest
 * token because there is no 300 in the ramp: the four steps are 400/500/600/700
 * and the suffix is deliberately one step under the lightest of them. Naming
 * regular here instead would have quietly bolded two dials the day this landed.
 *
 * The offset composes: on a card set to Bolder, --mt-weight-regular is itself a
 * calc() one step up, so the suffix rises with the number it sits beside and
 * keeps its distance from it. */
.manifold-renderer-dial-unit {
  font-weight: calc(var(--mt-weight-regular, 400) - 100);
}

/* MultiGauge unreceived " (no data yet)" suffix span. Pre-migration:
 *   { color:'var(--mt-dim)', marginLeft:4, textTransform:'none' } */
.manifold-renderer-multigauge-no-data {
  color: var(--mt-dim);
  margin-left: 4px;
}

/* MultiGauge empty-grid placeholder div. Pre-migration:
 *   { fontFamily:'"JetBrains Mono",monospace', fontSize:11,
 *     color:'var(--mt-muted)', fontStyle:'italic' } */
.manifold-renderer-multigauge-empty {
  font-size: 11px;
  color: var(--mt-muted);
}

/* EasyPie center-number text. Pre-migration:
 *   { fontFamily:'"JetBrains Mono", monospace', fontWeight:600,
 *     fontSize:28, color: ringColor, lineHeight:1, letterSpacing:'-0.02em',
 *     fontVariantNumeric:'tabular-nums' }
 * lineHeight/letterSpacing/fontVariantNumeric stay inline (not FORBIDDEN).
 * Class owns font-weight + font-size; color via --m-renderer-color (set
 * inline by JSX from ringColor which is state-driven). Mono composes. */
.manifold-renderer-easypie-center {
  font-weight: var(--mt-weight-semibold);
  /* Honors the named text-size menu: the renderer sets --m-renderer-easypie-size
   * (scaled from this 28px base by the textSize token) ONLY when style.textSize
   * is chosen. Unset → fall back to 28px so an untouched easyPie is pixel-identical. */
  font-size: var(--m-renderer-easypie-size, 28px);
  color: var(--m-renderer-color, var(--mt-fg));
}

/* EasyPie center-unit span (% or unit string after the big number).
 * Pre-migration:
 *   { fontSize:14, color:'var(--mt-muted)', fontWeight:300, marginLeft:2 } */
.manifold-renderer-easypie-unit {
  font-size: 14px;
  color: var(--mt-muted);
  font-weight: var(--mt-weight-regular);
  margin-left: 2px;
}

/* EasyPie detail row below the ring (raw value / max info). Pre-migration:
 *   { fontFamily:'"JetBrains Mono", monospace', fontSize:9,
 *     color:'var(--mt-muted)', letterSpacing:'0.04em', marginTop:4,
 *     textAlign:'center', maxWidth:'90%',
 *     overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }
 * letterSpacing/maxWidth stay inline. Truncate trio via .manifold-renderer-
 * truncate. Class owns font-size + color + marginTop + textAlign. */
/* #210: the "80% of 60 °C" under the ring is a row label. */
.manifold-renderer-easypie-detail {
  font-size: 9px;
  color: var(--mt-part-rowlabel, var(--mt-muted));
  margin-top: 4px;
  text-align: center;
}

/* ── DonutRenderer ───────────────────────────────────────────────────
 *
 * Donut slice colors flow through the per-segment `stroke` attribute on
 * <path> (SVG attr, not CSS — not audited). The inline-style sites are
 * the center label, the legend rows, the swatch chips.
 *
 * Donut center "TOTAL" big number. Pre-migration:
 *   { fontFamily:'"JetBrains Mono", monospace', fontWeight:600,
 *     fontSize:26, color:'var(--mt-fg)', lineHeight:1,
 *     letterSpacing:'-0.02em', fontVariantNumeric:'tabular-nums' } */
/* #210: the total in the middle of a ring is a big number, and this ONE rule
 * paints it for both `pie` and `donutMulti` — two rows of the audit table
 * close on one edit, the same way rankedBars and barChart shared a class in
 * #208. */
.manifold-renderer-donut-total {
  font-weight: var(--mt-weight-semibold);
  font-size: 26px;
  color: var(--mt-part-big, var(--mt-fg));
}

/* Donut center-label subtitle ("TOTAL" or unit). Pre-migration:
 *   { fontFamily:'"JetBrains Mono", monospace', fontSize:9,
 *     color:'var(--mt-muted)', letterSpacing:'0.1em', marginTop:4,
 *     textTransform:'uppercase' } */
/* #210: the TOTAL caption under that number is a row label. */
.manifold-renderer-donut-center-label {
  font-size: 9px;
  color: var(--mt-part-rowlabel, var(--mt-muted));
  margin-top: 4px;
}

/* Unit suffix beside the donut center total (donut-center-unit, 2026-06-08).
 * Proportional to the total (em, not px) so it scales with whatever font-size
 * the total renders at; smaller + dimmer so it reads as a unit, not a digit. */
.manifold-renderer-donut-center-unit {
  font-size: 0.5em;
  opacity: 0.7;
  margin-left: 0.1em;
}

/* Donut / pie content row (dial left, legend right). Gap G (Phase 98):
 * giving this row a class lets the portrait media rule flip it to a column
 * (legend under the dial) without inline aesthetic styles.
 * Structural flex props: row, centered, gap. Position/minHeight stay inline
 * (layout state driven by the containing flex context). */
.manifold-renderer-donut-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

/* Donut legend column wrapper. Pre-migration:
 *   { flex:'1 1 auto', minWidth:0, maxWidth:160,
 *     display:'flex', flexDirection:'column', gap:3,
 *     fontFamily:'"JetBrains Mono", monospace', fontSize:10,
 *     color:'var(--mt-dim)', overflow:'hidden' }
 * flex/min-width/display/gap stay inline. maxWidth and color/font-size
 * live here so the portrait rule can drop the width cap (Gap G, Phase 98). */
.manifold-renderer-donut-legend {
  /* EDITOR-03 (999.x follow-up): the cap scales with textSize too — POLISH-02
   * scaled the font but left max-width pinned at 170px, so large text truncated
   * the legend labels (User -> U...) while card space sat unused. JSX sets
   * --m-renderer-donut-legend-maxw = 170px * the same _pieMultiScale; unset
   * (no textSize chosen) falls back to the original 170px, pixel-identical. */
  max-width: var(--m-renderer-donut-legend-maxw, 170px);
  /* POLISH-02: textSize-aware. JSX sets --m-renderer-donut-legend-size
   * only when style.textSize is chosen; unset leaves the 10px default. */
  font-size: var(--m-renderer-donut-legend-size, 10px);
  color: var(--mt-dim);
}

/* Donut legend swatch box (small color square next to label). Pre-migration:
 *   { flex:'0 0 8px', width:8, height:8, background: s.color, borderRadius:1 }
 * flex/width/height stay inline. background per-slice via --m-renderer-color
 * SET PER SWATCH (preserves per-slice differentiation). borderRadius baked. */
.manifold-renderer-donut-swatch {
  background: var(--m-renderer-color, var(--mt-muted));
  border-radius: 1px;
}

/* Donut legend percent column. Pre-migration:
 *   { color:'var(--mt-muted)', fontVariantNumeric:'tabular-nums', flexShrink:0 } */
.manifold-renderer-donut-pct {
  color: var(--mt-muted);
}

/* ── StackedBarsTimeRenderer ─────────────────────────────────────────
 *
 * SVG-only chart; rect fills use the `fill` SVG attribute (not audited).
 * The only inline-style sites are the outer wrappers (padding).
 *
 * Series legend row rendered below the SVG. Uses the shared hint-text
 * class (--m-renderer-hint-size, 9px default) so a single CSS var from
 * the textSize gate covers both the legend dots and the label text.
 * POLISH-02: JSX sets --m-renderer-hint-size on the outer wrapper only
 * when style.textSize is chosen; unset cards stay pixel-identical. */
.manifold-renderer-stacked-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  justify-content: center;
  padding-top: 2px;
}

/* ── DistributionRenderer ────────────────────────────────────────────
 *
 * SVG-only chart; rect fills via SVG `fill` attribute. The only inline-
 * style site is the outer wrapper padding.
 * POLISH-02: axis tick / P99 text uses SVG fontSize attribute computed
 * from a JS variable scaled by the _txt map; no CSS class needed for
 * the font-size itself (SVG presentation attributes bypass the eslint
 * inline-style rule). The 9px base value is the scale denominator. */

/* ── SparklineAnomalyRenderer ────────────────────────────────────────
 *
 * Sparkline body uses SVG fill/stroke (not audited). Inline-style sites
 * are the header row (label + showValue numeric readout) and outer wrap.
 *
 * Header value-readout span. Pre-migration:
 *   { fontFamily:'"JetBrains Mono",monospace', fontSize:14, fontWeight:600,
 *     color: lineColor, fontVariantNumeric:'tabular-nums' }
 * Mono composes. Color flows through --m-renderer-color (lineColor today
 * is `var(--mt-accent)` baked in; routing through the prop means a
 * future per-card override is one line of JSX away). */
.manifold-renderer-sparkanom-val {
  /* POLISH-02: textSize-aware. JSX sets --m-renderer-sparkanom-val-size
   * only when style.textSize is chosen; unset leaves the 14px default. */
  font-size: var(--m-renderer-sparkanom-val-size, 14px);
  font-weight: var(--mt-weight-semibold);
  color: var(--m-renderer-color, var(--mt-accent));
}

/* Sparkline-anomaly value-unit span. Pre-migration:
 *   { fontSize:10, color:'var(--mt-muted)', marginLeft:3 } */
/* #210: the unit after the reading asks the Unit / suffix part. */
.manifold-renderer-sparkanom-unit {
  font-size: 10px;
  color: var(--mt-part-unit, var(--mt-muted));
  margin-left: 3px;
}

/* =========================================================================
 * Phase 19 Plan 06 — manifold.jsx dashboard-shell escape-hatch classes.
 *
 * Same authoring rules as 19-05a..g (visual primitives bundled; runtime
 * values flow through CSS custom properties; aesthetic values route
 * through --mt-* tokens). These classes back the shell migration: the
 * error-boundary fallback, modal-chrome (DesignHub / Users / AppliedCards
 * / ServiceListBuilder), tab-bar drag/rename affordances, the gear menu
 * + theme picker, the role/identity strip, the audit tab. Naming uses
 * `manifold-shell-*` so they're easy to scan as a family.
 * ========================================================================= */

/* ── Error-boundary fallback shell ───────────────────────────────────
 * The error boundary is special: it must paint even when the design
 * system has half-loaded. min-height:100vh keeps the fallback full-
 * viewport regardless of parent container collapse; monospace font is
 * deliberate (matches the diagnostic content). */
.manifold-error-boundary-root {
  min-height: 100vh;
  font-family: var(--m-font-mono);
}
.manifold-error-boundary-pre {
  white-space: pre-wrap;
  word-break: break-all;
  margin: 0;
}
.manifold-error-boundary-summary {
  cursor: pointer;
}
.manifold-error-boundary-hint {
  font-size: 10px;
}

/* ── TabBar — drag/rename/minRole affordances ────────────────────────
 * Drag-over visual: a 3px inset bar on the left edge using the page
 * accent (currentColor fallback for tabs whose colors are inherited).
 * data-dragover="true" replaces the inline `boxShadow: 'inset 3px 0 0
 * var(--m-accent, currentColor)'` spread the pre-migration JSX used.
 * Declarative selector beats imperative-spread because the DOM diff
 * is cleaner (an attribute flip vs a whole style-object swap). */
.manifold-tab[data-dragover="true"] {
  box-shadow: inset 3px 0 0 var(--m-accent, currentColor);
}
/* Inline rename-input chrome inside a tab cell. transparent bg +
 * currentColor border + color:inherit means the input picks up the
 * tab's text color (including the active-tab accent) so it visually
 * "becomes the tab label" while focused.  font:inherit pulls the
 * tab strip's font in so it matches the surrounding label perfectly. */
.manifold-tab-rename-input {
  background: transparent;
  border: 1px solid currentColor;
  border-radius: 3px;
  color: inherit;
  font: inherit;
  padding: 0 4px;
}
/* Minimum-role select chrome inside a tab cell — same color-inherit
 * posture as the rename input.  Uses the mt-border token so it skins
 * with the theme; rgba fallback covers the (rare) case where the var
 * isn't loaded yet at first paint. */
.manifold-tab-minrole-select {
  font-size: calc(10px * var(--m-renderer-text-scale, 1));
  padding: 1px 2px;
  margin-left: 4px;
  background: transparent;
  color: inherit;
  border: 1px solid var(--mt-border, rgba(255, 255, 255, 0.2));
  border-radius: 2px;
  font-family: inherit;
}

/* ── Empty-tab blurb (EmptyOrAddCard "this tab is empty" copy) ───────
 * Block-centered narrow body text inside the empty-tab panel.  Pre-
 * migration sites passed maxWidth inline so callers could vary the
 * width (540 for the fresh-install Services greeting, 480 for the
 * generic empty-tab message).  margin:'0 auto' moved here so the
 * caller only owns the dimension knob. */
.manifold-empty-tab-blurb {
  margin: 0 auto;
}

/* ── GearMenu — theme picker row + drag-shortcuts footnote ───────────
 * Indented child rows under the Theme expandable.  28px indent is the
 * gear-menu's nested-item convention; outside the utility-class
 * spacing scale because it's a specific affordance for "this row sits
 * under a heading" not a generic spacing step. */
.manifold-gear-theme-row {
  padding-left: 28px;
}
/* Drag-shortcuts footnote block — appears inside the gear menu only
 * while tabEditMode is true.  Stacked tips with small dim text, tighter
 * vertical padding than a regular gear-menu row, opacity dimmed because
 * the block is informational, not actionable. */
.manifold-gear-drag-shortcuts {
  font-size: 10px;
  line-height: 1.5;
  opacity: 0.75;
  flex-direction: column;
  align-items: flex-start;
  padding-top: 6px;
  padding-bottom: 6px;
}
.manifold-gear-drag-shortcuts-title {
  font-weight: var(--mt-weight-semibold);
  margin-bottom: 2px;
}
/* <kbd> elements inside the drag-shortcuts block default to a system
 * monospace; font:inherit pulls in the gear menu's sans so the inline
 * key-pills don't visually clash with the body text. */
.manifold-gear-drag-shortcuts-kbd {
  font-family: inherit;
}

/* ── Preview-as bar (owner-only "browse as another role" chrome) ────
 * Sits above the header.  Idle state: transparent bg + muted gray
 * text on a hairline border (visually neutral, so it doesn't compete
 * for attention).  Active state (data-active="true"): cyan fill +
 * deep-canvas text + 2px cyan bottom rule, so the owner can't miss
 * that the dashboard is showing a non-real view.
 *
 * Hex values (cyan #5eead4 / deep #0f1418 / muted #94a3b8 / hairline
 * #2a2f36) are intentionally hardcoded — this is a diagnostic surface
 * that must read distinct from any theme's accent.  If Karl wants
 * per-theme preview accents later, re-tokenize to --m-preview-* and
 * override per-theme.
 *
 * The bar uses `display:flex` + `gap:10px` baked in; the only inline
 * keys remaining on the JSX are zIndex + position (both allowlisted
 * and behaviorally load-bearing).
 *
 * data-active flips the bar, the status span (font-weight bold) and
 * the select (cyan text-on-deep vs gray text-on-deep) all together. */
.manifold-preview-as-bar {
  padding: 6px 12px;
  background: transparent;
  color: #94a3b8;
  border-bottom: 1px solid #2a2f36;
  font-family: var(--m-font-mono);
  font-size: 11px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.manifold-preview-as-bar[data-active="true"] {
  background: #5eead4;
  color: #0f1418;
  border-bottom: 2px solid #5eead4;
}
.manifold-preview-as-label {
  display: flex;
  align-items: center;
  gap: 6px;
}
.manifold-preview-as-status {
  opacity: 0.85;
  font-weight: var(--mt-weight-regular);
}
.manifold-preview-as-status[data-active="true"] {
  font-weight: var(--mt-weight-bold);
}
.manifold-preview-as-select {
  background: #0f1418;
  color: #e5e7eb;
  border: 1px solid #2a2f36;
  border-radius: 3px;
  padding: 2px 6px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  cursor: pointer;
}
.manifold-preview-as-select[data-active="true"] {
  color: #5eead4;
  border-color: #0f1418;
}
.manifold-preview-as-exit-btn {
  background: #0f1418;
  color: #5eead4;
  border: 1px solid #0f1418;
  border-radius: 3px;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: var(--mt-weight-semibold);
  font-family: var(--m-font-mono);
  cursor: pointer;
  margin-left: auto;
}

/* ── Safe-mode banner padding override ───────────────────────────────
 * The default .manifold-banner uses --m-pad-md/lg.  Safe mode wants
 * tighter vertical + wider horizontal pad because it sits above the
 * regular header (which has its own page-frame horizontal pad).  This
 * variant lets the two stripes align without nesting selectors. */
.manifold-banner--safe-mode {
  padding: 8px 20px;
}

/* ── Shared app-modal shell (DesignHub / Users / AppliedCards /
 *    ServiceListBuilder) ─────────────────────────────────────────────
 * Four owner-facing modals in manifold.jsx all share the same chrome:
 * a fixed-position overlay backdrop, a centered card with deep-canvas
 * background and a hairline border, a header strip with brand-amber
 * title + slate-gray subtitle + ghost close-button, and a scrollable
 * body region.  Pre-migration each modal duplicated ~10 inline-style
 * blocks; this family collapses them into one named primitive set.
 *
 * Hex values (overlay rgba(8,12,18,0.78), deep #0f1418, hairline
 * #2a2f36, slate #94a3b8, brand-amber #f5a623, slate-dim #64748b,
 * #e6edf3 body fg) preserved exactly — these are app-shell colors,
 * intentionally distinct from per-theme accents to keep modals
 * readable across the four themes.
 *
 * Use the `.manifold-app-modal-card` wrapper instead of the inline
 * fixed-position div + nested onClick={stopPropagation} pattern.
 * The backdrop click-to-close stays on the JSX (it's a behavioral
 * concern, not a style concern). */
.manifold-app-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(8, 12, 18, 0.78);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--m-font-mono);
}
.manifold-app-modal-card {
  background: #0f1418;
  border: 1px solid #2a2f36;
  border-radius: 6px;
  max-height: 82vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  color: #e6edf3;
}
.manifold-app-modal-header {
  padding: 14px 18px;
  border-bottom: 1px solid #2a2f36;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.manifold-app-modal-title {
  font-size: 14px;
  font-weight: var(--mt-weight-semibold);
  color: #f5a623;
}
.manifold-app-modal-subtitle {
  font-size: 10px;
  color: #64748b;
  margin-top: 2px;
}
.manifold-app-modal-close-btn {
  background: transparent;
  border: 1px solid #2a2f36;
  color: #94a3b8;
  font-family: inherit;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 4px;
  cursor: pointer;
}
.manifold-app-modal-close-btn:hover {
  border-color: var(--mt-hover-border, #f5a62388);
  color: var(--mt-hover-fg, #e6edf3);
}

/* DesignHub-specific tile grid + tile button.  The tile uses :hover
 * for the lift + accent-border + slight translate (replaces the
 * pre-migration onMouseEnter/Leave imperative style-mutation pattern;
 * same declarative-over-imperative posture as 19-05d's
 * .manifold-renderer-list-row hover).  data-disabled="true" swaps the
 * tile to a flat, non-interactive variant. */
.manifold-design-hub-grid {
  padding: 14px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  overflow-y: auto;
}
.manifold-design-hub-tile {
  text-align: left;
  background: #141a20;
  border: 1px solid #2a3540;
  border-radius: 5px;
  padding: 14px 16px;
  cursor: pointer;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-family: inherit;
  color: #e6edf3;
  transition: background 140ms, border-color 140ms, transform 140ms;
}
.manifold-design-hub-tile:hover:not([data-disabled="true"]) {
  background: var(--mt-hover-bg, #1a232c);
  border-color: var(--mt-hover-border, #f5a62388);
  transform: translateY(-1px);
}
.manifold-design-hub-tile[data-disabled="true"] {
  background: #0c1014;
  border-color: #1a1f25;
  cursor: not-allowed;
  color: #3a4250;
}
.manifold-design-hub-tile-glyph {
  font-size: 22px;
  line-height: 24px;
  color: var(--m-tile-accent, #f5a623);
  flex: 0 0 28px;
}
.manifold-design-hub-tile[data-disabled="true"] .manifold-design-hub-tile-glyph {
  color: #3a4250;
}
.manifold-design-hub-tile-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.manifold-design-hub-tile-title {
  font-size: 12px;
  font-weight: var(--mt-weight-semibold);
}
.manifold-design-hub-tile-description {
  font-size: 10px;
  opacity: 0.72;
  line-height: 1.45;
  color: #94a3b8;
}
.manifold-design-hub-tile[data-disabled="true"] .manifold-design-hub-tile-description {
  color: #3a4250;
}

/* ── App-modal body + footer chrome (shared by Users / AppliedCards
 *    / ServiceListBuilder) ───────────────────────────────────────────
 * Generic scrollable body region under the header, and a small
 * footer strip with hairline-top + slate-dim text.  Symmetric with
 * the header class above. */
.manifold-app-modal-body {
  overflow: auto;
  padding: 12px 0;
}
.manifold-app-modal-footer {
  padding: 10px 18px;
  border-top: 1px solid #2a2f36;
  font-size: 10px;
  color: #64748b;
  line-height: 1.5;
}
.manifold-app-modal-loading,
.manifold-app-modal-empty {
  padding: 12px 18px;
  font-size: 11px;
  color: #64748b;
}
.manifold-app-modal-error {
  margin: 4px 18px 12px;
  padding: 8px 10px;
  border: 1px solid #b91c1c66;
  border-radius: 4px;
  background: #7f1d1d22;
  color: #fca5a5;
  font-size: 11px;
  line-height: 1.4;
}
.manifold-app-modal-error-hint {
  margin-top: 6px;
  opacity: 0.85;
}
/* USERS-01 / D-01 — setup guidance screen when MANIFOLD_AUTHENTIK_API_TOKEN
 * isn't set yet. Styled as informational (amber tint), not as an error
 * (red tint) — the distinction matters for fresh-install UX. */
.manifold-users-setup-hint {
  margin: 4px 18px 12px;
  padding: 12px 14px;
  border: 1px solid #b4560044;
  border-radius: 4px;
  background: #78350f18;
  color: #fcd34d;
  font-size: 11px;
  line-height: 1.6;
}
.manifold-users-setup-hint strong {
  display: block;
  margin-bottom: 8px;
  font-size: 12px;
  color: #fbbf24;
}
.manifold-users-setup-hint ol {
  margin: 0;
  padding-left: 18px;
}
.manifold-users-setup-hint li {
  margin-bottom: 6px;
}
.manifold-users-setup-hint code,
.manifold-users-setup-hint pre {
  font-family: var(--m-font-mono);
  background: #0004;
  padding: 1px 4px;
  border-radius: 3px;
}
.manifold-users-setup-hint pre {
  display: inline;
  padding: 2px 6px;
}

/* ── CanvasSettingsModal — snap grid + grid-lines toggle ─────────────
 * Phase 43 DRAG-04 / DRAG-05. Reuses the app-modal shell; these classes
 * style the two settings rows. Theme tokens only (no aesthetic literals)
 * so it tracks whatever theme is active. */
.manifold-canvas-settings-row {
  padding: 12px 18px;
  border-bottom: 1px solid var(--mt-border, #2a2f36);
}
.manifold-canvas-settings-row:last-child {
  border-bottom: none;
}
.manifold-canvas-settings-label {
  font-size: var(--mt-text-sm-size, 13px);
  color: var(--mt-fg);
  font-weight: var(--mt-weight-semibold);
}
.manifold-canvas-settings-help {
  margin: 4px 0 8px;
  font-size: var(--mt-text-xs-size, 11px);
  color: var(--mt-muted, #64748b);
  line-height: 1.5;
}
.manifold-canvas-settings-choices {
  display: flex;
  gap: 6px;
}
.manifold-canvas-settings-choice {
  flex: 1;
  padding: 7px 10px;
  border: 1px solid var(--mt-border, #2a2f36);
  border-radius: 6px;
  background: transparent;
  color: var(--mt-muted, #64748b);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.manifold-canvas-settings-choice:hover {
  border-color: var(--mt-accent, #f59e0b);
  color: var(--mt-fg);
}
.manifold-canvas-settings-choice[data-selected="true"] {
  border-color: var(--mt-accent, #f59e0b);
  color: var(--mt-accent, #f59e0b);
  background: color-mix(in srgb, var(--mt-accent, #f59e0b) 12%, transparent);
}
.manifold-canvas-settings-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}
.manifold-canvas-settings-toggle input {
  accent-color: var(--mt-accent, #f59e0b);
  cursor: pointer;
}

/* ── UsersModal table + role pills ───────────────────────────────────
 * Owner-only promote/demote table.  Reuses the app-modal shell above.
 * Per-role color flows through --m-role-color (different name than
 * --m-renderer-color to avoid cross-talk with the renderer family —
 * this is auth-domain not renderer-domain).  Set + promote buttons
 * use color-mix(40%) for the border tint (replaces ${rc}66). */
.manifold-users-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
.manifold-users-table-head {
  color: #64748b;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.manifold-users-table-head th {
  padding: 6px 18px;
  text-align: left;
}
.manifold-users-table-head th[data-align="right"] {
  text-align: right;
}
.manifold-users-table-row {
  border-top: 1px solid #1c2228;
}
.manifold-users-table-row td {
  padding: 10px 18px;
}
.manifold-users-table-username {
  color: #e6edf3;
}
.manifold-users-table-groups {
  color: #94a3b8;
  font-size: 10px;
}
.manifold-users-table-actions {
  text-align: right;
  white-space: nowrap;
}
/* Role pill (slightly tighter padding than .manifold-auth-role-chip
 * because the table cell has its own row-padding and a wider pill
 * would crowd the column).  Per-role color via --m-role-color. */
.manifold-users-role-pill {
  color: var(--m-role-color, #64748b);
  border: 1px solid color-mix(in srgb, var(--m-role-color, #64748b) 33%, transparent);
  padding: 2px 6px;
  border-radius: 3px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: var(--mt-weight-semibold);
}
.manifold-users-singleton-marker {
  color: #64748b;
  font-size: 10px;
}
.manifold-users-flash {
  font-size: 10px;
}
.manifold-users-flash[data-ok="true"] { color: #22c55e; }
.manifold-users-flash[data-ok="false"] { color: #ef4444; }
.manifold-users-actions-group {
  display: inline-flex;
  gap: 4px;
}
/* Section-level button row for the Default Dashboard block — block-level
 * so the publish + push-all buttons sit on their own line with padding
 * matching the modal body rhythm. */
.manifold-users-default-section {
  display: flex;
  gap: 6px;
  padding: 8px 18px 4px;
}
/* Flash line below the Default Dashboard buttons — same left-edge as
 * the button row. */
.manifold-users-default-flash-row {
  padding: 4px 18px 0;
  display: flex;
  gap: 10px;
}
/* Per-role set button.  Idle: target role color + 40% border tint.
 * Current (already this role): muted hairline border, slate text,
 * cursor:default.  Pending (other-row mid-action): opacity dim.
 * Reuses --m-role-color the same way the pill does. */
.manifold-users-set-btn {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--m-role-color, #64748b) 40%, transparent);
  color: var(--m-role-color, #64748b);
  font-family: inherit;
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 3px;
  cursor: pointer;
}
.manifold-users-set-btn[data-current="true"],
.manifold-users-set-btn:disabled[data-current="true"] {
  border-color: #2a2f36;
  color: #64748b;
  cursor: default;
}
.manifold-users-set-btn:disabled:not([data-current="true"]) {
  opacity: 0.5;
}

/* ── AppliedCardsModal — 3-level audit table (cards → widgets → verbs) ─
 * Reuses the OLD .manifold-modal shell (not the .manifold-app-modal-*
 * family); the modal is older and predates the consolidated app-modal
 * chrome.  Phase 19's job here is the table family only.
 *
 * Tables nest 3 levels deep: outer cards table → per-card widgets
 * sub-table → per-widget verbs sub-table.  Each level uses the same
 * column padding rhythm (6/8/4 px) with progressive opacity reduction
 * so the nesting reads visually.
 *
 * Per-level base class lets a future Phase re-skin a single level
 * (e.g. "make the verbs row tighter on phones") without touching JSX. */
.manifold-applied-intro {
  font-size: 12px;
  opacity: 0.7;
  margin-bottom: 12px;
  line-height: 1.5;
  font-family: var(--m-font-mono);
}
/* #96: "you are now editing" notice, above the table.
 *
 * Amber rather than red or muted grey, deliberately. Red is this modal's error
 * line and nothing has gone wrong here; muted grey is the intro paragraph
 * directly above it, which reads as help text and gets skipped. Amber is the
 * accent edit mode already uses across the rest of the chrome, so the notice
 * matches the state it is reporting. The left rule and tinted field are the
 * only decoration — one strip of colour in an otherwise monochrome modal. */
.manifold-applied-editing-note {
  font-family: var(--m-font-mono);
  font-size: 11px;
  line-height: 1.5;
  color: var(--m-text, #e8e6e3);
  background: var(--m-accent-low, rgba(245, 166, 35, 0.12));
  border-left: 2px solid var(--m-accent, #f5a623);
  border-radius: 0 var(--m-radius-sm, 2px) var(--m-radius-sm, 2px) 0;
  padding: var(--m-pad-sm, 8px) var(--m-pad-md, 14px);
  margin-bottom: 12px;
}
/* Phase 999.13: header-level audit bulk toggle bar in the Applied elements modal. */
.manifold-applied-bulk-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  font-size: 12px;
  font-family: var(--m-font-mono);
}
.manifold-applied-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  font-family: var(--m-font-mono);
}
.manifold-applied-table-head {
  text-align: left;
  border-bottom: 1px solid var(--m-border, #444);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.6;
}
.manifold-applied-table-head th {
  padding: 6px 8px;
}
.manifold-applied-table-head th[data-narrow="caret"]  { padding: 6px 4px; width: 24px; }
.manifold-applied-table-head th[data-narrow="role"]   { padding: 6px 8px; width: 100px; text-align: center; }
.manifold-applied-card-row td {
  padding: 8px;
  vertical-align: top;
}
.manifold-applied-card-row td[data-cell="caret"]   { padding: 8px 4px; text-align: center; }
.manifold-applied-card-row td[data-cell="centered"] { padding: 8px; text-align: center; }
.manifold-applied-card-row[data-busy="true"]      { opacity: 0.5; }
.manifold-applied-card-row[data-open-below="true"] td { border-bottom: none; }
.manifold-applied-card-row:not([data-open-below="true"]) td {
  border-bottom: 1px solid var(--m-border-soft, rgba(255, 255, 255, 0.05));
}
/* Caret button — disclosure arrow inside the leftmost column. */
.manifold-applied-caret-btn {
  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
  font-size: 12px;
  padding: 0;
  line-height: 1;
  font-family: var(--m-font-mono);
}
.manifold-applied-card-name {
  font-weight: var(--mt-weight-semibold);
}
.manifold-applied-card-sub {
  font-size: 10px;
  opacity: 0.5;
}
.manifold-applied-card-where {
  opacity: 0.85;
}
/* Min-role <select> chrome.  Reuses the --mt-border token to track
 * theme switches.  color-scheme:dark + explicit option chrome together
 * fix the white-on-white native-popup bug: without these, Chrome and
 * Firefox draw the <option> list against the OS-native light surface
 * regardless of the page colors, leaving the dropdown unreadable on a
 * dark dashboard. */
.manifold-applied-minrole-select {
  font-size: 11px;
  padding: 2px 4px;
  font-family: inherit;
  background: var(--mt-bg-elev, var(--mt-bg, #15181d));
  color: var(--mt-fg, inherit);
  border: 1px solid var(--mt-border, rgba(255, 255, 255, 0.2));
  border-radius: 2px;
  color-scheme: dark;
}
.manifold-applied-minrole-select option {
  background: var(--mt-bg-elev, var(--mt-bg, #15181d));
  color: var(--mt-fg, #e8e6e3);
}
/* #96: the hoverable wrapper that carries the "why is this off" text. A
 * disabled <select> gets no mouse events in Chrome, so its own title is a
 * tooltip nobody can summon — and disabled is exactly when there is something
 * to explain. inline-block so the span has a box to hover. */
.manifold-applied-minrole-wrap {
  display: inline-block;
}
.manifold-applied-minrole-select:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* Expanded widgets sub-row (level 2). */
.manifold-applied-widgets-cell {
  padding: 0 8px 12px 30px;
  border-bottom: 1px solid var(--m-border-soft, rgba(255, 255, 255, 0.05));
}
.manifold-applied-widgets-empty {
  font-size: 11px;
  opacity: 0.55;
  font-style: italic;
}
.manifold-applied-widgets-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}
.manifold-applied-widgets-table-head {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.5;
  border-bottom: 1px solid var(--m-border-soft, rgba(255, 255, 255, 0.04));
}
.manifold-applied-widgets-table-head th {
  text-align: left;
  padding: 4px 8px;
}
.manifold-applied-widgets-table-head th[data-narrow="renderer"] { width: 130px; }
.manifold-applied-widgets-table-head th[data-narrow="audit"]    { text-align: center; width: 70px; }
.manifold-applied-widget-row td {
  padding: 4px 8px;
}
.manifold-applied-widget-row td[data-cell="renderer"]  { opacity: 0.7; }
.manifold-applied-widget-row td[data-cell="centered"]  { text-align: center; }
.manifold-applied-widget-row[data-busy="true"]         { opacity: 0.5; }
.manifold-applied-widget-id {
  font-size: 9px;
  opacity: 0.45;
  margin-left: 8px;
}
.manifold-applied-widget-noaudit-marker {
  opacity: 0.35;
}
/* Caret button inside the widget row (smaller than the card-row caret). */
.manifold-applied-widget-caret-btn {
  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
  font-size: 11px;
  padding: 0;
  margin-right: 6px;
  line-height: 1;
  font-family: var(--m-font-mono);
}
/* Expanded verbs sub-row (level 3). */
.manifold-applied-verbs-row {
  background: rgba(255, 255, 255, 0.02);
}
.manifold-applied-verbs-cell {
  padding: 6px 8px 10px 28px;
}
.manifold-applied-verbs-intro {
  font-size: 10px;
  opacity: 0.55;
  margin-bottom: 6px;
  line-height: 1.5;
}
.manifold-applied-verbs-empty {
  font-size: 11px;
  opacity: 0.55;
  font-style: italic;
}
.manifold-applied-verbs-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}
.manifold-applied-verbs-table-head {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.5;
}
.manifold-applied-verbs-table-head th {
  text-align: left;
  padding: 3px 8px;
}
.manifold-applied-verbs-table-head th[data-cell="centered-60"] { text-align: center; padding: 3px 8px; width: 60px; }
.manifold-applied-verbs-table-head th[data-cell="centered-50"] { text-align: center; padding: 3px 8px; width: 50px; }
.manifold-applied-verbs-row-data {
}
.manifold-applied-verbs-row-data[data-busy="true"] { opacity: 0.5; }
.manifold-applied-verbs-row-data td {
  padding: 3px 8px;
}
.manifold-applied-verbs-row-data td[data-cell="verb-name"] {
  font-family: var(--m-font-mono);
}
.manifold-applied-verbs-row-data td[data-cell="centered"] {
  text-align: center;
}
.manifold-applied-verbs-inherit-marker {
  margin-left: 6px;
  opacity: 0.45;
  font-size: 9px;
}

/* ── ServiceListBuilderModal — service-card-builder form ─────────────
 * Builder form for "auto-iterate every entry in /admin/services."  Two
 * style modes (placards / table) toggled via radio cards; table mode
 * exposes column checkboxes + framing knobs + zebra options.
 *
 * Reuses the OLDER .manifold-modal-* shell (same as AppliedCardsModal).
 *
 * Three repeating visual primitives that the JSX used to pass via two
 * inline-style constants (sectionLabelStyle + cbRow's label-style):
 *
 *   .manifold-slb-section-label  - 10px bold uppercase tracker (a
 *                                  visual divider between form
 *                                  groups).  Used ~6 times.
 *   .manifold-slb-cb-row         - 12px mono row with checkbox + label
 *                                  + optional hint.  Used ~10 times.
 *   .manifold-slb-cb-hint        - small dim hint inside a cb-row.
 *
 * Plus a few one-off primitives:
 *   .manifold-slb-intro          - body intro paragraph
 *   .manifold-slb-style-row      - flex container for the two style cards
 *   .manifold-slb-style-card     - one of the two style-mode toggles;
 *                                  data-active="true" swaps to the accent
 *                                  border + tinted bg
 *   .manifold-slb-col-grid       - 2-col checkbox grid for columns
 *   .manifold-slb-zebra-grid     - 2-col grid for even/odd color inputs
 *   .manifold-slb-zebra-hint     - full-width hint under the zebra inputs
 *   .manifold-slb-range-row      - container for a label + range input
 *   .manifold-slb-range-label    - small label above a range input
 *   .manifold-slb-actions-row    - right-aligned button group at the bottom
 */
.manifold-slb-section-label {
  font-size: 10px;
  font-weight: var(--mt-weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  opacity: 0.6;
  margin-top: 14px;
  margin-bottom: 6px;
  font-family: var(--m-font-mono);
}
.manifold-slb-cb-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.4;
  font-family: var(--m-font-mono);
  cursor: pointer;
}
.manifold-slb-cb-row-checkbox {
  margin-top: 2px;
}
.manifold-slb-cb-hint {
  display: block;
  font-size: 10px;
  opacity: 0.55;
  margin-top: 2px;
}
.manifold-slb-intro {
  font-size: 12px;
  opacity: 0.7;
  margin-bottom: 4px;
  line-height: 1.45;
  font-family: var(--m-font-mono);
}
.manifold-slb-intro-code {
  margin-left: 4px;
}
.manifold-slb-style-row {
  display: flex;
  gap: 8px;
  font-family: var(--m-font-mono);
  font-size: 12px;
}
.manifold-slb-style-card {
  flex: 1;
  padding: 10px 12px;
  border: 1px solid var(--manifold-border, #333);
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  transition: border-color 0.15s, background 0.15s;
}
.manifold-slb-style-card[data-active="true"] {
  border-color: var(--manifold-accent, #5d9cec);
  background: rgba(93, 156, 236, 0.10);
}
.manifold-slb-style-card-header {
  display: flex;
  align-items: center;
  gap: 8px;
}
.manifold-slb-style-card-title {
  font-weight: var(--mt-weight-semibold);
}
.manifold-slb-style-card-hint {
  font-size: 10px;
  opacity: 0.6;
  margin-left: 22px;
}
.manifold-slb-col-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}
.manifold-slb-col-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-family: var(--m-font-mono);
  cursor: pointer;
}
.manifold-slb-range-row {
  margin-top: 8px;
  margin-left: 22px;
}
.manifold-slb-range-row--top {
  margin-top: 10px;
}
.manifold-slb-range-label {
  font-size: 11px;
  margin-bottom: 4px;
  font-family: var(--m-font-mono);
  opacity: 0.7;
}
.manifold-slb-zebra-grid {
  margin-top: 8px;
  margin-left: 22px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.manifold-slb-zebra-hint {
  grid-column: 1 / -1;
  font-size: 10px;
  opacity: 0.55;
  line-height: 1.4;
  font-family: var(--m-font-mono);
}
.manifold-slb-error-line {
  margin-top: 12px;
}
.manifold-slb-actions-row {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}

/* ── GearAuthState — identity/role/sign-in/sign-out block ────────────
 * Lives at the bottom of the gear menu's About slot.  Two branches
 * share an identical outer wrapper (padding, mono font, column flex,
 * gap) but differ in body color (anonymous = muted gray, signed-in =
 * bright white-on-deep).  data-variant attribute swaps the color.
 *
 * Hex values (slate #94a3b8 anonymous body, #e6edf3 signed-in body,
 * warn #b8554d border + #d2945e text for sign-out, role-pill family
 * cyan/orange/violet/slate) preserved exactly per the same posture
 * as the preview-as bar — gear-menu identity chrome reads distinct
 * from theme accents on purpose.
 *
 * The role chip + sign-in button both reuse --m-renderer-color (the
 * cross-renderer per-instance color carrier from 19-05) so the JSX
 * sets ONE custom property and both border + text on the chip pick
 * it up via color-mix(33%) — same shape as .manifold-renderer-pill
 * (19-05d) but with a square 3px radius instead of the rounded
 * 9999px pill, because the gear menu's vertical-axis budget reads
 * better with a chip than a pill. */
.manifold-gear-auth-state {
  padding: 6px 14px 10px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  color: var(--mt-fg);
}
.manifold-gear-auth-state[data-variant="anonymous"] {
  color: var(--mt-dim);
}
.manifold-gear-auth-anon-msg {
  opacity: 0.75;
  line-height: 1.4;
}
.manifold-gear-auth-signin-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--m-renderer-color, #64748b) 33%, transparent);
  color: #e6edf3;
  border-radius: 4px;
  font-family: inherit;
  font-size: 11px;
  text-decoration: none;
  cursor: pointer;
  align-self: flex-start;
}
.manifold-gear-auth-userrow {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.manifold-auth-role-chip {
  color: var(--m-renderer-color, #64748b);
  border: 1px solid color-mix(in srgb, var(--m-renderer-color, #64748b) 33%, transparent);
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: var(--mt-weight-semibold);
}
.manifold-gear-auth-signout-btn {
  align-self: flex-start;
  padding: 4px 10px;
  background: transparent;
  border: 1px solid color-mix(in srgb, #b8554d 33%, transparent);
  color: #d2945e;
  border-radius: 4px;
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
}
.manifold-gear-auth-footer {
  margin-top: 4px;
  font-size: 10px;
  line-height: 1.5;
  color: var(--m-text-muted);
}
/* Anonymous branch's footer needs slightly more top margin because the
 * sign-in button above it has more visual weight than the sign-out
 * button in the signed-in branch.  Variant-scoped override. */
.manifold-gear-auth-state[data-variant="anonymous"] .manifold-gear-auth-footer {
  margin-top: 6px;
}

/* ── Header-loading placeholder ──────────────────────────────────────
 * Renders only during the brief window when the user's saved header
 * card hasn't returned from the backend yet.  Asymmetric padding
 * (12 vertical / 18 horizontal) matches the header's page-frame pad
 * so the placeholder doesn't visually "jump" when the real card
 * arrives.  Outside the utility-class spacing scale. */
.manifold-header-loading {
  padding: 12px 18px;
}

/* ── AuditTabRenderer — owner-only binding-drift diagnostic page ─────
 * Single-column max-width page wrapper.  margin:'0 auto' is the
 * load-bearing centering; max-width stays inline (dimension key) so
 * the caller can vary the column width if a future audit grows.
 * The refresh button and the bordered data panel use their own
 * escape-hatch classes (below) so the page can be re-themed as one
 * unit without touching JSX. */
.manifold-audit-tab-root {
  margin: 0 auto;
}
.manifold-audit-tab-title {
  font-size: 14px;
}
/* Ghost refresh button — same posture as a Manifold modal's secondary
 * action: bordered, theme-bg, no fill.  Hover swaps to the slightly
 * elevated surface to give a visible affordance. */
.manifold-audit-tab-refresh-btn {
  padding: 3px 10px;
  cursor: pointer;
}
.manifold-audit-tab-refresh-btn:hover:not(:disabled) {
  background: var(--mt-bg-elev);
}
/* Bordered panel that wraps the BindingDriftTable.  overflow:hidden
 * is mandatory: without it the table's first/last-row corners punch
 * through the panel's rounded radius. */
.manifold-audit-tab-panel {
  overflow: hidden;
}

/* =========================================================================
 * Health-of-dependencies modal (gear → Health) + status dot on the gear icon.
 * ========================================================================= */

.manifold-gear-status-dot {
  position: absolute;
  top: 6px; right: 6px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--m-error, #b85050);
  box-shadow: 0 0 0 2px var(--m-bg-deep, #0d0f12);
  pointer-events: none;
}
.manifold-h-gear { position: relative; }   /* anchor the dot */

.manifold-health-overall {
  padding: var(--m-pad-md);
  border-radius: var(--m-radius-md);
  font-family: var(--m-font-mono);
  font-size: 12px;
  margin-bottom: var(--m-pad-md);
}
.manifold-health-overall--ok {
  background: color-mix(in srgb, var(--m-success, #4caf50) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--m-success, #4caf50) 35%, transparent);
  color: var(--m-success, #4caf50);
}
.manifold-health-overall--bad {
  background: color-mix(in srgb, var(--m-warn, #f0a040) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--m-warn, #f0a040) 35%, transparent);
  color: var(--m-warn, #f0a040);
}
.manifold-health-fetch-err {
  font-size: 10px; color: var(--m-error, #b85050); margin-top: 4px;
  font-family: var(--m-font-mono);
}

.manifold-health-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 4px;
}
.manifold-health-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--m-pad-sm);
  padding: 6px 10px;
  background: var(--m-surface);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
}
.manifold-health-row-name {
  font-family: var(--m-font-sans);
  font-size: 13px;
  color: var(--m-text);
}
.manifold-health-row-id {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--m-text-faint);
  margin-left: 6px;
}

.manifold-health-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px;
  border-radius: 999px;
  font-family: var(--m-font-mono);
  font-size: 10px;
  white-space: nowrap;
}
.manifold-health-pill-dot {
  width: 8px; height: 8px; border-radius: 50%;
  flex-shrink: 0;
}
.manifold-health-pill--ok {
  background: color-mix(in srgb, var(--m-success, #4caf50) 18%, transparent);
  color: var(--m-success, #4caf50);
}
.manifold-health-pill--ok .manifold-health-pill-dot { background: var(--m-success, #4caf50); }
.manifold-health-pill--bad {
  background: color-mix(in srgb, var(--m-error, #b85050) 18%, transparent);
  color: var(--m-error, #b85050);
}
.manifold-health-pill--bad .manifold-health-pill-dot { background: var(--m-error, #b85050); }
.manifold-health-pill--critical {
  outline: 1px solid color-mix(in srgb, currentColor 50%, transparent);
}
.manifold-health-pill-flag {
  font-size: 8px;
  letter-spacing: 0.08em;
  font-weight: var(--mt-weight-bold);
  opacity: 0.7;
}

.manifold-health-loading {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--m-text-muted);
  padding: var(--m-pad-md) 0;
}
.manifold-health-foot {
  margin-top: var(--m-pad-md);
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--m-text-faint);
  text-align: right;
}

/* =========================================================================
 * Card-bundle import picker
 * Modal that walks the user through binding each portable-card source
 * (capability + metric) to a local instance. Outcome marker is what
 * differentiates rows: ✓ (auto-bound), ⚠ (needs pick), ✗ (skipped).
 * ========================================================================= */

.manifold-bundle-subtitle {
  padding: 0 var(--m-pad-lg) var(--m-pad-sm);
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--m-text-muted);
  border-bottom: 1px solid var(--m-border);
}

.manifold-bundle-targetrow {
  display: flex; align-items: center; gap: var(--m-pad-sm);
  padding-bottom: var(--m-pad-sm);
}

.manifold-bundle-targetrow label {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--m-text-muted);
}

.manifold-bundle-targetrow select,
.manifold-bundle-card select {
  background: var(--m-surface);
  border: 1px solid var(--m-border);
  color: var(--m-text);
  font-family: var(--m-font-mono);
  font-size: 11px;
  padding: 4px 8px;
  border-radius: var(--m-radius-sm);
}

.manifold-bundle-bulkrow {
  display: flex; align-items: center; gap: var(--m-pad-sm); flex-wrap: wrap;
  padding-bottom: var(--m-pad-sm);
  border-bottom: 1px dashed var(--m-border);
  margin-bottom: var(--m-pad-sm);
}

.manifold-bundle-bulkrow > span {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--m-text-muted);
}

.manifold-bundle-cardlist {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 6px;
}

.manifold-bundle-card {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  gap: var(--m-pad-sm);
  align-items: center;
  padding: 8px 12px;
  background: var(--m-surface);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  font-family: var(--m-font-sans);
  font-size: 13px;
}

.manifold-bundle-card--ok           { border-left: 3px solid var(--m-success, #4caf50); }
.manifold-bundle-card--prompt_needed { border-left: 3px solid var(--m-warn, #f0a040); }
.manifold-bundle-card--unmet_dep,
.manifold-bundle-card--metric_missing,
.manifold-bundle-card--instance_missing,
.manifold-bundle-card--version_mismatch {
  border-left: 3px solid var(--m-error, #b85050);
  opacity: 0.7;
}

.manifold-bundle-marker {
  font-family: var(--m-font-mono);
  font-size: 14px;
  width: 18px; text-align: center;
}

.manifold-bundle-cardtitle {
  color: var(--m-text);
}

.manifold-bundle-cardmeta {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--m-text-faint);
  white-space: nowrap;
}

.manifold-bundle-bound {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--m-text-muted);
  white-space: nowrap;
}

.manifold-bundle-detail {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--m-text-faint);
  max-width: 240px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.manifold-bundle-error {
  margin-top: var(--m-pad-sm);
  padding: var(--m-pad-sm);
  border: 1px solid var(--m-error, #b85050);
  border-radius: var(--m-radius-sm);
  color: var(--m-error, #b85050);
  font-family: var(--m-font-mono);
  font-size: 11px;
}

/* =========================================================================
 * Phase 19 Plan 07 — card-wizard.jsx escape-hatch classes.
 *
 * The card-creation wizard (Tier 1 of the three card-editing surfaces)
 * walks the user through recipe → data → placement → tune → review. It
 * was the densest remaining inline-style file at the start of Phase 19
 * Plan 07 — 302 FORBIDDEN_KEYS sites across 5 named Step components plus
 * shared preview-pane + modal-frame chrome.
 *
 * Authoring rules carry over from 19-05/06: visual primitives bundled
 * into one class when they always co-occur (the wizard's Step-tag /
 * step-title / step-subtitle trio always reads together as a section
 * header, so they're three classes that compose, not 12 utility
 * classes). Runtime values (recipe-tile selected/unselected, size
 * stepper hover) route through CSS custom properties (`--m-wizard-*`)
 * or `data-*` attribute selectors so the JSX stays declarative.
 *
 * Why a dedicated section rather than utility-class composition: the
 * wizard's chrome reads as one coherent surface — the recipe tiles,
 * step indicator strip, preview pane, and modal frame are all SHAPED
 * by the wizard's UI-SPEC, not by ad-hoc placement of generic
 * utilities. Naming uses `manifold-wizard-*` so they scan as a
 * family in `git grep` and a future plan could swap the wizard's
 * visual posture by editing this block only.
 *
 * The 5 named Step components stay at module scope per
 * `project_manifold_wizard_step_components` — only their className
 * attributes changed; component identity is preserved across the
 * migration (Task 1 + Task 2 name-exact grep verification).
 * ========================================================================= */

/* ── Step-section header trio ───────────────────────────────────────
 * The "Step N of 5 / Big title / Small subtitle" header that opens
 * every step. The three classes always compose in this order on
 * three sibling <div>s — splitting keeps each line's typography
 * tunable without forcing all three to move together. */

.manifold-wizard-sublabel {
  /* Mirrors the `styles.subLabel(t)` shape — small uppercase mono
     label that styles.subLabel produces. Pre-migration this object
     was spread via `{...sub, ...}` into ~25 inline-style sites; the
     class composes on a className attribute instead so utility-class
     overrides above (mt-text-accent etc.) can flow without inline
     specificity wins. */
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: var(--mt-weight-bold);
  color: var(--mt-muted);
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

.manifold-wizard-step-tag {
  /* "Step N of 5" tag — same base shape as .manifold-wizard-sublabel
     but with the accent color override baked in (replaces the
     `{...sub, color:'var(--mt-accent)'}` repetition). Sits at the
     top of every step's header trio. */
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: var(--mt-weight-bold);
  color: var(--mt-accent);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin-bottom: 4px;
}

.manifold-wizard-step-title {
  /* Big display-serif step title — "What are you building?",
     "Pick the data", "Where it lives", "Tune the basics", "Review".
     One shape across all 5 steps. */
  font-family: var(--m-font-display);
  font-size: 18px;
  color: var(--mt-fg);
  letter-spacing: 0.04em;
}

.manifold-wizard-step-subtitle {
  /* Body paragraph under the step title — explains what this step
     does in plain language. Reads loosely (lineHeight 1.5) and in
     the dim foreground tone. */
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-medium);
  color: var(--mt-dim);
  letter-spacing: 0.04em;
  margin-top: 6px;
  text-transform: none;
  line-height: 1.5;
}

.manifold-wizard-step-summary {
  /* "Looking at: <recipe>" affordance under the recipe grid in Step 1.
     Same shape as the subtitle but with accent color (the user just
     picked a recipe — read it back). */
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-medium);
  color: var(--mt-accent);
  letter-spacing: 0.04em;
  margin-top: 4px;
  text-transform: none;
  line-height: 1.5;
}

/* ── Step container + standard layout primitives ────────────────────
 * The outer flex column every step body wraps itself in. Same
 * padding / gap across StepRecipe / StepPlacement / StepTune /
 * StepReview (StepData uses a slightly different grid shape — see
 * .manifold-wizard-stepdata-container below). */

.manifold-wizard-step-container {
  padding: 16px 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.manifold-wizard-stepdata-container {
  /* StepData uses a 2-row grid (top = description; bottom = picker)
     so the LeafPicker fills the remaining height. The 24px-top
     padding flavor (for the fallback when CB.LeafPicker hasn't
     loaded yet) reads differently — see .manifold-wizard-stepdata-fallback. */
  padding: 16px 22px 0 22px;
  display: grid;
  grid-template-rows: auto 1fr;
  min-height: 0;
  gap: 10px;
  height: 100%;
}

.manifold-wizard-stepdata-fallback {
  /* LeafPicker-not-loaded fallback — shows "try a hard refresh."
     Distinct padding from the main StepData container so the
     missing-Picker state reads as a quiet column rather than the
     full picker frame. */
  padding: 24px 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.manifold-wizard-stepdata-picker-frame {
  /* The frame around the LeafPicker — hairline border, hidden
     overflow, sits at the bottom of the StepData grid. */
  min-height: 0;
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.manifold-wizard-parameter-stack {
  /* The parameter-picker stack that appears under each parameterized
     leaf inside StepData (Plan 10-02 inline parameters). Indented 12px
     so it reads as nested under the picker. */
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 6px;
  margin-left: 12px;
}

/* ── Recipe tile grid (Step 1) ──────────────────────────────────────
 * The recipe-picker tile grid + per-tile button + per-tile typography.
 * Selected state flows through `data-selected="true"` so the JSX
 * doesn't have to ternary the className. The selected/unselected
 * variants of bg and border use color-mix on --mt-accent so they
 * re-skin with theme switches automatically. */

.manifold-wizard-recipe-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  margin-top: 6px;
}

.manifold-wizard-recipe-tile {
  padding: 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
  text-align: left;
  cursor: pointer;
  background: var(--mt-bg-deep);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  transition: background 120ms linear, border-color 120ms linear;
}
.manifold-wizard-recipe-tile[data-selected="true"] {
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
  border-color: color-mix(in srgb, var(--mt-accent) 60%, transparent);
}

.manifold-wizard-recipe-glyph {
  font-family: var(--m-font-mono);
  font-size: 18px;
  color: var(--mt-dim);
}
.manifold-wizard-recipe-tile[data-selected="true"] .manifold-wizard-recipe-glyph {
  color: var(--mt-accent);
}

.manifold-wizard-recipe-label {
  font-family: var(--m-font-display);
  font-size: 13px;
  font-weight: var(--mt-weight-semibold);
  color: var(--mt-dim);
  letter-spacing: 0.02em;
}
.manifold-wizard-recipe-tile[data-selected="true"] .manifold-wizard-recipe-label {
  color: var(--mt-fg);
}

.manifold-wizard-recipe-hint {
  font-family: var(--m-font-mono);
  font-size: 10px;
  line-height: 1.5;
  color: var(--mt-muted);
}

.manifold-wizard-recipe-bold {
  /* Inline-emphasis pattern inside the "Looking at: X — hint" summary. */
  color: var(--mt-fg);
}

/* ── Placement step (Step 3) ────────────────────────────────────────
 * One tab list and its "Tab" sublabel. Ticket #10 removed the second
 * column (a section picker with one option) and the "grid cells" size
 * stepper, so the two-column wrapper went with them — the list is the
 * whole step now and takes the full width. */

.manifold-wizard-placement-column {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.manifold-wizard-placement-column-label {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: var(--mt-weight-bold);
  color: var(--mt-muted);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin-bottom: 2px;
}

.manifold-wizard-list-pane {
  /* Scrollable list of selectable rows. StepPlacement's tab list is the
     only user left after ticket #10. */
  display: flex;
  flex-direction: column;
  gap: 4px;
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  padding: 6px;
  background: var(--mt-bg-deep);
  max-height: 240px;
  overflow-y: auto;
}

.manifold-wizard-list-empty {
  /* Empty-state row inside .manifold-wizard-list-pane — "No tabs yet". */
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-medium);
  color: var(--mt-muted);
  letter-spacing: 0.04em;
  text-transform: none;
  padding: 8px 10px;
}

.manifold-wizard-list-row-flex {
  /* `flex:1` middle slot inside a list-row button so the trailing
     id/count chip pushes to the right. */
  flex: 1;
}

.manifold-wizard-list-row-tag {
  /* The small uppercase id / card-count chip after the row label. */
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: var(--mt-weight-bold);
  color: var(--mt-muted);
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

/* ── Tune step (Step 4) ─────────────────────────────────────────────
 * Field column + per-field label/hint pair. */

.manifold-wizard-form-column {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 480px;
}

.manifold-wizard-field-hint {
  /* The grey hint paragraph under each input — "Used in the card
     library and the card's frame header". 10px (below the smallest
     mt-text-xs utility-class size) is intentional; reads as
     incidental annotation. */
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-weight: var(--mt-weight-medium);
  color: var(--mt-muted);
  letter-spacing: 0.04em;
  margin-top: 4px;
  text-transform: none;
  line-height: 1.5;
}

.manifold-wizard-field-input-fallback {
  /* Fallback for styles.fieldInput when CardBuilder hasn't published
     its shared input style — production load always wins via the
     spread before this fallback is evaluated, but the test harness /
     partial-load case needs this. */
  width: 100%;
  padding: 5px 7px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
}

.manifold-wizard-field-label {
  /* Per-field "Card name" / "Widget label" header above each input. */
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: var(--mt-weight-bold);
  color: var(--mt-muted);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin-bottom: 4px;
}

/* ── Review step (Step 5) ───────────────────────────────────────────
 * The two-column summary grid that reads back every choice. */

.manifold-wizard-review-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 18px;
  padding: 14px 16px;
  background: var(--mt-bg-deep);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
}

.manifold-wizard-review-key {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: var(--mt-weight-bold);
  color: var(--mt-muted);
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

.manifold-wizard-review-value {
  font-family: var(--m-font-mono);
  font-size: 12px;
  color: var(--mt-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Preview pane chrome ────────────────────────────────────────────
 * The right-side pane that follows the user through all 5 steps.
 * Outer pane + chrome (title + step indicator + separator) + body
 * variants (recipe-hint card, data preview, in-progress card render,
 * placeholder). */

.manifold-wizard-preview-pane {
  /* Pre-Plan-07 the className was set but inline `style={{width, height,
     minHeight, display, ...}}` overrode it; now the class carries the
     full primitive so it can re-skin with theme switches. */
  width: 100%;
  height: 100%;
  min-height: 280px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px 14px;
  background: var(--mt-bg);
  box-sizing: border-box;
}

.manifold-wizard-preview-chrome {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--mt-border);
}

.manifold-wizard-preview-chrome-title {
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.4em;
  color: var(--mt-fg);
  text-transform: uppercase;
}

.manifold-wizard-preview-chrome-step {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  font-variant-numeric: tabular-nums;
}

.manifold-wizard-preview-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.manifold-wizard-preview-recipe-wrap {
  /* Step 1 — recipe pane body: pushes the recipe-hint card to the
     bottom of the pane (top half intentionally empty per UI-SPEC). */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  flex: 1;
}

.manifold-wizard-preview-recipe-card {
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  padding: 14px;
}

.manifold-wizard-preview-recipe-hint {
  font-family: var(--m-font-display);
  font-size: 12px;
  color: var(--mt-fg);
  line-height: 1.5;
}

.manifold-wizard-hint-placeholder {
  /* Italic muted placeholder text — "Pick a recipe to see what it builds",
     "Hover a leaf to preview", "Preview will build as you fill fields". */
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-style: italic;
  color: var(--mt-muted);
  opacity: 0.7;
}

.manifold-wizard-hint-placeholder-centered {
  /* Same as .manifold-wizard-hint-placeholder but center-aligned and
     loosely-leaded for the larger preview frames where the hint sits
     centered in a 160px box. */
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-style: italic;
  color: var(--mt-muted);
  opacity: 0.7;
  text-align: center;
  line-height: 1.6;
}

.manifold-wizard-preview-frame {
  /* 160px fixed-height preview region used by both WizardStepDataBody
     and WizardStepDataBodyDataFirst — the live renderer-preview box
     that sits above the metadata block / candidate list. */
  height: 160px;
  flex-shrink: 0;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
}

.manifold-wizard-preview-frame-fill {
  /* The inner full-bleed wrapper the RendererComponent sits in. */
  width: 100%;
  height: 100%;
}

.manifold-wizard-preview-card-frame {
  /* Steps 3-4: card-level preview. A larger frame than the data-step
     preview because it renders the entire in-progress card via
     CardRenderer. */
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  min-height: 160px;
}

.manifold-wizard-preview-card-frame-dashed {
  /* Step 5 (review) variant — same primitive but dashed border to
     read as "preview, not final". */
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  background: var(--mt-bg-elev);
  border: 1px dashed var(--mt-border);
  border-radius: 4px;
  min-height: 160px;
}

.manifold-wizard-preview-card-scale {
  /* Plan 49-03 WIZ-03 — outer wrapper that scales the true-size preview
     to fit the pane. The scale factor comes from --mw-preview-scale (set
     inline by WizardPreviewPane = min(paneWidth / cardWidth, 1), so a card
     narrower than the pane stays at true 1:1 size and is never blown up).
     transform-origin: top center keeps the scaled box hanging tidily from
     the top of the frame's centered column. */
  transform: scale(var(--mw-preview-scale, 1));
  transform-origin: top center;
}

.manifold-wizard-preview-card-fill {
  /* Plan 49-03 WIZ-03 — the inner box CardRenderer sits in is now sized to
     the card's REAL per-renderer default canvas box (the same {minW,minH}
     rendererMinPx hands the canvas placement in Plan 02), driven by the
     inline --mw-preview-w / --mw-preview-h custom properties. This replaces
     the old width:100% stretch so "what you see is what lands": the preview
     is the real card at landed size, scaled to fit by the -scale wrapper. */
  width: var(--mw-preview-w, 100%);
  height: var(--mw-preview-h, auto);
}

.manifold-wizard-data-body {
  /* StepDataBody pane — vertical stack of preview frame + metadata. */
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  flex: 1;
}

.manifold-wizard-data-meta {
  /* Three-line metadata block under the preview frame: display name,
     kind · unit, timeago · health. Lives in mono font. */
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: var(--m-font-mono);
}

.manifold-wizard-data-meta-name {
  font-size: 12px;
  color: var(--mt-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manifold-wizard-data-meta-kind {
  font-size: 10px;
  color: var(--mt-dim);
}

.manifold-wizard-data-meta-time {
  font-size: 10px;
  color: var(--mt-muted);
  font-variant-numeric: tabular-nums;
}

/* ── Wizard Columns summary (Phase 36 Plan 04) ──────────────────────
 * Two-line read-only block that sits between the preview frame and the
 * metadata block on Step 2 (Data) of the wizard. Only renders for the
 * `list` renderer when the leaf has item_shape.fields. Summary text is
 * dim (informational), helper line is muted (one shade dimmer — meta-
 * instruction, not data). Sits in the parent's existing 12px gap; no
 * margins of its own. */

.manifold-wizard-cols-summary-block {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.manifold-wizard-cols-summary {
  font-size: 12px;
  color: var(--mt-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manifold-wizard-cols-helper {
  font-size: 10px;
  color: var(--mt-muted);
  font-style: italic;
}

/* ── Data-first candidate list (Plan 06-03) ─────────────────────────
 * The ranked compatible-renderers list that lives below the live
 * preview in data-first mode. Each row reads as a stack of two
 * lines (displayName above kind), with a left rail that lights up
 * accent on the selected row and border-strong on the highlight. */

.manifold-wizard-dfcl-empty {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.manifold-wizard-dfcl-empty-title {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: var(--mt-weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mt-fg);
}

.manifold-wizard-dfcl-empty-text {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-dim);
  line-height: 1.4;
  padding: 8px 2px;
}

.manifold-wizard-dfcl {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 0;
  flex: 1;
}

.manifold-wizard-dfcl-header {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: var(--mt-weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mt-fg);
}

.manifold-wizard-dfcl-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--mt-border);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  overflow-y: auto;
  min-height: 0;
  flex: 1 1 auto;
}

.manifold-wizard-dfcl-row {
  /* Each candidate row. The left rail color flows through
     --m-dfcl-rail and the data-selected attribute swaps the row's
     selected vs highlighted vs idle posture. */
  height: 36px;
  padding: 4px 12px;
  background: var(--mt-bg-elev);
  border-left: 2px solid var(--m-dfcl-rail, transparent);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: relative;
  transition: border-color 80ms linear;
}

.manifold-wizard-dfcl-row-name {
  font-family: var(--m-font-sans);
  font-size: 13px;
  font-weight: var(--mt-weight-regular);
  color: var(--mt-fg);
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.manifold-wizard-dfcl-row[data-selected="true"] .manifold-wizard-dfcl-row-name {
  color: var(--mt-accent);
}

.manifold-wizard-dfcl-row-kind {
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-medium);
  color: var(--mt-dim);
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manifold-wizard-dfcl-row-picked {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-accent);
  letter-spacing: 0.06em;
}

/* ── Narrow-viewport pane toggle ────────────────────────────────────
 * The Show preview / Hide preview button that replaces the inline
 * pane on viewports <= 800px. Carries a data-open attribute so the
 * "open" state reads in both the color and the border. */

.manifold-wizard-pane-toggle {
  padding: 5px 12px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  cursor: pointer;
}
.manifold-wizard-pane-toggle[data-open="true"] {
  color: var(--mt-accent);
  border-color: var(--mt-accent);
}

.manifold-wizard-pane-toggle-row {
  padding: 10px 22px 0 22px;
  display: flex;
  justify-content: flex-end;
}

/* ── Modal frame (CardWizardModal) ──────────────────────────────────
 * Outer backdrop + modal card + header + step indicator + body grid +
 * footer + narrow-viewport overlay. The modal-card width is set
 * inline (depends on viewport) — that's the one remaining inline
 * style on the card itself and it's allowed (state-driven width). */

.manifold-wizard-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.72);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.manifold-wizard-modal-card {
  /* `width` stays inline (state-driven: min(96vw, 1280px) vs 1100px
     depending on showPaneInline). Other chrome lives here. */
  max-height: min(92vh, 820px);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--mt-border-strong);
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}

.manifold-wizard-modal-header {
  padding: 12px 18px;
  border-bottom: 1px solid var(--mt-border);
  background: var(--mt-bg-elev);
  display: flex;
  align-items: center;
  gap: 12px;
}

.manifold-wizard-modal-header-tag {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: var(--mt-weight-bold);
  color: var(--mt-accent);
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

.manifold-wizard-modal-header-spacer {
  flex: 1;
}

.manifold-wizard-modal-btn-ghost {
  /* Header buttons — Switch to advanced + Cancel. Falls back to this
     shape when CB.styles.btnGhost isn't published. Production load
     always wins via the spread. */
  padding: 5px 12px;
  font-size: 11px;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 2px;
  cursor: pointer;
}

.manifold-wizard-modal-btn-ghost-wide {
  /* Cancel button — slightly wider padding than Switch to advanced. */
  padding: 5px 14px;
  font-size: 11px;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 2px;
  cursor: pointer;
}

.manifold-wizard-step-strip {
  /* Five-dot step indicator under the header. Grid columns count is
     state-driven (4 in data-first mode, 5 otherwise) — that count
     flows through --m-wizard-step-count. */
  padding: 10px 22px;
  background: var(--mt-bg);
  border-bottom: 1px solid var(--mt-border);
  display: grid;
  grid-template-columns: repeat(var(--m-wizard-step-count, 5), 1fr);
  gap: 8px;
}

.manifold-wizard-step-dot {
  /* Each step-strip entry — the 3px rail above + the step label below. */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.manifold-wizard-step-rail {
  /* The thin colored bar above each step label. data-state controls
     the color: 'active' / 'done' / 'idle'. */
  height: 3px;
  width: 100%;
  background: var(--mt-border);
  transition: background 120ms linear;
}
.manifold-wizard-step-rail[data-state="active"] {
  background: var(--mt-accent);
}
.manifold-wizard-step-rail[data-state="done"] {
  background: color-mix(in srgb, var(--mt-accent) 33%, transparent);
}

.manifold-wizard-step-label {
  /* The "01 · Choose a shape" label under each step rail. data-state
     swaps the color the same way. */
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.manifold-wizard-step-label[data-state="active"] {
  color: var(--mt-accent);
}
.manifold-wizard-step-label[data-state="done"] {
  color: var(--mt-dim);
}

.manifold-wizard-modal-body {
  /* The body grid — step content on the left + persistent preview
     pane on the right. Columns are state-driven (showPaneInline +
     viewport width); set via --m-wizard-pane-col on the element. */
  display: grid;
  grid-template-columns: var(--m-wizard-body-cols, 1fr);
  min-height: 0;
}

.manifold-wizard-modal-body-left {
  overflow: auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.manifold-wizard-modal-body-left[data-pane-inline="true"] {
  border-right: 1px solid var(--mt-border);
}

.manifold-wizard-modal-body-right {
  min-width: 0;
  overflow: hidden;
}

.manifold-wizard-modal-footer {
  padding: 12px 18px;
  border-top: 1px solid var(--mt-border);
  background: var(--mt-bg-elev);
  display: flex;
  align-items: center;
  gap: 10px;
}

.manifold-wizard-modal-footer-spacer {
  flex: 1;
}

.manifold-wizard-modal-footer-actions {
  /* Column wrapping the Next/Create button so the "Pick a renderer
     to continue" hint can sit directly below it without affecting
     the row layout. */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}

.manifold-wizard-modal-footer-hint {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
  letter-spacing: 0.04em;
}

.manifold-wizard-modal-back-btn {
  /* Back button — same ghost shape as the header buttons but with
     a wider padding flavor. */
  padding: 6px 14px;
  font-size: 11px;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 2px;
  cursor: pointer;
}

.manifold-wizard-modal-primary-btn {
  /* Next / Create card button — falls back to this shape when
     CB.styles.btnPrimary isn't published. */
  padding: 6px 18px;
  font-size: 11px;
}

/* ── Narrow-viewport overlay (D-21) ─────────────────────────────────
 * Replaces the inline preview pane with an overlay modal on viewports
 * <= 800px. Sits inside the existing .manifold-modal-overlay /
 * .manifold-modal shells from the legacy modal family so dismiss
 * affordances (ESC, X, backdrop click) all work uniformly. */

/* Plan 19-09: the wizard preview overlay used to layer
 * .manifold-modal-overlay + .manifold-modal under the custom
 * .manifold-wizard-overlay-card. Phase 19-09 swaps those generic modal
 * classes for a wizard-specific .manifold-wizard-overlay-backdrop so
 * the overlay no longer trips the `manifold-modal-*` namespace
 * cleanup gate. Visual identity is preserved exactly: the backdrop
 * dim, fixed positioning, and centered flex-layout copy verbatim
 * from .manifold-modal-overlay. */
.manifold-wizard-overlay-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(13, 15, 18, 0.7);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--m-pad-xl);
}

.manifold-wizard-overlay-card {
  max-width: 480px;
  width: 94vw;
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  background: var(--mt-bg);
  color: var(--mt-fg);
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--mt-border-strong);
}

.manifold-wizard-overlay-header {
  padding: 8px 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--mt-border);
  background: var(--mt-bg-elev);
}

.manifold-wizard-overlay-header-title {
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.4em;
  color: var(--mt-accent);
  text-transform: uppercase;
}

.manifold-wizard-overlay-close-btn {
  background: transparent;
  color: var(--mt-dim);
  border: none;
  cursor: pointer;
  font-family: var(--m-font-mono);
  font-size: 18px;
  line-height: 1;
  padding: 0 4px;
}

.manifold-wizard-overlay-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
}

/* ── Placeholder fallback (unused in production, kept for safety) ───
 * The "Step N of 5 / title / summary" three-line fallback shown when
 * a step body hasn't been written yet. Not currently rendered (all
 * five real Step components ship) but kept so adding a sixth step is
 * a one-line change. */

.manifold-wizard-placeholder-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px 22px;
}

.manifold-wizard-placeholder-summary {
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-medium);
  color: var(--mt-dim);
  letter-spacing: 0.04em;
  text-transform: none;
  line-height: 1.6;
  padding: 14px 16px;
  border: 1px dashed var(--mt-border);
  border-radius: 6px;
}

/* =========================================================================
 * Accessibility
 * ========================================================================= */

button:focus-visible {
  outline: 2px solid var(--m-accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* --- Widget-level transparent propagation -------------------------------
 * When a header widget has style.transparent set, the cell wrapper
 * already drops its own background+border via resolveChrome. These
 * rules push the same intent down to the widget's *internal* chrome —
 * the tab-bar strip, the auth pill, the gear hover surface — so the
 * whole widget reads as "no fill" rather than just the wrapper. Karl
 * 2026-05-07.
 *
 * !important so themes that re-set the same surfaces with their own
 * higher-specificity rules (`:root[data-theme="X"] .manifold-tabbar`)
 * still get overridden. The toggle is opt-in per widget; nothing
 * changes for widgets that don't carry data-widget-transparent.
 */
[data-widget-transparent="true"] .manifold-tabbar {
  background: transparent !important;
  border-bottom: none !important;
}
[data-widget-transparent="true"] .manifold-tab.active::after {
  /* The accent-bar underline reads as part of the tab label, not the
     bar fill — keep it. (Listed here for documentation; no override.) */
}
[data-widget-transparent="true"] .manifold-h-auth-pill {
  background: transparent !important;
  border-color: transparent !important;
}
[data-widget-transparent="true"] .manifold-h-gear:hover,
[data-widget-transparent="true"] .manifold-h-gear.open {
  background: transparent !important;
}

/* ─────────────────────────────────────────────────────────────────
 * Mobile-tolerance baseline (Karl 2026-05-08).
 *
 * The dashboard's primary modes:
 *   1. Desktop (≥768px): full layout, hover affordances, drag-drop
 *      edit mode all work as designed.
 *   2. Tablet / phone-landscape (480-767px): cards reflow to a
 *      4-col grid (clamped via _effectiveCols in card-builder.jsx).
 *      Tap targets enlarged to ≥36px so finger-precision works.
 *   3. Phone-portrait (<480px): cards reflow to 2-col, tab bar
 *      wraps to multiple rows (all tabs visible), header chrome wraps.
 *
 * Edit-mode mobile (drag-and-drop) is NOT covered here. HTML5 drag
 * events don't fire on touch devices; touch drag-emulation is a
 * larger lift and lives on the follow-ups list. View mode works
 * fully on mobile.
 * ──────────────────────────────────────────────────────────────── */

@media (max-width: 767px) {
  /* Larger tap targets on mid-narrow viewports. The 36px floor is
     the sweet spot between "comfortable to tap" and "doesn't dominate
     the chrome." Apple HIG says 44px; Material says 48dp; 36px is
     the lower-density compromise that keeps the dashboard's
     information-first density without making every button a thumb-
     trap. */
  .manifold-edit-fab {
    min-width: 44px;
    min-height: 44px;
    font-size: 13px;
  }
  .manifold-h-gear {
    min-width: 36px;
    min-height: 36px;
  }
  .manifold-h-auth-pill {
    min-height: 32px;
    padding: 4px 12px;
  }
}

@media (max-width: 479px) {
  /* Phone-portrait: shrink the header strip's horizontal padding
     so the brand + auth + gear all fit on a single 360-400px row.
     Anything wider than that wraps via flex naturally. */
  .manifold-header-strip {
    padding: 0 4px;
  }
  /* Gap D (Phase 98, MOBILE-01): in portrait the tab bar wraps so every
     tab is visible without horizontal scrolling. The desktop scroll
     behavior (overflow-x:auto + flex-shrink:0) lives in the base rules
     and applies everywhere above this breakpoint.
     - flex-wrap:wrap lets rows spill onto as many lines as needed.
     - height:auto replaces the fixed 38px single-row height.
     - overflow-x:visible cancels the horizontal scroll; overflow-y:visible
       lets wrapped rows grow past the old single-row bounding box.
     - The scrollbar rules in the base styles become moot at this width but
       are harmless (scrollbar-width:thin has no effect when there's no
       scroll). */
  .manifold-tabbar {
    flex-wrap: wrap;
    height: auto;
    overflow-x: visible;
    overflow-y: visible;
  }
  /* Let tabs shrink and wrap. flex-shrink:0 is overridden here so tabs
     are no longer forced onto a single row. min-width keeps each tab
     legible (prevents a long label from collapsing to zero). The active
     underline (::after, position:absolute, bottom:-1px) still works
     correctly — each wrapped row is its own flex row with tabs
     positioned relative to themselves. */
  .manifold-tab {
    flex-shrink: 1;
    min-width: 0;
  }
  /* Header tab bar inside the header strip — pad less aggressively in
     portrait so tabs have more usable width on each wrapped row. */
  .manifold-h-tabbar-host .manifold-tabbar {
    padding: 0 4px;
  }
  /* Cards' internal text shouldn't go below 11px even at this
     size — illegibility is worse than a slight horizontal scroll. */
  .manifold-card body, .manifold-card label {
    font-size: 11px;
  }
  /* Gap C (Phase 98, MOBILE-01): at 390px portrait the time-axis tick
     labels on multiSeriesChart/lineChart (absolutely-positioned with
     translateX(-50%) on each gridline) can physically overlap when 3+
     ticks fire in a narrow window. Hide them at this width — the tick
     lines themselves remain visible in the SVG, so the grid still reads
     as a time axis; the individual timestamps are just not legible at
     this density. The hover tooltip still shows the exact time-ago. */
  .manifold-chart-time-label {
    display: none;
  }
  /* Gap C (Phase 98, MOBILE-01): server-temps barChart uses style.horizontal
     with 15 DIMM series. Each row's label+value text inherits the card body's
     14px at desktop; at 390px portrait those 15 rows each get ~17px of
     vertical space (260px / 15 rows, minus gaps), making 14px text nearly
     touch the row above. Reduce to 11px so label+value fit comfortably in
     the compressed rows. Only applies below 480px, so desktop is untouched.

     Phase 227: this was ONE declaration on a shared selector list naming both
     spans. It is now SPLIT into two rules so the two can take different
     variables — the label follows labelSize, the value follows textSize. Left
     as one rule, the value span would have picked up the label variable too,
     making the numbers follow labelSize on a phone and textSize on a desktop.
     Both rules keep the 11px fallback, which is the whole point of the block. */
  .manifold-renderer-rankedbars-label-text {
    font-size: var(--m-renderer-barlabel-label-size, var(--m-renderer-barlabel-size, 11px));
  }
  .manifold-renderer-rankedbars-val {
    font-size: var(--m-renderer-barlabel-size, 11px);
  }
  /* Gap G (Phase 98, MOBILE-01): in portrait the donut/pie content row
     flips from row (dial left, legend right) to column (dial top, legend
     bottom). The dial gets an explicit min-height so it doesn't collapse
     when the row is a column and there's no sibling to define the cross-axis
     height. The legend width cap is dropped (max-width: none) so the legend
     uses the full card width beneath the dial. */
  .manifold-renderer-donut-row {
    flex-direction: column;
    align-items: stretch;
  }
  .manifold-renderer-donut-row > div:first-child {
    /* height:'100%' is set inline (correct for the row layout where the parent
       has a defined flex-box height). In portrait column mode the parent is
       content-height so 100% would collapse; override it explicitly here.
       !important is required to beat the inline style. */
    height: 160px !important;
    min-height: 140px;
    flex: 0 0 auto;
    align-self: center;
  }
  .manifold-renderer-donut-legend {
    max-width: none;
  }
}
/* Phase 125: Compact density mobile floor is enforced in applyDensity() JS,
   not here. CSS self-referencing max() on codegen-owned vars is forbidden
   by the sentinel test and would create a circular reference anyway. The JS
   path in manifold.jsx reads window.innerWidth and applies Math.max() floors
   (xs≥4px, sm≥6px, md≥10px) when the viewport is ≤480px. */

/* Phone-landscape: a landscape phone (e.g. iPhone 14 at 844×390px)
 * has a CSS width of 844px, which is above the 767px max-width band's
 * ceiling. So the phone lands on the full desktop CSS path with no
 * mobile-tolerance adjustments — tap targets stay desktop-sized and
 * section padding stays at the full desktop value. This band catches
 * that case. The height ≤ 500px bound is the discriminator: landscape
 * phones are at most ~430px tall (iPhone 14: 390px, Samsung S23:
 * 360px), while a desktop monitor in landscape is always > 768px tall.
 * So max-height: 500px fires for phones and never for a desktop monitor.
 * This rule is additive — desktop CSS is completely unaffected.
 * (Phase 98, MOBILE-04) */
@media (orientation: landscape) and (max-height: 500px) {
  /* Same tap-target floors as the 480–767px band. */
  .manifold-edit-fab {
    min-width: 44px;
    min-height: 44px;
    font-size: 13px;
  }
  .manifold-h-gear {
    min-width: 36px;
    min-height: 36px;
  }
  .manifold-h-auth-pill {
    min-height: 32px;
    padding: 4px 12px;
  }
}

/* ── ServicePlacardRenderer ────────────────────────────────────────
   Status LED soft pulse for healthy services. Only fires while the
   service is `ok`/`live`; warn uses a faster pulse, fault is solid
   (broken state shouldn't dance). Karl 2026-05-09. */
@keyframes manifold-led-pulse {
  0%, 100% { opacity: 0.7; }
  50%      { opacity: 1.0; }
}
@keyframes manifold-led-pulse-warn {
  0%, 100% { opacity: 0.6; }
  50%      { opacity: 1.0; }
}
/* Plan 07 — small spinner used by the Secrets tab's Test connection
   button while a probe is in flight. 12px ring, transparent top so the
   rotation reads visually. */
@keyframes manifold-spin {
  to { transform: rotate(360deg); }
}

/* =========================================================================
 * Phase 19 Plan 08 — long-tail file escape-hatch classes.
 *
 * The plan closes the migration tail across seven files: plugin-config.jsx,
 * logs-tab.jsx, header-strip.jsx, card-bundle.jsx, catalog-reorg-modal.jsx,
 * shared.jsx, health-panel.jsx. After this block lands, every JSX file in
 * core/dashboard/ has been swept and the file-scoped audit gate is clean
 * for all 11 (4 primitives + 19-04..07 four heavy files + this plan's 7).
 *
 * Naming conventions:
 *   .manifold-pc-*  → plugin-config.jsx (Plugins gear modal + config form)
 *   .manifold-logs-*→ logs-tab.jsx (Logs tab — chip bar, rows, feed)
 *   .manifold-hdr-* → header-strip.jsx (header chrome — assets picker,
 *                     auth popover, errors banner; manifold-h-* family
 *                     stays as-is per the 19-08 plan body — that disposition
 *                     is 19-09's job)
 *   .manifold-bundle-warn / -info / -tag — card-bundle.jsx amber + blue
 *                     attention-bars; the "name in use" tag pill
 *   .manifold-catreorg-* → catalog-reorg-modal.jsx body/list/secondary
 *   .manifold-shared-* → shared.jsx primitives (HBar, Dot)
 *   .manifold-cfg-*  → config-hub.jsx (Gear → System → Configuration
 *                     status list, CONFIG-01 — Phase 206 Plan 02)
 *
 * Authoring rules carry over from 19-04..07: classes bundle visual
 * primitives that always co-occur; runtime values (HBar color/bg, dot
 * status color) ride CSS custom properties so the JSX stays declarative
 * and inline style sites drop to zero on the audit.
 * ========================================================================= */

/* ── plugin-config.jsx — Plugins gear modal + per-plugin config form ──── */

/* The center-aligned "empty / placeholder" body block ("No plugins
   installed.", "This plugin has no configurable settings.", etc.).
   Bundles padding + center alignment + dim opacity into one primitive
   used in three sites. Opacity stays as a property of the class — the
   audit does not gate `opacity`. */
.manifold-pc-empty {
  padding: 24px;
  text-align: center;
  opacity: 0.6;
}
/* Variant used inside the Secrets-tab "owner only" guard + the
   "this plugin declares no secrets" placeholder — same center+dim
   shape but with the monospace small-text caption styling baked in. */
.manifold-pc-empty-mono {
  padding: 24px;
  text-align: center;
  opacity: 0.6;
  font-family: var(--m-font-mono);
  font-size: 12px;
}

/* The plugin-list <ul> reset — flat list, no bullets. Used in both the
   PluginListModal body and the secrets tab. */
.manifold-pc-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Each plugin tile button in PluginListModal — full-width row,
   transparent surface, soft hover tint, baseline-aligned label + id +
   version columns. Hover background swap stays declarative via :hover
   instead of the pre-migration onMouseEnter / onMouseLeave imperative
   inline-style mutation. */
.manifold-pc-list-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  text-align: left;
  background: transparent;
  border: 1px solid var(--m-border, var(--mt-border, #444));
  border-radius: 6px;
  color: inherit;
  font: inherit;
  margin-bottom: 6px;
  cursor: pointer;
  transition: background 120ms ease-out;
}
.manifold-pc-list-row:hover {
  background: color-mix(in srgb, var(--mt-fg, #fff) 4%, transparent);
}
/* Plugin name (sans, semibold, sized as default body text). */
.manifold-pc-list-name {
  font-family: var(--m-font-sans);
  font-weight: var(--mt-weight-semibold);
  font-size: 14px;
  flex: 1;
}
/* Plugin id (smaller mono, dim). */
.manifold-pc-list-id {
  font-family: var(--m-font-mono);
  font-size: 10px;
  opacity: 0.7;
}
/* Plugin version (smaller mono, dimmer). */
.manifold-pc-list-ver {
  font-family: var(--m-font-mono);
  font-size: 10px;
  opacity: 0.5;
}

/* PluginConfigModal back arrow inside the header — borderless,
   inherit-font, slight padding so the arrow has hit area. */
.manifold-pc-back-arrow {
  background: transparent;
  border: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  padding: 0 6px;
  margin-right: 6px;
  opacity: 0.6;
}
/* Plugin-id chip next to the name in the modal header (small mono). */
.manifold-pc-header-id {
  font-family: var(--m-font-mono);
  font-size: 12px;
  opacity: 0.6;
  font-weight: var(--mt-weight-regular);
}

/* Tab strip below the modal header — sits along the bottom border
   between the header and the body, holds the two tabs (Config / Secrets). */
.manifold-pc-tabstrip {
  display: flex;
  gap: 0;
  padding: 0 16px;
  border-bottom: 1px solid var(--m-border, var(--mt-border, #444));
}
/* Individual tab button. Selected state via data-selected attribute
   selector instead of a JSX ternary that baked the accent color into
   border-bottom + color per render. */
.manifold-pc-tabbtn {
  background: transparent;
  border: none;
  padding: 10px 14px;
  cursor: pointer;
  color: inherit;
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.manifold-pc-tabbtn[data-selected="true"] {
  color: var(--m-accent, var(--mt-accent, #4af));
  border-bottom: 2px solid var(--m-accent, var(--mt-accent, #4af));
}

/* The small mono caption block at the top of the Config tab body ("Saved
   values write to <code>…</code>"). Same shape reused at the top of the
   Secrets tab body. */
.manifold-pc-caption {
  font-size: 11px;
  opacity: 0.6;
  margin-bottom: 14px;
  font-family: var(--m-font-mono);
}

/* Reload-status banner shown after a successful save+restart. Background
   + border-color depend on whether the systemd restart succeeded — the
   green vs amber distinction rides --m-pc-banner-bg / --m-pc-banner-border
   custom properties so the class itself is shape-only. */
.manifold-pc-reload-status {
  margin-top: 14px;
  padding: 8px 12px;
  border-radius: 4px;
  background: var(--m-pc-banner-bg, color-mix(in srgb, var(--mt-live, #50c878) 8%, transparent));
  border: 1px solid var(--m-pc-banner-border, color-mix(in srgb, var(--mt-live, #50c878) 40%, transparent));
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: inherit;
}
/* error-line variant uses just the top-margin tweak; the underlying
   .manifold-error-line carries the color. */
.manifold-pc-error-line-spaced {
  margin-top: 14px;
}

/* ── InstanceSetEditor — multi-row Instances editor inside Secrets tab ── */

/* Outer wrapper — small top gap separating instances from the password
   rows above. */
.manifold-pc-instances {
  margin-top: 16px;
}
/* Header row above the instance grid — "Instances (description)" on left,
   "+ Add instance" button on the right. */
.manifold-pc-instances-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
/* Header title (small uppercase mono label). */
.manifold-pc-instances-title {
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.7;
}
/* Subtle field-key hint after the title. */
.manifold-pc-instances-hint {
  margin-left: 8px;
  font-weight: var(--mt-weight-regular);
  opacity: 0.6;
}
/* "+ Add instance" + "Remove" small buttons share size + padding. */
.manifold-pc-btn-sm {
  font-size: 11px;
  padding: 3px 10px;
}
.manifold-pc-btn-xs {
  font-size: 10px;
  padding: 2px 8px;
}
/* Remove-instance button gets error-color border + text. */
.manifold-pc-btn-remove {
  color: var(--m-error, var(--mt-error, #f55));
  border-color: var(--m-error, var(--mt-error, #f55));
}
/* Empty placeholder when no instances configured. */
.manifold-pc-instances-empty {
  font-family: var(--m-font-mono);
  font-size: 11px;
  opacity: 0.5;
  padding: 10px 0;
}
/* Each instance card — bordered tile holding the field grid. */
.manifold-pc-instance-card {
  border: 1px solid var(--m-border, var(--mt-border, #444));
  border-radius: 4px;
  padding: 10px 12px;
  margin-bottom: 8px;
}
/* Header row inside each instance card — "Instance N" label + Remove btn. */
.manifold-pc-instance-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
/* "Instance N" small mono label. */
.manifold-pc-instance-label {
  font-family: var(--m-font-mono);
  font-size: 10px;
  opacity: 0.5;
}
/* Field-key uppercase label inside the instance grid. */
.manifold-pc-instance-field-label {
  display: block;
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-weight: var(--mt-weight-bold);
  margin-bottom: 3px;
  opacity: 0.7;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
/* "(opt)" suffix marker after a field key. */
.manifold-pc-opt-marker {
  margin-left: 4px;
  font-weight: var(--mt-weight-regular);
  opacity: 0.5;
}

/* ── Secrets tab — password row + Test connection button + result banner ─ */

/* One row per secret. Border-bottom hairline separates rows. */
.manifold-pc-secret-row {
  border-bottom: 1px solid var(--m-border, var(--mt-border, #444));
  padding: var(--m-pad-sm, 8px) var(--m-pad-md, 12px);
}
/* Inner flex row holding label, input, button. */
.manifold-pc-secret-row-inner {
  display: flex;
  align-items: center;
  gap: var(--m-pad-md, 12px);
}
/* Secret label (the env-var KEY, slightly larger). */
.manifold-pc-secret-key {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: var(--mt-weight-bold);
  letter-spacing: 0.02em;
}
/* "optional" marker after the key. */
.manifold-pc-secret-opt {
  margin-left: 8px;
  font-weight: var(--mt-weight-regular);
  opacity: 0.5;
  font-size: 10px;
}
/* Description / source caption under the key. */
.manifold-pc-secret-desc {
  font-family: var(--m-font-mono);
  font-size: 10px;
  opacity: 0.6;
  margin-top: 2px;
}
/* Test-connection button background tints by status via
   --m-pc-test-bg custom prop. Idle = transparent; ok = accent-tinted;
   fail = error-tinted. */
.manifold-pc-test-btn {
  flex: 0 0 auto;
  background: var(--m-pc-test-bg, transparent);
  min-width: 132px;
  transition: background 180ms ease-out;
}
/* Inline spinner ring inside the test button while a probe is in flight. */
.manifold-pc-test-spinner {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 6px;
  vertical-align: -1px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: manifold-spin 800ms linear infinite;
}
/* Inline test-connection result banner under the row — colored left
   stripe + tinted background by --m-pc-result-bg / --m-pc-result-border. */
.manifold-pc-secret-result {
  margin-top: 8px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  padding: 6px 10px;
  border-radius: 4px;
  background: var(--m-pc-result-bg, transparent);
  border-left: var(--m-pc-result-border, 3px solid transparent);
  color: var(--m-text, inherit);
}

/* ── ConfigField — per-field label/hint/error rows in the Config tab ──── */

.manifold-pc-field-label {
  display: block;
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-bold);
  margin-bottom: 4px;
  margin-top: 14px;
  letter-spacing: 0.02em;
}
.manifold-pc-field-key {
  font-weight: var(--mt-weight-bold);
}
.manifold-pc-field-type {
  margin-left: 8px;
  font-weight: var(--mt-weight-regular);
  opacity: 0.5;
  text-transform: lowercase;
  font-size: 10px;
}
.manifold-pc-field-hint {
  font-family: var(--m-font-mono);
  font-size: 10px;
  opacity: 0.6;
  margin-top: 4px;
  margin-bottom: 0;
}
.manifold-pc-field-err {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--m-error, var(--mt-error, #ff5050));
  margin-top: 4px;
}
/* Boolean-field "enabled/disabled" label gets the body font size. */
.manifold-pc-bool-state {
  font-size: 12px;
}

/* ── config-hub.jsx — Gear → System → Configuration status list (CONFIG-01) ──
 * Reuses .manifold-pc-list / .manifold-pc-empty / .manifold-pc-btn-sm as-is;
 * only the row composition (icon + name/sub + status tag + button) and the
 * status tag's green/amber tint are new, and the tint reuses the same
 * --mt-live / --mt-warn pairing already established by
 * .manifold-pc-reload-status above. */

/* One row per installed app. Not a button itself (unlike
   .manifold-pc-list-row) — only the trailing Edit/Set up button is
   interactive. */
.manifold-cfg-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--m-border, var(--mt-border, #444));
  border-radius: 6px;
  margin-bottom: 6px;
}
/* Initials badge — small mono square, derived client-side from the
   plugin's display name (no manifest field carries a dedicated icon). */
.manifold-cfg-ico {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  background: color-mix(in srgb, var(--mt-fg, #fff) 6%, transparent);
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: var(--mt-weight-bold);
  opacity: 0.8;
}
.manifold-cfg-grow {
  flex: 1;
  min-width: 0;
}
.manifold-cfg-name {
  font-family: var(--m-font-sans);
  font-weight: var(--mt-weight-semibold);
  font-size: 14px;
}
.manifold-cfg-sub {
  font-family: var(--m-font-mono);
  font-size: 10px;
  opacity: 0.6;
  margin-top: 2px;
}
/* Status tag pill — data-status selector mirrors the tabbtn
   data-selected convention. Green tint (--mt-live) when connected,
   amber (--mt-warn) when needs setup — same token pairing the
   reload-status banner already uses, no new palette. */
.manifold-cfg-tag {
  flex: 0 0 auto;
  padding: 3px 10px;
  border-radius: 999px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  white-space: nowrap;
}
.manifold-cfg-tag[data-status="ok"] {
  background: color-mix(in srgb, var(--mt-live, #6e9b65) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-live, #6e9b65) 40%, transparent);
  color: var(--mt-live, #6e9b65);
}
.manifold-cfg-tag[data-status="needs"] {
  background: color-mix(in srgb, var(--mt-warn, #d2945e) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-warn, #d2945e) 40%, transparent);
  color: var(--mt-warn, #d2945e);
}

/* ── logs-tab.jsx — Logs tab chrome (chip bar, rows, feed) ────────────── */

/* Outer tab container — full-height column with rounded border. */
.manifold-logs-tab {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 140px);
  min-height: 400px;
  background: var(--mt-bg, #0f1418);
  border: 1px solid var(--mt-border, #2a2f36);
  border-radius: 6px;
  overflow: hidden;
}
/* Owner-only banner shown when /api/v0/logs/sources returns 403. */
.manifold-logs-auth-banner {
  padding: 32px;
  text-align: center;
  color: var(--mt-muted, #94a3b8);
  font-family: var(--m-font-mono);
  font-size: 13px;
}
/* Chip bar — sticky toolbar at the top, holds source chips + level + time + search. */
.manifold-logs-chipbar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--mt-bg-elev, #0f1418);
  border-bottom: 1px solid var(--mt-border, #2a2f36);
  padding: 10px 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  font-family: var(--m-font-mono);
}
/* The chip group flexes to fill remaining space. */
.manifold-logs-chipgroup {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  flex: 1;
  min-width: 0;
}
/* "loading sources…" italic placeholder. */
.manifold-logs-chipgroup-loading {
  color: var(--mt-muted, #64748b);
  font-size: 11px;
  font-style: italic;
}
/* Each source chip — pill-shaped button. Background + border + text
   color depend on `on` state and per-source color (FNV hash). Both
   move onto --m-logs-chip-color (the source's hue) + a data-on=true|false
   attribute selector. */
.manifold-logs-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 1px solid var(--mt-border, #2a2f36);
  border-radius: 999px;
  padding: 3px 10px;
  cursor: pointer;
  user-select: none;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-muted, #94a3b8);
}
.manifold-logs-chip[data-on="true"] {
  background: color-mix(in srgb, var(--m-logs-chip-color, var(--mt-accent)) 13%, transparent);
  border-color: var(--m-logs-chip-color, var(--mt-accent));
  color: var(--mt-fg, #e5e7eb);
}
/* A source the backend couldn't read this poll — amber badge. Reads the
   shared --mt-warn token so all four themes restyle for free. The amber
   dashed border + warm dot override the source's own hue so a broken source
   reads as a warning, not just another colored chip. Distinct from the
   neutral empty state (.manifold-logs-feed-empty), which carries no warm hue. */
.manifold-logs-chip[data-unreachable="true"] {
  border-color: var(--mt-warn, #d2945e);
  border-style: dashed;
  background: color-mix(in srgb, var(--mt-warn, #d2945e) 12%, transparent);
  color: var(--mt-warn, #d2945e);
}
.manifold-logs-chip[data-unreachable="true"] .manifold-logs-chip-dot {
  background: var(--mt-warn, #d2945e);
}
/* The little color dot inside each chip. */
.manifold-logs-chip-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--m-logs-chip-color, var(--mt-accent));
  flex-shrink: 0;
}
/* "cls" classifier marker after a chip's label. */
.manifold-logs-chip-cls {
  opacity: 0.6;
  font-size: 9px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
/* Right group: level + time + search + jump-to-live. */
.manifold-logs-rightgroup {
  display: flex;
  gap: 6px;
  align-items: center;
}
/* Shared <select> + <input> chrome (level dropdown, time-window
   dropdown, search box). One class because all three carry the same
   bordered-box-with-mono-text shape. */
.manifold-logs-control {
  background: var(--mt-bg, #0f1418);
  border: 1px solid var(--mt-border, #2a2f36);
  border-radius: 4px;
  padding: 5px 8px;
  font-size: 11px;
  color: var(--mt-fg, #e5e7eb);
  font-family: var(--m-font-mono);
  cursor: pointer;
}
/* Search box specifically fills a wider track and uses text-cursor. */
.manifold-logs-search {
  width: 140px;
  cursor: text;
}
/* "▼ Jump to live" sticky button — accent fill, dark text. */
.manifold-logs-jump-btn {
  background: #5eead4;
  color: #0f1418;
  border: none;
  border-radius: 4px;
  padding: 5px 10px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  font-weight: var(--mt-weight-semibold);
  cursor: pointer;
}
/* Polling error banner above the feed. */
.manifold-logs-err {
  padding: 6px 12px;
  background: #450a0a;
  color: #fca5a5;
  font-family: var(--m-font-mono);
  font-size: 11px;
}
/* "couldn't read: <source>" banner — amber, names the broken source(s).
   Distinct from the red polling-error banner (.manifold-logs-err) and from
   the neutral empty state. Uses the shared --mt-warn token so all four
   themes restyle for free. */
.manifold-logs-unreachable {
  padding: 6px 12px;
  background: color-mix(in srgb, var(--mt-warn, #d2945e) 13%, transparent);
  color: var(--mt-warn, #d2945e);
  border-left: 3px solid var(--mt-warn, #d2945e);
  font-family: var(--m-font-mono);
  font-size: 11px;
}
/* The scrollable feed body. */
.manifold-logs-feed {
  flex: 1;
  overflow: auto;
  min-height: 0;
  background: var(--mt-bg, #0f1418);
  font-family: var(--m-font-mono);
}
/* Empty-state placeholder when no lines yet. */
.manifold-logs-feed-empty {
  padding: 24px;
  text-align: center;
  color: var(--mt-muted, #64748b);
  font-size: 12px;
  font-style: italic;
}
/* Each row — left-edge colored stripe by source (--m-logs-row-color),
   small mono text. Expanded row gets a hairline bottom + elevated bg. */
.manifold-logs-row {
  padding: 2px 12px 2px 0;
  border-left: 3px solid var(--m-logs-row-color, var(--mt-border, #2a2f36));
  padding-left: 9px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  line-height: 1.45;
  color: var(--mt-fg, #e5e7eb);
  cursor: pointer;
  background: transparent;
  border-bottom: 1px solid transparent;
}
.manifold-logs-row[data-expanded="true"] {
  background: var(--mt-bg-elev, #1a1f26);
  border-bottom: 1px solid var(--mt-border, #2a2f36);
}
/* Row top-line container (timestamp + level pill + source + host + msg). */
.manifold-logs-row-top {
  display: flex;
  gap: 8px;
  align-items: baseline;
  white-space: nowrap;
}
/* Timestamp — muted + tabular-nums so columns line up. */
.manifold-logs-row-ts {
  color: var(--mt-muted, #64748b);
  font-variant-numeric: tabular-nums;
}
/* Level pill — bg + fg from --m-logs-pill-bg / --m-logs-pill-fg
   (info/warn/err palette in JS). */
.manifold-logs-pill {
  background: var(--m-logs-pill-bg, transparent);
  color: var(--m-logs-pill-fg, inherit);
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 9px;
  font-weight: var(--mt-weight-semibold);
  letter-spacing: 0.5px;
  text-transform: uppercase;
  flex-shrink: 0;
}
/* Source label colored by FNV hash via --m-logs-row-color. */
.manifold-logs-row-source {
  color: var(--m-logs-row-color, var(--mt-fg));
  font-size: 10px;
  opacity: 0.8;
}
/* "@host" suffix. */
.manifold-logs-row-host {
  color: var(--mt-muted, #64748b);
  font-size: 10px;
  opacity: 0.6;
}
/* The actual log message — fills remaining horizontal space, truncates. */
.manifold-logs-row-msg {
  color: var(--mt-fg, #e5e7eb);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Expanded fields grid below the top line. */
.manifold-logs-row-fields {
  margin-top: 4px;
  padding-left: 12px;
  font-size: 10px;
  color: var(--mt-muted, #94a3b8);
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 12px;
  row-gap: 2px;
}
.manifold-logs-row-fkey {
  color: var(--mt-muted, #64748b);
  white-space: nowrap;
}
.manifold-logs-row-fval {
  color: var(--mt-fg, #cbd5e1);
  word-break: break-all;
}

/* ── header-strip.jsx — assets picker hint, auth popover, errors banner ─ */

/* Small "loading…" caption in the gear-icon picker. */
.manifold-hdr-loading-hint {
  opacity: 0.6;
  font-size: 0.85em;
}
/* "current: <name>" caption alongside the refresh button. */
.manifold-hdr-current-asset {
  font-size: 0.85em;
  opacity: 0.7;
  align-self: center;
}
/* Anonymous viewer's Sign-in pill — bordered, transparent, pill shape. */
.manifold-hdr-auth-signin {
  cursor: pointer;
  background: transparent;
  border: 1px solid currentColor;
  color: inherit;
  font: inherit;
  padding: 2px 10px;
  border-radius: 4px;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* The ↪ glyph inside the Sign-in pill. */
.manifold-hdr-auth-icon {
  font-size: 12px;
}
/* Authenticated user pill — borderless small-padding variant. */
.manifold-hdr-auth-pill-btn {
  cursor: pointer;
  background: transparent;
  border: 1px solid currentColor;
  color: inherit;
  font: inherit;
  padding: 2px 8px;
  border-radius: 4px;
}
/* Auth popover floating menu — dropped via React portal, position:fixed
   handled inline (state-driven coordinates). */
.manifold-hdr-auth-popover {
  background: var(--m-bg-elev, var(--mt-bg-elev, #1a1a1a));
  border: 1px solid var(--m-border, var(--mt-border, #444));
  border-radius: 6px;
  padding: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  font-family: var(--m-font-mono);
  font-size: 12px;
  color: var(--m-text, var(--mt-fg, #eee));
}
/* "Signed in as <name> · <role>" caption above the Sign-out button. */
.manifold-hdr-auth-caption {
  padding: 4px 8px 8px;
  opacity: 0.7;
}
/* Sign-out menu item — block button with hover background. */
.manifold-hdr-signout-btn {
  display: block;
  width: 100%;
  text-align: left;
  padding: 6px 8px;
  background: transparent;
  color: inherit;
  font: inherit;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
}
.manifold-hdr-signout-btn:hover {
  background: color-mix(in srgb, var(--mt-fg, #fff) 5%, transparent);
}
/* Errors banner in EditHeaderModal — red-tinted with body title + ul. */
.manifold-hdr-errs {
  margin: 0 14px 12px;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--mt-error, #ff5050) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-error, #ff5050) 40%, transparent);
  border-radius: 4px;
  color: var(--m-error, var(--mt-error, #ff5050));
  font-family: var(--m-font-mono);
  font-size: 12px;
}
.manifold-hdr-errs-title {
  font-weight: var(--mt-weight-bold);
  margin-bottom: 4px;
}
.manifold-hdr-errs-list {
  margin: 0;
  padding-left: 20px;
}
/* CellEditor refresh-row gap. */
.manifold-hdr-refresh-row {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}
/* CellEditor "remove" button pushed to the right of the cell label. */
.manifold-hdr-remove-pushed {
  margin-left: auto;
}

/* ── card-bundle.jsx — collision / select-all bulkrow bars + "in use" tag ─ */

/* Inline caption sitting next to the target <select> ("Cards land in
   your library…"). */
.manifold-bundle-target-hint {
  margin-left: 10px;
  font-size: 11px;
  opacity: 0.7;
}
/* Amber-tinted bulkrow — "N name collision…" callout above the card list. */
.manifold-bundle-warn {
  margin-top: 6px;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--mt-warn, #ffb450) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-warn, #ffb450) 35%, transparent);
  border-radius: 4px;
}
/* Blue-tinted bulkrow — "Importing N of M cards" + Select all / none. */
.manifold-bundle-info {
  margin-top: 6px;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--mt-accent, #50b4ff) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-accent, #50b4ff) 35%, transparent);
  border-radius: 4px;
}
/* Bold caption inside each bulkrow ("N name collisions:" / "Importing…"). */
.manifold-bundle-bulkrow-title {
  font-weight: var(--mt-weight-bold);
}
/* Hint span trailing each bulkrow ("Uncheck cards you don't want…"). */
.manifold-bundle-bulkrow-hint {
  font-size: 11px;
  opacity: 0.7;
  margin-left: 8px;
}
/* Per-card "⚠ name in use" tag — small uppercase mono pill, amber tint. */
.manifold-bundle-nameused-tag {
  margin-left: 8px;
  font-size: 10px;
  font-weight: var(--mt-weight-bold);
  padding: 1px 6px;
  border-radius: 99px;
  background: color-mix(in srgb, var(--mt-warn, #ffb450) 20%, transparent);
  color: var(--mt-warn, #ffb450);
  border: 1px solid color-mix(in srgb, var(--mt-warn, #ffb450) 45%, transparent);
  font-family: var(--m-font-mono);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Card-checkbox in the bundle preview — small gap to the right of the box. */
.manifold-bundle-card-check {
  margin-right: 6px;
}

/* ── catalog-reorg-modal.jsx — informational migration modal body chrome ─ */

/* The body paragraph above the optional list. */
.manifold-catreorg-body {
  font-family: var(--m-font-sans);
  font-size: var(--mt-text-md-size, 14px);
  line-height: 1.5;
  color: var(--m-text, var(--mt-fg));
  padding: var(--m-pad-md, 14px) 0;
}
/* The disc-bullet list of affected cards (visible only when n > 1). */
.manifold-catreorg-list {
  list-style: disc inside;
  margin: 0;
  padding-left: var(--m-pad-md, 14px);
  color: var(--m-text, var(--mt-fg));
  font-size: var(--mt-text-md-size, 14px);
  line-height: 1.6;
}
.manifold-catreorg-list-row {
  padding: 2px 0;
}
/* Secondary muted text after each card title (unresolved leaves). */
.manifold-catreorg-secondary {
  color: var(--m-text-muted, var(--mt-dim, #8b8985));
  font-size: var(--mt-text-sm-size, 12px);
  margin-left: var(--m-pad-sm, 8px);
}
/* "…and N more" trailing tail. */
.manifold-catreorg-more {
  list-style: none;
  color: var(--m-text-muted, var(--mt-dim, #8b8985));
  font-style: italic;
}

/* ── shared.jsx — generic primitives (HBar, Dot) ──────────────────────── */

/* Horizontal-bar track. Background color rides --m-hbar-bg (default uses
   a 6%-white soft fill). Border-radius rides --m-hbar-radius. The
   prop-driven height stays inline (state-driven; not a FORBIDDEN_KEY). */
.manifold-shared-hbar-track {
  background: var(--m-hbar-bg, rgba(255, 255, 255, 0.06));
  border-radius: var(--m-hbar-radius, 0);
  overflow: hidden;
  width: 100%;
}
/* Fill ribbon inside the track — width set inline (state-driven %). */
.manifold-shared-hbar-fill {
  background: var(--m-hbar-color, currentColor);
  height: 100%;
  transition: width 600ms ease;
}
/* Status dot — small filled circle with a glow halo. Fill + shadow color
   ride --m-shared-dot-color (status-keyed in JS: ok=green, warn=amber,
   err=red, idle=gray). */
.manifold-shared-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 8px;
  background: var(--m-shared-dot-color, #888);
  box-shadow: 0 0 6px var(--m-shared-dot-color, #888);
}

/* ── Backup & restore (Phase 56) ─────────────────────────────────────
 * Layout-only chrome for BackupRestoreModal. Sits in the always-loaded
 * default theme.css (the base layer); every value consumes a --m-* /
 * --mt-* token, so the four themes restyle it for free (no hex/px
 * literals, per 56-UI-SPEC). The modal shell, buttons, banner, and
 * Replace/Add chooser reuse the existing .manifold-app-modal-* /
 * .manifold-button / .manifold-banner / .manifold-h-mode-* classes;
 * these rules only add the section spacing + list-row + auth-banner
 * structure that those primitives don't already cover. */
.manifold-backup-body {
  padding: var(--m-pad-lg);
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-lg);
}
.manifold-backup-section {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-sm);
}
.manifold-backup-section-help {
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--m-text-muted);
  line-height: 1.5;
}
.manifold-backup-row {
  display: flex;
  align-items: center;
  gap: var(--m-pad-md);
  flex-wrap: wrap;
}
.manifold-backup-check {
  display: flex;
  align-items: center;
  gap: var(--m-pad-xs);
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--m-text-muted);
  cursor: pointer;
}
.manifold-backup-check input {
  accent-color: var(--m-accent);
  cursor: pointer;
}

/* Success tick + error refusal line. The success tick reuses the health
 * status-dot; the refusal reuses the base .manifold-banner (error red). */
.manifold-backup-notice {
  display: flex;
  align-items: center;
  gap: var(--m-pad-sm);
  font-size: var(--mt-text-sm-size, 12px);
  line-height: 1.5;
}
.manifold-backup-notice.success {
  color: var(--m-text);
}
.manifold-backup-refusal {
  border-radius: var(--m-radius-sm);
  line-height: 1.5;
}

/* Replace / Add-alongside chooser block. */
.manifold-backup-chooser {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-sm);
  margin-top: var(--m-pad-xs);
}
.manifold-backup-chooser-prompt {
  font-size: var(--mt-text-md-size, 14px);
  font-weight: var(--mt-weight-semibold);
  color: var(--m-text);
}
.manifold-backup-chooser-note {
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--m-text-muted);
  line-height: 1.5;
}
.manifold-backup-chooser-actions {
  display: flex;
  gap: var(--m-pad-sm);
  margin-top: var(--m-pad-xs);
}

/* "Saved on the server" list. */
.manifold-backup-list {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-sm);
}
.manifold-backup-list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--m-pad-md);
  padding: var(--m-pad-md);
  background: var(--m-surface-2, var(--m-surface));
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-md);
}
.manifold-backup-list-row-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.manifold-backup-list-row-label {
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--m-text);
  line-height: 1.4;
}
.manifold-backup-list-row-time {
  font-family: var(--m-font-mono);
  font-size: 12px;
  color: var(--m-text-faint);
}
.manifold-backup-empty {
  padding: var(--m-pad-md);
  background: var(--m-surface-2, var(--m-surface));
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-md);
}
.manifold-backup-empty-heading {
  font-size: var(--mt-text-sm-size, 12px);
  font-weight: var(--mt-weight-semibold);
  color: var(--m-text);
  margin-bottom: var(--m-pad-xs);
}
.manifold-backup-empty-body {
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--m-text-muted);
  line-height: 1.5;
}

/* Post-restore "re-enter login" banner (D-05) — built on the warm
 * .manifold-banner.warning base; these rules stack its lines and style
 * the per-card "Re-enter login" accent link. */
.manifold-backup-auth-banner {
  align-items: flex-start;
  border-radius: var(--m-radius-sm);
}
.manifold-backup-auth-banner-body {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-sm);
}
.manifold-backup-auth-banner-heading {
  font-weight: var(--mt-weight-semibold);
}
.manifold-backup-auth-banner-text {
  font-weight: var(--mt-weight-regular);
  line-height: 1.5;
}
.manifold-backup-auth-banner-list {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-xs);
}
.manifold-backup-auth-banner-card {
  font-weight: var(--mt-weight-regular);
  line-height: 1.5;
}
.manifold-backup-auth-banner-card-name {
  font-weight: var(--mt-weight-semibold);
}
.manifold-backup-auth-banner-link {
  background: transparent;
  border: none;
  padding: 0;
  color: var(--m-accent-hi, var(--m-accent));
  font-family: inherit;
  font-size: inherit;
  font-weight: var(--mt-weight-semibold);
  cursor: pointer;
  text-decoration: underline;
}
.manifold-backup-auth-banner-link:hover {
  color: var(--m-accent);
}

/* Phase 74 (ANON-01) D-06 — "Edit public tab" context banner.
 * Phase 140 (THEME-01) — override the warning amber fill with an elevated
 * surface + warm left-border stripe so the banner clears WCAG 4.5:1 on every
 * theme. The warm identity (amber) survives as a border-left accent and as the
 * bold lead-word color (dark themes only — light themes inherit --m-text
 * because --m-warning on a light surface fails contrast). Token-only per D-02:
 * no hex literals. Base .manifold-banner.warning left UNCHANGED. */
.manifold-banner.warning.manifold-edit-public-banner {
  background: var(--mt-bg-elev);
  color: var(--m-text);
  border-left: 3px solid var(--m-warning);
  align-items: flex-start;
  border-radius: var(--m-radius-sm);
}
.manifold-edit-public-banner strong {
  color: var(--m-warning);
}
.manifold-edit-public-banner-text {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-xs);
  line-height: 1.5;
}
.manifold-edit-public-banner-text strong {
  font-weight: var(--mt-weight-semibold);
}
.manifold-edit-public-banner-hint {
  font-weight: var(--mt-weight-regular);
  opacity: 0.85;
}

/* Phase 82 (PUB-HDR-01): public-header controls embedded in the edit banner.
 * The controls group sits inside .manifold-banner-actions alongside "Done
 * editing" — it wraps on narrow viewports but stays flush on desktop.
 * No color literals: everything routes through --mt-* / --m-* tokens so it
 * re-skins with each theme. The picker and buttons reuse the same
 * rgba(255,255,255,0.12) glass style as the rest of the amber banner's buttons. */
.manifold-pub-header-controls {
  display: flex;
  align-items: center;
  gap: var(--m-pad-sm);
  flex-wrap: wrap;
  border-right: 1px solid rgba(255,255,255,0.2);
  padding-right: var(--m-pad-sm);
  margin-right: var(--m-pad-xs);
}
.manifold-pub-header-status {
  font-size: 11px;
  opacity: 0.8;
  white-space: nowrap;
}
.manifold-pub-header-picker {
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.2);
  color: var(--m-text);
  padding: 3px var(--m-pad-sm);
  border-radius: var(--m-radius-sm);
  font-family: var(--m-font-sans);
  font-size: 12px;
  cursor: pointer;
  max-width: 160px;
}
.manifold-pub-header-picker option {
  background: var(--m-surface, #1a1a1a);
  color: var(--m-text);
}
/* The "Start from my brand header" button gets a subtle accent tint to
 * distinguish it as the primary suggested action vs the utilitarian set/clear. */
.manifold-pub-header-brand-btn {
  background: rgba(255,255,255,0.18);
  border-color: rgba(255,255,255,0.3);
}
.manifold-pub-header-brand-btn:hover {
  background: var(--mt-hover-bg, rgba(255,255,255,0.28));
}
/* Disabled "Set as public header" button (nothing picked yet). */
.manifold-pub-header-controls button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* =========================================================================
 * Erratic brand wordmark — theme-token recolor (2026-06-05)
 *
 * The [ERRATIC_DATACENTER] header wordmark is a custom brand widget whose
 * HTML carries its own <style> block with hardcoded teal/grey colors
 * (#4fc1c9 letters, #777/#888 brackets, #d4915e dot). That blob is saved in
 * every user's dashboard state, so it predates — and ignores — the theme
 * token system: the mark stayed teal under every theme, clashing on the
 * non-teal ones (orange Sunset, lime Leafy Green, navy Deep Sea).
 *
 * These rules re-skin the mark through the active theme's tokens WITHOUT
 * editing anyone's saved state. The widget's inline rules are plain
 * `.erratic-mark {...}` (specificity 0,1,0); prefixing with `:root[data-theme]`
 * raises ours to 0,3,0 so we win regardless of DOM order. The chromatic-
 * aberration glitch on `.word` (hardcoded red/cyan keyframes inside the saved
 * blob) is stilled so it doesn't fight the accent. The code default in
 * card-builder-library.jsx (_ERRATIC_BRAND_HTML) is updated to match for
 * fresh installs; this override is what makes existing saved marks re-skin.
 * ========================================================================= */
:root[data-theme] .erratic-mark {
  color: var(--mt-accent);
  text-shadow:
    0 0 10px color-mix(in srgb, var(--mt-accent) 45%, transparent),
    0 0 20px color-mix(in srgb, var(--mt-accent) 15%, transparent);
}
:root[data-theme] .erratic-mark .bracket,
:root[data-theme] .erratic-mark .dc { color: var(--mt-muted); }
:root[data-theme] .erratic-mark .dot { color: var(--mt-warn); }
:root[data-theme] .erratic-mark .cursor { color: var(--mt-accent); }
:root[data-theme] .erratic-mark .word { animation: none; }

/* =========================================================================
 * Build-your-own-theme editor chrome (Phase 72, THEME-04 CRITICAL)
 *
 * WHY a private --te-* palette instead of the usual --mt-* tokens:
 * the editor's live preview paints the user's chosen colors onto
 * documentElement as inline --mt-* custom properties (themes.js
 * setCustomColors). Those vars are GLOBAL — every element reading
 * var(--mt-fg) repaints, INCLUDING any modal chrome that reads them. If the
 * editor's own buttons/inputs/labels read --mt-*, previewing bg:#000 fg:#000
 * would make the editor itself unreadable and the user could not find Cancel.
 *
 * So the editor declares its OWN fixed, theme-INDEPENDENT palette (--te-*)
 * with literal values the preview can never touch, and builds ALL chrome from
 * --te-* (plus structural utilities). The only place a user-chosen color
 * appears is the per-slot preview chip, which carries it via the allowlisted
 * --te-chip custom property (data, not chrome). The --te-panel-fg/--te-panel-bg
 * pair (#e8e6e3 on #15181d) is the default theme's known-legible amber-slate
 * combo — WCAG contrast ~12:1, well above 4.5 — so the panel stays readable
 * under any preview.
 *
 * Recovery if a SAVED theme is unreadable: ?theme=default (resolveActive
 * precedence honors the URL param above a saved active theme). Surfaced in the
 * panel copy.
 * ========================================================================= */
/* Phase 72 follow-up: the theme editor docks to the RIGHT as a non-dimming
 * inspector, NOT a centered modal. A dark scrim would defeat the whole point of
 * a live color preview — you can't judge colors you've just dimmed. So the
 * overlay is transparent and click-through; only the panel catches pointer
 * events, leaving the live dashboard fully bright and visible to the left.
 * Dismiss is ESC / Cancel / close (there is no backdrop to click). */
.manifold-theme-editor-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: transparent;
  pointer-events: none;
  display: flex;
  justify-content: flex-end;
}
.manifold-theme-editor-overlay > .manifold-theme-editor {
  pointer-events: auto;
  position: relative;
  width: 380px;
  max-width: 92vw;
  height: 100vh;
  max-height: 100vh;
  margin: 0;
  border: none;
  border-left: 1px solid var(--te-panel-border);
  border-radius: 0;
  /* Left-edge lift so the panel reads as floating over the live dashboard. */
  box-shadow: -14px 0 48px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: te-slide-in 160ms ease-out;
}
@keyframes te-slide-in {
  from { transform: translateX(18px); opacity: 0.55; }
  to   { transform: translateX(0);    opacity: 1; }
}
/* Header pinned; body scrolls inside the full-height docked panel. */
.manifold-theme-editor-overlay > .manifold-theme-editor > .manifold-theme-editor-body {
  flex: 1 1 auto;
  overflow-y: auto;
}

/* #104: .msp-panel--page is the Style panel wearing this same palette while the
   page reach is open. The declaration is shared rather than copied: the panel's
   shell reads --mt-*, which the live preview repaints, so around a body built
   from --te-* it would go black-on-black in exactly the case this palette
   exists to survive. One selector, seven literals, one place to change them. */
/* #163: .msp-panel--wizard is the same panel wearing the same palette while
   Build a theme is open, and for a sharper version of the same reason — the
   wizard previews the page palette live, on the real page, so a chrome reading
   --mt-* would black itself out at the moment somebody previews a near-black
   ground, taking the Cancel that undoes it with it. */
.manifold-theme-editor,
.msp-panel--page,
.msp-panel--wizard {
  /* Fixed, preview-proof palette — literal values, never the previewable
     theme tokens (see the WHY block above). */
  --te-panel-bg: #15181d;
  --te-panel-bg-elev: #1d2128;
  --te-panel-fg: #e8e6e3;
  --te-panel-dim: #8b8985;
  --te-panel-border: #353c47;
  --te-panel-border-soft: #252a32;
  --te-panel-accent: #f5a623;
  --te-warn: #d2945e;

  font-family: var(--m-font-sans);
  color: var(--te-panel-fg);
}

.manifold-theme-editor-body {
  padding: 12px 18px;
}

.manifold-theme-editor .manifold-theme-editor-intro {
  font-size: 11px;
  color: var(--te-panel-dim);
  line-height: 1.5;
  padding: 0 2px 6px;
}

/* Start-from-theme selector row. */
.manifold-theme-editor-startfrom {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 2px 12px;
  border-bottom: 1px solid var(--te-panel-border-soft);
  margin-bottom: 10px;
}
.manifold-theme-editor-startfrom label {
  font-size: 11px;
  color: var(--te-panel-dim);
  white-space: nowrap;
}
.manifold-theme-editor-startfrom select {
  flex: 1 1 auto;
  background: var(--te-panel-bg-elev);
  color: var(--te-panel-fg);
  border: 1px solid var(--te-panel-border);
  border-radius: 4px;
  padding: 5px 8px;
  font-family: inherit;
  font-size: 12px;
}

/* Section labels (Main / Base / Chart series / Heatmap). */
.manifold-theme-editor-section-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--te-panel-dim);
  margin: 12px 2px 6px;
}

/* A slot row: label + hex input + preview chip + advisory note. */
.manifold-theme-editor-slot {
  display: grid;
  grid-template-columns: 110px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 5px 2px;
}
.manifold-theme-editor-slot-label {
  font-size: 12px;
  color: var(--te-panel-fg);
}
.manifold-theme-editor-slot-advisory {
  grid-column: 1 / -1;
  font-size: 10px;
  color: var(--te-warn);
  padding: 0 2px 2px 120px;
  line-height: 1.4;
}

/* The per-slot color preview chip — the ONE place a user color shows.
 * Color arrives via the allowlisted --te-chip custom property (data carrier),
 * exactly like the ThemeSwatch chips. */
.manifold-theme-editor-chip {
  width: 26px;
  height: 18px;
  border-radius: 3px;
  border: 1px solid var(--te-panel-border);
  background: var(--te-chip, var(--te-panel-bg-elev));
  flex: 0 0 auto;
}

/* The small hex input pair (native picker + text), scoped to --te-*. */
.manifold-theme-editor-hex {
  display: flex;
  align-items: center;
  gap: 6px;
}
.manifold-theme-editor-hex input[type="color"] {
  width: 28px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--te-panel-border);
  border-radius: 4px;
  background: var(--te-panel-bg-elev);
  cursor: pointer;
}
.manifold-theme-editor-hex input[type="text"] {
  width: 96px;
  background: var(--te-panel-bg-elev);
  color: var(--te-panel-fg);
  border: 1px solid var(--te-panel-border);
  border-radius: 4px;
  padding: 4px 6px;
  font-family: var(--m-font-mono);
  font-size: 11px;
}

/* Show-all disclosure toggle. */
.manifold-theme-editor-showall {
  background: transparent;
  border: 1px dashed var(--te-panel-border);
  color: var(--te-panel-dim);
  font-family: inherit;
  font-size: 11px;
  padding: 6px 10px;
  border-radius: 4px;
  cursor: pointer;
  margin: 12px 2px 4px;
}
.manifold-theme-editor-showall:hover {
  border-color: var(--te-panel-accent);
  color: var(--te-panel-fg);
}

/* ── #108: YOUR COLOURS ────────────────────────────────────────────────────
 * The block that names a colour, under the theme's own colours in the page
 * reach. Every value here is a --te-* one: the panel sits in front of a LIVE
 * PREVIEW of the palette being edited, so a control that read --mt-* would go
 * black-on-black in exactly the case this palette exists to survive (THEME-04).
 *
 * The row grid is the slot row's, one column wider — name, value, remove.
 * Deliberately the same rhythm: this list sits directly under "More base
 * colors" and two grids a few pixels apart read as a mistake. */
.manifold-te-own {
  border-top: 1px solid var(--te-panel-border-soft);
  margin-top: 12px;
  padding-top: 4px;
}
.manifold-te-own-note,
.manifold-te-own-empty {
  font-size: 10px;
  color: var(--te-panel-dim);
  line-height: 1.5;
  padding: 0 2px 6px;
}
.manifold-te-own-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 4px 2px;
}
.manifold-te-own-name {
  font-family: var(--m-font-mono);
  font-size: 12px;
  color: var(--te-panel-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.manifold-te-own-remove {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--te-panel-dim);
  font-family: inherit;
  font-size: 14px;
  line-height: 1;
  padding: 3px 6px;
  cursor: pointer;
}
.manifold-te-own-remove:hover {
  border-color: var(--te-panel-border);
  color: var(--te-panel-fg);
}
.manifold-te-own-remove:focus-visible,
.manifold-te-own-input:focus-visible {
  outline: 2px solid var(--te-panel-accent);
  outline-offset: 1px;
}

/* The confirm line. Full width under its row, and it says the count before it
 * offers the button — the number is the reason the question is being asked. */
.manifold-te-own-confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 6px 2px 8px;
}
.manifold-te-own-confirm-msg {
  flex: 1 1 100%;
  font-size: 10px;
  line-height: 1.5;
  color: var(--te-warn);
}

/* The add form: name, value, then the two buttons on their own line — at
 * 300px the three would wrap into a ragged stack. */
.manifold-te-own-add {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 6px 2px 2px;
}
.manifold-te-own-input {
  background: var(--te-panel-bg-elev);
  color: var(--te-panel-fg);
  border: 1px solid var(--te-panel-border);
  border-radius: 4px;
  padding: 5px 8px;
  font-family: var(--m-font-mono);
  font-size: 12px;
  min-width: 0;
}
.manifold-te-own-add-actions {
  grid-column: 1 / -1;
  display: flex;
  gap: 8px;
}
.manifold-te-own-error {
  grid-column: 1 / -1;
  font-size: 10px;
  line-height: 1.5;
  color: var(--te-warn);
}
.manifold-te-own-add-btn {
  display: block;
  margin: 8px 2px 4px;
}

/* The manage view's footer row: Back to editor (left) · Close.
 *
 * The theme editor's own Reset · Cancel · Save left this row in ticket 155 for
 * the commit bar pinned to the bottom of the Style panel. What stays under the
 * Look section's colours is `.manifold-theme-editor-manage`, one button, below. */
.manifold-theme-editor-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 10px;
}
.manifold-theme-editor-footer .te-spacer { flex: 1 1 auto; }
/* What is left of the Look section's own action row once the theme's three
   commit buttons moved to the pinned bar (ticket 155): Manage themes, which
   opens a different screen rather than ending an edit. Same top gap the row
   had, so nothing above it shifted. */
.manifold-theme-editor-manage {
  padding-top: 10px;
}
.manifold-theme-editor-btn {
  background: var(--te-panel-bg-elev);
  color: var(--te-panel-fg);
  border: 1px solid var(--te-panel-border);
  border-radius: 4px;
  font-family: inherit;
  font-size: 12px;
  padding: 6px 14px;
  cursor: pointer;
}
.manifold-theme-editor-btn:hover { border-color: var(--te-panel-accent); }
.manifold-theme-editor-btn.is-primary {
  background: var(--te-panel-accent);
  color: #15181d;
  border-color: var(--te-panel-accent);
  font-weight: var(--mt-weight-semibold);
}
.manifold-theme-editor-btn.is-primary:hover { filter: brightness(1.08); }
/* #108: the destructive one. --te-warn rather than a red, because this palette
   is deliberately seven fixed literals that survive a black-on-black preview,
   and a red only this button used would be an eighth. */
.manifold-theme-editor-btn.is-danger {
  color: var(--te-warn);
  border-color: var(--te-warn);
}
.manifold-theme-editor-btn.is-danger:hover {
  background: var(--te-warn);
  color: #15181d;
}

/* Manage your themes — the two things you can do to the list itself, above it
   (#103). Save first and primary because making a theme is the common case;
   Import second because bringing one in from a file is the rare one. They wrap
   rather than squeeze: the Save label is a sentence, and a narrow modal should
   put Import on its own line instead of clipping either. */
.manifold-te-manage-import-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding-bottom: 10px;
}

/* The ?theme=default escape-hatch recovery line (D-06). */
.manifold-theme-editor-escape {
  font-size: 10px;
  color: var(--te-panel-dim);
  line-height: 1.5;
  padding: 8px 2px 0;
  border-top: 1px solid var(--te-panel-border-soft);
  margin-top: 8px;
}
.manifold-theme-editor-escape code {
  font-family: var(--m-font-mono);
  color: var(--te-warn);
}

/* Inline save-failure error (concurrency reject etc.) — keeps modal open. */
.manifold-theme-editor-error {
  font-size: 11px;
  color: var(--te-warn);
  padding: 6px 2px 0;
}

/* ── Phase 150 revision: Customize panel — three stacked sections ───────────
 *
 * Look / Size / Layout are now labeled sections that all render at once,
 * replacing the previous tab strip. Section headers use the amber accent
 * (--te-panel-accent) so they read as top-level axis labels — a step above
 * the dimmer sub-labels inside the Look body (manifold-theme-editor-section-label
 * is 10px dim; these are 11px amber). A top border on Size and Layout creates
 * clear visual breaks between the three axes without extra chrome.
 *
 * No hardcoded hex — all colors via existing --te-* custom properties.
 * No new tokens introduced.
 * ─────────────────────────────────────────────────────────────────────────── */

.manifold-customize-section {
  padding-bottom: 8px;
}

.manifold-customize-section + .manifold-customize-section {
  border-top: 1px solid var(--te-panel-border);
  padding-top: 14px;
  margin-top: 8px;
}

/* Axis label (LOOK / SIZE / LAYOUT). Amber makes these top-level groupings
   visually distinct from the dimmer sub-labels within the Look body. */
.manifold-customize-section-header {
  font-family: var(--m-font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--te-panel-accent);
  margin-bottom: 10px;
}

/* Layout section explainer. Same voice as the Look intro: small, dim, readable. */
.manifold-customize-layout-explainer {
  font-size: 12px;
  color: var(--te-panel-dim);
  line-height: 1.6;
  margin: 0;
  padding: 2px 2px 8px;
}

/* ── Phase 189-04 FONT-01: flat font-list picker inside ThemeEditorModal ────
 *
 * All classes live under .manifold-theme-editor so the --te-* fixed palette
 * applies. NO hex literals here — all colors via var(--te-*). No new palette,
 * no new type scale — reuses tokens/values already established by this
 * block's retired predecessors (.manifold-te-look-card et al.) and by
 * .cb-toggle-pill / .cb-toggle-pill-row (card-builder-helpers.css:4543-4564),
 * the cited grammar for the role tabs. Literal-px convention preserved
 * (this stylesheet region predates --mt-space-* extraction) — no
 * --mt-space-* introduced here.
 * ────────────────────────────────────────────────────────────────────────── */

/* Section intro below the "Fonts" label. */
.manifold-theme-editor .manifold-te-font-intro {
  font-size: 11px;
  color: var(--te-panel-dim);
  line-height: 1.5;
  padding: 0 2px 8px;
}

/* #101 — the cards that are not wearing the theme's fonts, said where the
 * theme's fonts are chosen. A NOTE, not an alarm: 46 cards holding a typeface of
 * their own is a fact to read, and giving it a warning colour would make the
 * Fonts block shout every time Karl opens it to change a heading.
 *
 * Same 11px --te-panel-dim as the section intro directly above it, so the two
 * read as one paragraph of context before the controls start. The button below
 * it borrows .manifold-te-role-tab's shape exactly — same border, radius, height
 * and hover — because it sits four pixels above a row of them and a second
 * button grammar here would look like a mistake. No new palette, no new type
 * scale; literal-px convention preserved with the rest of this region. */
.manifold-theme-editor .manifold-te-offtheme {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 0 2px 10px;
}

.manifold-theme-editor .manifold-te-offtheme-note {
  font-size: 11px;
  color: var(--te-panel-dim);
  line-height: 1.5;
}

.manifold-theme-editor .manifold-te-offtheme-btn {
  background: transparent;
  color: var(--te-panel-fg);
  border: 1px solid var(--te-panel-border);
  border-radius: 4px;
  padding: 4px 8px;
  font-size: 11px;
  font-family: var(--m-font-sans);
  cursor: pointer;
  transition: background 120ms linear, color 120ms linear, border-color 120ms linear;
}

.manifold-theme-editor .manifold-te-offtheme-btn:hover {
  border-color: var(--te-panel-accent);
}

.manifold-theme-editor .manifold-te-offtheme-btn:focus-visible {
  outline: 2px solid var(--te-panel-accent);
  outline-offset: 1px;
}

/* Role tabs row (Body / Headings / Numbers / Data) — modeled on .cb-toggle-pill-row. */
.manifold-te-role-tabs {
  display: flex;
  gap: 4px;
  padding: 0 2px 10px;
}

/* One role tab — modeled on .cb-toggle-pill, against the --te-panel-* palette. */
.manifold-te-role-tab {
  background: transparent;
  color: var(--te-panel-dim);
  border: 1px solid var(--te-panel-border);
  border-radius: 4px;
  padding: 4px 6px;
  font-size: 11px;
  font-family: var(--m-font-sans);
  cursor: pointer;
  transition: background 120ms linear, color 120ms linear, border-color 120ms linear;
}
.manifold-te-role-tab:hover {
  border-color: var(--te-panel-accent);
  color: var(--te-panel-fg);
}
.manifold-te-role-tab.is-active {
  background: color-mix(in srgb, var(--te-panel-accent) 19%, transparent);
  color: var(--te-panel-accent);
  border-color: var(--te-panel-accent);
  font-weight: var(--mt-weight-semibold);
}
.manifold-te-role-tab:focus-visible {
  outline: 2px solid var(--te-panel-accent);
  outline-offset: 2px;
}

/* Flat, vertically-scrolling font list (replaces the old wrapping look grid).
 *
 * #25 — the cap is viewport-relative, not a magic pixel number. It used to be
 * a flat 320px, which on Karl's 893px screen showed three and a half of
 * nineteen fonts: 1642px of list in a 320px window, scrolling inside a panel
 * that already scrolls. A fixed number also cannot know how tall the screen
 * is — the same 320px is most of a laptop's viewport and a fifth of his.
 *
 * Still capped rather than let loose. Nineteen rows unbounded is 1642px, which
 * puts "Use your own font" most of a screen and a half below the list, and
 * import is a real thing people need to find. 68vh is the compromise: eight
 * rows or so on a normal screen, and the section below stays within reach.
 * The 900px ceiling stops a very tall display turning it into a wall. */
.manifold-te-font-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: min(68vh, 900px);
  overflow-y: auto;
  padding: 0 2px 12px;
}

/* One font-list row — a full-width button, not a card. Same bg/radius/hover/
 * selected treatment as the retired .manifold-te-look-card. */
.manifold-te-font-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 8px 10px;
  width: 100%;
  text-align: left;
  background: var(--te-panel-bg-elev);
  border: 1px solid var(--te-panel-border);
  border-radius: 6px;
  cursor: pointer;
  transition: border-color 0.12s;
  color: var(--te-panel-fg);
}
.manifold-te-font-item:hover {
  border-color: var(--te-panel-accent);
}
.manifold-te-font-item.is-selected {
  border-color: var(--te-panel-accent);
  border-width: 2px;
}
.manifold-te-font-item:focus-visible {
  outline: 2px solid var(--te-panel-accent);
  outline-offset: 2px;
}

/* Sampler line 1: the font name. 14px/600 — carried over verbatim from the
 * retired .manifold-te-look-name. */
.manifold-te-font-item-name {
  font-size: 14px;
  font-weight: var(--mt-weight-semibold);
  color: var(--te-panel-fg);
  line-height: 1.3;
}

/* Sampler line 2: the locked judging sentence. One step up from the name
 * line so it reads as the most legible line. */
.manifold-te-font-item-sentence {
  font-size: 15px;
  font-weight: var(--mt-weight-regular);
  color: var(--te-panel-fg);
  line-height: 1.4;
}

/* Sampler line 3: Bold / Italic / Light / 0123 as separate spans. Gap via
 * flex `gap`, NOT literal multi-space text nodes (UI-SPEC D-04 a11y note). */
.manifold-te-font-item-styles {
  display: flex;
  gap: 10px;
  font-size: 12px;
  color: var(--te-panel-dim);
  line-height: 1.4;
}
.manifold-te-font-item-styles .is-bold { font-weight: 700; }  /* SAFE: a sample of bold has to be bold whatever Text weight is set to */
.manifold-te-font-item-styles .is-italic { font-style: italic; }
.manifold-te-font-item-styles .is-light { font-weight: 300; }  /* SAFE: a sample of light has to be light whatever Text weight is set to */

/* ── Phase 144-05: TeScaleSlider (replaces Phase 125 TeDensitySection) ───────
 *
 * 5-stop discrete slider replacing the three density buttons. One control
 * drives both spacing and card geometry via a single per-stop factor.
 * Scale is its own axis — NOT theme-bound; saved per user per display the
 * moment the slider moves.
 *
 * Visual language:
 *   - Two-tone track: accent fill left of handle, muted right. Driven by the
 *     --fill CSS custom property set as an inline style on the wrapper element
 *     (the ONLY allowed inline style per the deploy lint gate rule).
 *   - Thumb: 16px circle, accent color, slight border + shadow for depth.
 *   - Ticks: 5 thin vertical marks below the track, space-between aligned.
 *   - Labels: Compact / Normal / Expanded at the three named stops only;
 *     indices 1 and 3 are intentionally unlabeled.
 *   - Hint: one plain sentence describing the extremes.
 *
 * Tokens: --te-panel-bg-elev, --te-panel-bg, --te-panel-border,
 *         --te-panel-fg, --te-panel-dim, --te-panel-accent.
 * No new tokens. No inline aesthetic styles in JSX. --fill is data, not chrome.
 * ────────────────────────────────────────────────────────────────────────── */

/* Outer wrapper — top margin separates from TeFontSection above. */
.manifold-te-density-section {
  margin-top: 14px;
  padding-bottom: 4px;
}

/* Inner wrapper that carries --fill (the accent track fill percent).
   Set via inline style={{ '--fill': stop/4*100 + '%' }} — data carrier only. */
.manifold-te-density-slider-wrap {
  padding: 0 2px 0;
}

/* The <input type="range"> — styled entirely here, zero inline aesthetic style. */
.manifold-te-density-slider {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 4px;
  border-radius: 2px;
  /* Two-tone track: accent up to --fill, muted border from --fill onward. */
  background: linear-gradient(
    to right,
    var(--te-panel-accent) var(--fill, 50%),
    var(--te-panel-border) var(--fill, 50%)
  );
  outline: none;
  cursor: pointer;
  margin: 8px 0 0;
}

/* Thumb — WebKit / Blink */
.manifold-te-density-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--te-panel-accent);
  border: 2px solid var(--te-panel-bg-elev);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
  cursor: pointer;
  transition: box-shadow 0.12s;
}
.manifold-te-density-slider::-webkit-slider-thumb:hover {
  box-shadow: 0 1px 7px rgba(0, 0, 0, 0.6);
}

/* Thumb — Firefox */
.manifold-te-density-slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--te-panel-accent);
  border: 2px solid var(--te-panel-bg-elev);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
  cursor: pointer;
}

/* Track override for Firefox (it doesn't inherit the gradient from the input). */
.manifold-te-density-slider::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: var(--te-panel-border);
}
.manifold-te-density-slider::-moz-range-progress {
  height: 4px;
  border-radius: 2px 0 0 2px;
  background: var(--te-panel-accent);
}

/* Keyboard focus ring — uses the panel accent color (THEME-04 safe). */
.manifold-te-density-slider:focus-visible {
  outline: 2px solid var(--te-panel-accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Five tick marks, space-between so they align with the 5 stops. */
.manifold-te-density-ticks {
  display: flex;
  justify-content: space-between;
  padding: 0 7px;   /* ~half thumb width; aligns ticks to thumb centers */
  margin-top: 4px;
}
.manifold-te-density-tick {
  width: 1px;
  height: 5px;
  background: var(--te-panel-border);
  border-radius: 1px;
}

/* Three labels: Compact (left), Normal (center), Expanded (right).
   Indices 1 and 3 are unlabeled — intentional white space. */
.manifold-te-density-labels {
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  padding: 0 2px;
}
.manifold-te-density-label {
  font-size: 10px;
  color: var(--te-panel-dim);
  line-height: 1;
  user-select: none;
}

/* ── #102 / #105: the six ramps ───────────────────────────────────────────
 *
 * One row per family: a label on the left, its notches on the right, wrapping
 * on a narrow modal rather than squeezing. The notches themselves are
 * .manifold-te-role-tab — the Fonts block's pill, reused rather than
 * re-drawn, so the two blocks in this dialog stay one control idiom and both
 * repaint together when the panel palette changes.
 *
 * Roundness has four notches and Line height three, so the rows are
 * deliberately NOT a grid: forcing equal columns would stretch "Tight ·
 * Normal · Loose" across the width Square·Soft·Round·Pill needs and make two
 * different-sized sets look like one broken one.
 */
.manifold-te-ramps {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 2px 2px 10px;
}
.manifold-te-ramp-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.manifold-te-ramp-label {
  font-size: 11px;
  color: var(--te-panel-dim);
  min-width: 78px;
  user-select: none;
}
.manifold-te-ramp-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
/* The live pixel values. Set in the readings face at the smallest step so it
   reads as a readout rather than as another sentence to parse. Was
   `var(--m-font-data, var(--m-font-mono))` until #140 removed the Data role;
   the fallback was the only branch that ever fired. */
.manifold-te-ramp-preview {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--te-panel-dim);
  line-height: 1.6;
  padding: 4px 2px 0;
  word-break: break-word;
}

/* ── #105: the chart-weight preview ───────────────────────────────────────
 *
 * Twelve traces in the theme's twelve series colours, stroked at whatever the
 * Chart weight knob is currently on. Full width and short, because it is a
 * readout of a palette rather than a chart anybody reads values off — and
 * preserveAspectRatio="none" on the SVG means the height here is the height on
 * screen, so the stroke weight the knob sets is the stroke weight seen.
 *
 * The strip sits under the pixel readout and above nothing: it is the last
 * thing in the block, so a narrow modal wraps the notches above it rather than
 * squeezing this.
 */
.manifold-te-chart-preview {
  padding: 2px 2px 0;
}
.manifold-te-chart-preview svg {
  display: block;
  width: 100%;
  height: 22px;
}

/* Helper text beneath the labels — one plain sentence. */
.manifold-te-density-hint {
  font-size: 11px;
  color: var(--te-panel-dim);
  line-height: 1.5;
  padding: 6px 2px 8px;
}

/* ── Phase 124 (189-04: import-only, D-07): Bring-your-own font upload ─────
 *
 * Upload zone, licensing notice, and error line for the DHUB-02
 * bring-your-own upload control. All values use the existing --te-*
 * palette — no hex literals, no new tokens. The "Apply to" role-select row
 * (.manifold-te-byo-row / -role-label / -role-select) is retired — upload
 * is import-only; the font joins the flat list unselected (see
 * .manifold-te-font-item above).
 *
 * Upload zone uses --te-panel-bg-elev (elevated surface, same as font-list
 * rows). Error uses --te-warn. Notice uses --te-panel-dim.
 * ────────────────────────────────────────────────────────────────────────── */

/* Outer wrapper — vertical stack with a top divider to separate from the
   flat font list above. */
.manifold-te-byo-font {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--te-panel-border);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Upload zone: a <label> wrapping the hidden <input type="file">.
   Elevated surface matching look cards and per-role selects. */
.manifold-te-byo-upload-zone {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--te-panel-bg-elev);
  border: 1px solid var(--te-panel-border);
  border-radius: 4px;
  padding: 10px 16px;
  cursor: pointer;
  transition: border-color 0.15s;
}
.manifold-te-byo-upload-zone:hover {
  border-color: var(--te-panel-accent);
}
.manifold-te-byo-upload-zone.is-uploading {
  opacity: 0.6;
  cursor: default;
  pointer-events: none;
}

/* The actual file input — visually hidden, keyboard-accessible via the label. */
.manifold-te-byo-file-input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Button label text inside the upload zone. */
.manifold-te-byo-btn-label {
  font-size: 12px;
  color: var(--te-panel-fg);
}

/* Validation / server error line — appears below the upload zone. */
.manifold-te-byo-error {
  font-size: 12px;
  color: var(--te-warn);
  line-height: 1.4;
}

/* Licensing notice — always visible, non-blocking, below the upload zone
   (or error line). Dim text signals informational, not actionable. */
.manifold-te-byo-notice {
  font-size: 12px;
  color: var(--te-panel-dim);
  line-height: 1.4;
  margin-top: 4px;
}

/* ── Phase 86: Internal Daemons — read-only System-hub section ─────────────
 *
 * New classes follow the manifold-svc-daemon-* BEM sub-namespace.
 * Appended here (no new <link> tag) to avoid the dist-index.html.tmpl
 * parity trap (feedback_manifold_dist_template_parity).
 *
 * Reuses without modification from Phases 84-85 (do NOT redefine):
 *   .manifold-svc-pill* / .manifold-svc-header* / .manifold-svc-group*
 *
 * All values use --m-* / --mt-* CSS-var tokens — no hardcoded hex.
 * ────────────────────────────────────────────────────────────────────────── */

/* ── Modal container ── */
.manifold-svc-daemon-modal {
  width: min(680px, 92vw);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
}

/* Modal header bar: title + read-only tag + close button */
.manifold-svc-daemon-modal__header {
  background: var(--mt-bg-elev);
  border-bottom: 1px solid var(--m-border);
}

/* "Internal daemons" heading — flex row so read-only tag sits inline */
.manifold-svc-daemon-modal__title {
  display: flex;
  align-items: baseline;
  gap: var(--m-pad-sm);
  font-size: 15px;
  font-weight: var(--m-font-weight-semibold);
  color: var(--mt-fg);
}

/* "read-only" inline mono badge — signals no control actions exist */
.manifold-svc-daemon-modal__readonly {
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-weight: var(--mt-weight-regular);
  color: var(--m-text-faint);
  border: 1px dashed var(--m-border);
  border-radius: var(--m-radius-full);
  padding: 2px 6px;
  white-space: nowrap;
}

/* Scrollable content area: health header + groups.
 * flex-basis MUST be auto (not 0): the modal card has only max-height, no
 * definite height, so a basis-0 child collapses to ~one line and overflow
 * clips the rest (the body rendered 1095px of content into a 28px box).
 * basis auto sizes the body to its content; min-height:0 lets it shrink and
 * scroll once content exceeds the card's max-height. Matches the working
 * scrollable-modal-body pattern used elsewhere in this file. */
.manifold-svc-daemon-modal__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--m-pad-md);
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-md);
}

/* ── Daemon row ── */
/* Mirrors .manifold-svc-row but 4px shorter (40px vs 44px) for modal density */
.manifold-svc-daemon-row {
  display: flex;
  align-items: center;
  gap: var(--m-pad-sm);
  min-height: 40px;
  padding: 0 var(--m-pad-md);
  border-bottom: 1px solid var(--m-border);
  transition: background 140ms;
}
.manifold-svc-daemon-row:hover {
  background: color-mix(in srgb, var(--mt-accent) 4%, transparent);
}

/* Unit name + role stack (flex 1 — takes remaining width) */
.manifold-svc-daemon-row__text {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Unit name: 13px semibold — matches .manifold-svc-row__name */
.manifold-svc-daemon-row__name {
  font-size: 13px;
  font-weight: var(--m-font-weight-semibold);
  line-height: 1.2;
  color: var(--mt-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Role description: 11px faint — one level below unit name */
.manifold-svc-daemon-row__role {
  font-size: 11px;
  font-weight: var(--mt-weight-regular);
  color: var(--m-text-faint);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Last-seen timestamp: mono 10.5px faint — matches .manifold-svc-row__meta */
.manifold-svc-daemon-row__lastseen {
  flex: none;
  font-family: var(--m-font-mono);
  font-size: 10.5px;
  color: var(--m-text-faint);
  white-space: nowrap;
  min-width: 72px;
  text-align: right;
}

/* → Logs text-link button: 12px sans, muted at rest, accent on hover */
.manifold-svc-daemon-row__logs {
  flex: none;
  font-family: var(--m-font-sans);
  font-size: 12px;
  font-weight: var(--mt-weight-regular);
  color: var(--m-text-muted);
  background: none;
  border: none;
  padding: 4px 8px;
  min-height: 28px;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  border-radius: var(--m-radius-sm);
  transition: color 120ms;
}
.manifold-svc-daemon-row__logs:hover {
  color: var(--mt-fg);
  text-decoration: underline;
  text-decoration-color: var(--mt-accent);
}

/* Disabled → Logs: opacity 0.4, no pointer events — slot kept for alignment */
.manifold-svc-daemon-row__logs--disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

/* Restart: owner-only action control (Phase 89 / D-05). Reads as a real
   button — a quiet outline that warms to amber on hover — so it's clearly
   actionable next to the muted "→ Logs" text-link, without shouting. The
   amber matches the confirm dialog it opens (D-10). */
.manifold-svc-daemon-row__restart {
  flex: none;
  font-family: var(--m-font-sans);
  font-size: 12px;
  font-weight: var(--m-font-weight-semibold);
  color: var(--m-text-muted);
  background: none;
  border: 1px solid var(--m-border);
  padding: 4px 12px;
  min-height: 28px;
  cursor: pointer;
  white-space: nowrap;
  border-radius: var(--m-radius-sm);
  transition: color 120ms, border-color 120ms, background 120ms;
}
.manifold-svc-daemon-row__restart:hover {
  color: var(--m-warning);
  border-color: var(--m-warning);
  background: color-mix(in srgb, var(--m-warning) 12%, transparent);
}
.manifold-svc-daemon-row__restart:disabled {
  opacity: 0.6;
  cursor: default;
  color: var(--m-text-faint);
  border-color: var(--m-border);
  background: none;
}

/* Restart outcome (D-11): a compact mono status that sits left of the
   timestamp. "restarting…" muted, "restart sent" green, "restart failed:…"
   red. Recovery itself is shown by the status pill, not this text. */
.manifold-svc-daemon-row__restart-msg {
  flex: none;
  font-family: var(--m-font-mono);
  font-size: 10.5px;
  white-space: nowrap;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.manifold-svc-daemon-row__restart-msg--sending { color: var(--m-text-muted); }
.manifold-svc-daemon-row__restart-msg--sent    { color: var(--m-success); }
.manifold-svc-daemon-row__restart-msg--failed  { color: var(--m-error); }

/* ── Error / loading state ── */
.manifold-svc-daemon-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--m-pad-sm);
  padding: var(--m-pad-lg) var(--m-pad-md);
  text-align: center;
}

.manifold-svc-daemon-error__icon {
  font-size: 24px;
  color: var(--m-text-faint);
  line-height: 1;
}

.manifold-svc-daemon-error__heading {
  font-size: 14px;
  font-weight: var(--m-font-weight-semibold);
  color: var(--mt-fg);
}

.manifold-svc-daemon-error__body {
  font-size: 12px;
  color: var(--m-text-muted);
  max-width: 360px;
  line-height: 1.5;
}

/* Retry button: plain bordered button using existing manifold-button pattern */
.manifold-svc-daemon-error__retry {
  margin-top: var(--m-pad-xs);
  padding: 6px 16px;
  font-size: 12px;
  font-family: var(--m-font-sans);
  color: var(--mt-fg);
  background: var(--mt-bg-elev);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-md);
  cursor: pointer;
  transition: background 120ms;
}
.manifold-svc-daemon-error__retry:hover {
  background: color-mix(in srgb, var(--mt-accent) 8%, var(--mt-bg-elev));
}
/* ── end Phase 86: Internal Daemons ─────────────────────────────────────── */

/* ── Phase 90: Health tab two-section layout ────────────────────────────────
 * .manifold-health-tab* — outer wrapper and section structure for the
 * consolidated Health tab (SERVICE-CONSOLE-CONSOLIDATION-CONTRACT §0).
 *
 * The tab renders two stacked sections: Apps on top (ServicesTabRenderer),
 * Plumbing below (daemons + connectors + bus link).  Each section gets its
 * OWN labeled header and health bar (D-05 — app trouble and plumbing trouble
 * must never blur into one number).
 *
 * All values use --m-* / --mt-* tokens — no hardcoded hex.
 * ────────────────────────────────────────────────────────────────────────── */

/* Outer tab wrapper — full-height scroll container */
.manifold-health-tab {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-lg);
  padding: var(--m-pad-md);
  overflow-y: auto;
}

/* One section (Apps or Plumbing) */
.manifold-health-tab__section {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-sm);
}

/* Section label — e.g. "Plumbing" or "Apps" — mirrors .manifold-section-label */
.manifold-health-tab__section-label {
  font-size: 11px;
  font-weight: var(--m-font-weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--m-text-faint);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--m-border);
}

/* Section health bar — same .manifold-svc-header base, slight top margin so
   it reads as the header of its own section, not a continuation of the label */
.manifold-health-tab__section-header {
  margin-top: 2px;
}
/* ── end Phase 90: Health tab layout ────────────────────────────────────── */

/* ── Phase 129: edit-lock chrome ────────────────────────────────────────── */

/* Informational banner — calm elevated-glass surface. Overrides the base
 * .manifold-banner (which uses --m-error red). Must come after
 * .manifold-banner.warning so this modifier wins the cascade. */
.manifold-banner.info {
  background: var(--mt-bg-elev);
  border-bottom: 1px solid var(--mt-border-strong);
  color: var(--mt-fg);
}

/* Save/Cancel button-pair wrapper in the header strip. Layout only — no
 * color properties so the theme cascade applies to the buttons inside. */
.manifold-edit-chrome {
  display: flex;
  align-items: center;
  gap: var(--mt-space-sm);
}

/* Narrower modal for two-button confirmation dialogs (480px vs 720px). */
.manifold-modal.compact {
  max-width: 480px;
}
/* ── end Phase 129: edit-lock chrome ───────────────────────────────────── */

/* ── Phase 131: Version history list ───────────────────────────────────── */

.manifold-version-list {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-sm);
  padding: 0 var(--m-pad-md) var(--m-pad-md);
}

.manifold-version-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--m-pad-md);
  padding: var(--m-pad-md);
  background: var(--m-surface-2, var(--m-surface));
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-md);
  transition: background 140ms, border-color 140ms;
}

.manifold-version-row:hover {
  background: var(--mt-hover-bg);
  border-color: var(--mt-hover-border);
}

.manifold-version-row-text {
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-2xs);
  min-width: 0;
}

.manifold-version-row-label {
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--m-text);
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.manifold-version-row-meta {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--m-text-faint);
  line-height: 1.4;
}

/* ── end Phase 131: Version history list ────────────────────────────────── */

/* ── Phase 135: Restore reconciler ─────────────────────────────────────── */

/* Summary line at the top of the reconciler body — scope one-liner. */
.manifold-restore-summary-line {
  font-size: var(--mt-text-md-size, 14px);
  font-weight: var(--mt-weight-medium, 500);
  color: var(--mt-fg);
  line-height: 1.5;
  padding: var(--m-pad-md) var(--m-pad-lg);
}

/* Bulk "decide all at once" strip — shown only when 2+ collision pairs exist. */
.manifold-restore-bulk-bar {
  display: flex;
  align-items: center;
  gap: var(--m-pad-sm);
  flex-wrap: wrap;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-md, 4px);
  padding: var(--m-pad-sm) var(--m-pad-md);
  margin: 0 var(--m-pad-lg) var(--m-pad-sm);
  font-size: 11px;
  color: var(--mt-dim);
  font-family: var(--m-font-sans);
}

/* Section wrapper (Tabs / Cards / Cards that need a home). */
.manifold-restore-section {
  margin: 0 0 var(--m-pad-md);
  padding: 0 var(--m-pad-lg);
}

/* One tab row inside the Tabs section. Reuses .manifold-version-row chrome
   (border, bg, radius, gap) but adds a flex column for subnote. */
.manifold-restore-tab-row {
  display: flex;
  align-items: flex-start;
  gap: var(--m-pad-md);
  padding: var(--m-pad-sm) var(--m-pad-md);
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-md, 4px);
  margin-bottom: var(--m-pad-xs);
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--mt-fg);
}

/* Checkbox inside old-only tab row; accent-colored via the native CSS property. */
.manifold-restore-tab-checkbox {
  flex: 0 0 auto;
  margin-top: 1px;
  accent-color: var(--mt-accent);
}

/* Sub-note under old-only tab row (checked/unchecked hint). */
.manifold-restore-tab-subnote {
  font-size: var(--mt-text-xs-size, 10px);
  color: var(--mt-dim);
  margin-top: var(--m-pad-2xs);
  line-height: 1.5;
}

/* Collapsed "N cards — kept as-is" or "N old-only cards" count line.
   Uses <details> for the expand (CSS-only; no JS needed). */
.manifold-restore-auto-count {
  font-size: var(--mt-text-xs-size, 10px);
  color: var(--mt-dim);
  padding: var(--m-pad-xs) var(--m-pad-sm);
  cursor: pointer;
  line-height: 1.5;
}
.manifold-restore-auto-count summary {
  list-style: none;
  cursor: pointer;
}
.manifold-restore-auto-count summary::-webkit-details-marker { display: none; }

/* One difference pair — two-column side-by-side layout. */
.manifold-restore-diff-pair {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-md, 4px);
  background: var(--mt-bg-elev);
  margin-bottom: var(--m-pad-sm);
  position: relative;
  overflow: hidden;
}

/* Chosen column gets accent border; unchosen dims to 40% opacity. */
.manifold-restore-diff-col {
  padding: var(--m-pad-md);
  display: flex;
  flex-direction: column;
  gap: var(--m-pad-xs);
  transition: opacity 150ms;
}
.manifold-restore-diff-col[data-chosen="true"] {
  outline: 2px solid var(--mt-accent);
  outline-offset: -2px;
}
.manifold-restore-diff-col[data-dimmed="true"] {
  opacity: 0.4;
}

/* Column header label: "YOUR CURRENT VERSION" / "THE OLD VERSION" */
.manifold-restore-diff-col-header {
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 10px);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--mt-dim);
  margin-bottom: var(--m-pad-2xs);
}

/* Card name inside a diff column. */
.manifold-restore-diff-card-name {
  font-size: var(--mt-text-sm-size, 12px);
  font-weight: var(--mt-weight-semibold);
  color: var(--mt-fg);
  line-height: 1.3;
}

/* Card-type pill badge. */
.manifold-restore-diff-type-badge {
  display: inline-block;
  border: 1px solid var(--mt-border);
  border-radius: 9999px;
  font-size: 10px;
  padding: 1px var(--m-pad-xs);
  color: var(--mt-dim);
  font-family: var(--m-font-mono);
  margin-left: var(--m-pad-xs);
  vertical-align: middle;
}

/* "What's different:" list container. */
.manifold-restore-diff-changes {
  font-size: var(--mt-text-xs-size, 10px);
  color: var(--mt-dim);
  line-height: 1.5;
}

/* One bullet line in the changes list. */
.manifold-restore-diff-change-item {
  display: flex;
  align-items: flex-start;
  gap: var(--m-pad-2xs);
  color: var(--mt-fg);
}
.manifold-restore-diff-change-item::before {
  content: '•';
  color: var(--mt-dim);
  flex: 0 0 auto;
}

/* Vertical 1px divider between the two diff columns. */
.manifold-restore-diff-divider {
  width: 1px;
  background: var(--mt-border);
  align-self: stretch;
}

/* Status indicator in the top-right corner of a diff pair. */
.manifold-restore-diff-pair-status {
  position: absolute;
  top: var(--m-pad-xs);
  right: var(--m-pad-xs);
  font-size: 14px;
  line-height: 1;
}

/* One card-needing-a-home row. */
.manifold-restore-rehome-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--m-pad-sm);
  padding: var(--m-pad-sm) var(--m-pad-md);
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-md, 4px);
  margin-bottom: var(--m-pad-xs);
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--mt-fg);
}

/* The tab dropdown inside a re-home row. */
.manifold-restore-rehome-select {
  background: var(--mt-bg);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-sm, 3px);
  color: var(--mt-fg);
  font-size: var(--mt-text-xs-size, 10px);
  font-family: var(--m-font-mono);
  padding: var(--m-pad-2xs) var(--m-pad-xs);
  cursor: pointer;
}
.manifold-restore-rehome-select:focus {
  outline: 1px solid var(--mt-accent);
}

/* "Your restore was canceled" overlay — overlays the reconciler body. */
.manifold-restore-interrupted {
  position: absolute;
  inset: 0;
  z-index: 10;
  background: var(--mt-bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--m-pad-md);
  padding: var(--m-pad-xl);
  text-align: center;
}
.manifold-restore-interrupted-title {
  font-size: var(--mt-text-md-size, 14px);
  font-weight: var(--mt-weight-semibold);
  color: var(--mt-warn);
}
.manifold-restore-interrupted-body {
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--mt-dim);
  max-width: 340px;
  line-height: 1.6;
}

/* Footer button row — right-aligned inside .manifold-app-modal-footer. */
.manifold-restore-footer-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--m-pad-sm);
}

/* ── Phase 135 sub-classes for inline-style-free components ─────────────── */

/* Tab row meta text — "from your old dashboard", "yours — keeping it", etc. */
.manifold-restore-tab-meta {
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 10px);
  color: var(--mt-dim);
}
/* Inline variant with left spacing (for "yours — keeping it" / "in both versions" text) */
.manifold-restore-tab-meta-spaced {
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 10px);
  color: var(--mt-dim);
  margin-left: var(--m-pad-sm);
}

/* Diff column "What's different:" label */
.manifold-restore-diff-whats-different {
  font-size: var(--mt-text-xs-size, 10px);
  color: var(--mt-dim);
  margin-bottom: var(--m-pad-2xs);
}

/* Diff column button area */
.manifold-restore-diff-action {
  margin-top: var(--m-pad-xs);
}

/* Nothing-to-do / already-have-all heading */
.manifold-restore-empty-heading {
  font-weight: var(--mt-weight-semibold, 600);
  margin-bottom: var(--m-pad-sm);
}

/* Nothing-to-do / already-have-all body text */
.manifold-restore-empty-body {
  font-size: var(--mt-text-sm-size, 12px);
  color: var(--mt-dim);
  line-height: 1.6;
}

/* Re-home row "Put it on:" label */
.manifold-restore-rehome-label {
  font-size: var(--mt-text-xs-size, 10px);
  color: var(--mt-dim);
}

/* Re-home row card name */
.manifold-restore-rehome-card-name {
  font-weight: var(--mt-weight-semibold, 600);
}

/* Re-home row "was on:" meta */
.manifold-restore-rehome-meta {
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 10px);
  color: var(--mt-dim);
  margin-left: var(--m-pad-sm);
}

/* Status glyphs for diff pair resolution indicator */
.manifold-restore-status-resolved {
  color: var(--mt-live);
  font-size: 14px;
  line-height: 1;
}
.manifold-restore-status-unresolved {
  color: var(--mt-warn);
  font-size: 13px;
  line-height: 1;
}

/* Diff card-name + badge row */
.manifold-restore-diff-card-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--m-pad-2xs);
}

/* Inline apply-error margin wrapper */
.manifold-restore-apply-error {
  margin: var(--m-pad-sm) 0;
}

/* Nothing-to-do body padding */
.manifold-restore-empty-body-pad {
  padding: var(--m-pad-lg);
}

/* Section label with top spacing for stacked sections */
.manifold-restore-section-label-spaced {
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 10px);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--mt-dim);
  margin-top: var(--m-pad-sm);
  margin-bottom: var(--m-pad-xs);
}

/* ── end Phase 135: Restore reconciler ──────────────────────────────────── */

/* ── Phase 144-06: density content-scale wrapper ────────────────────────── */
/*
 * .cb-card-content-scale wraps CardRenderer inside DraggableCard at non-Normal
 * density stops. Inline style provides: width:w (base px), height:h or
 * minHeight:h (base px), zoom:df. The zoom re-rasterises text and SVG crisply
 * (unlike transform:scale which produces a blurry scale of the rasterised
 * output). box-sizing:border-box ensures the base w×h measurements include any
 * card-frame border so the zoomed wrapper fills the outer density-scaled box
 * exactly. At Normal (df=1.0) the wrapper is not applied; at all other stops
 * it appears with zoom set so one factor scales box geometry AND content.
 */
.cb-card-content-scale {
  box-sizing: border-box;
}
/* ── end Phase 144-06 ───────────────────────────────────────────────────── */

/* ── Phase 223: the Style result strip ──────────────────────────────────── */
/*
 * The strip that appears across the top of the dashboard after a look has been
 * applied to more than one card. It rides on the shipped .manifold-banner.info
 * surface — the calm raised variant, never the error red — and adds only what
 * that surface does not already give it.
 *
 * NOTHING BELOW MODIFIES AN EXISTING RULE. Safe mode, the signed-out-page
 * editor and the backup-auth notice all ride on .manifold-banner and
 * .manifold-banner.warning; all three are outside this phase and all three
 * would change under them if these were edits rather than additions.
 *
 * Every value here is a theme token or a mix against one. No hex literal and
 * no raw colour, so all twelve themes get this for free.
 */

/*
 * The strip itself. .manifold-banner.info already draws exactly this bottom
 * rule, and the restatement is deliberate: .info has three other users, and the
 * strip needs its separation from the canvas underneath whatever happens to
 * that shared rule later. Vertical alignment is left to the base rule — a
 * wrapped failure sentence centres its dot and its buttons against the block,
 * which is what every other multi-line banner in this file already does.
 */
.manifold-banner.info.manifold-style-banner {
  border-bottom: 1px solid var(--mt-border-strong);
}

/*
 * Something did not take. Marked with an edge, NOT a fill.
 *
 * The shipped .manifold-banner.warning fills the strip with --m-warning and
 * keeps --m-text on top of it: about 2:1, below every contrast floor there is,
 * on a message whose entire value is the card names it carries. So both
 * outcomes keep the calm raised surface, whose text pairing is what the whole
 * dashboard is already built on, and the failure state is marked instead.
 *
 * Solid --m-warning, not a tint and not a mix: a mixed amber against a white
 * raised surface disappears, and Silver Lining's raised surface is white.
 */
.manifold-banner.info.manifold-style-banner.is-partial {
  border-left: 3px solid var(--m-warning);
}

/*
 * The dot before the text. Decorative only — it is hidden from screen readers
 * in the markup, because the sentence carries the meaning.
 *
 * --m-radius-full is not defined by every theme, so it is written with a
 * fallback, matching the convention style-panel.css already uses for corners.
 */
.manifold-style-banner-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--m-radius-full, 9999px);
  background: var(--m-warning);
  flex: 0 0 auto;
}

/*
 * THE UNDO BUTTON — the rule that decides whether the most important control
 * this phase ships is visible at all.
 *
 * `.manifold-banner button` gives every banner button a white 12% fill and a
 * white 20% border. Those were written for the red base banner and they are
 * right there. On the calm raised surface they wash out on all three light
 * themes, and Silver Lining's raised surface is pure white — a white overlay
 * on white is nothing at all, so the button becomes an invisible box carrying
 * the word Undo.
 *
 * Adding the ordinary button class does not rescue it: `.manifold-banner
 * button` is (0,1,1) and `.manifold-button` is (0,1,0), so the inherited rule
 * wins. This selector is (0,3,1) and beats it.
 *
 * DO NOT DELETE THIS AS REDUNDANT. It only looks redundant on the nine dark
 * themes, where the white overlays happen to work.
 */
.manifold-banner.info.manifold-style-banner button {
  background: color-mix(in srgb, var(--mt-fg) 8%, transparent);
  border: 1px solid var(--mt-border-strong);
  color: var(--mt-fg);
}

.manifold-banner.info.manifold-style-banner button:hover {
  background: color-mix(in srgb, var(--mt-fg) 16%, transparent);
}
/* ── end Phase 223 ──────────────────────────────────────────────────────── */

/* =========================================================================
 * Phase 223 gap closure — ledger id 7
 *
 * THE "EDIT HERE INSTEAD" BUTTON on the someone-else-is-editing notice.
 *
 * Same defect as the Undo button above, same cause, same shape of fix.
 * `.manifold-banner button` gives every banner button a white 12% fill and a
 * white 20% border. Those were written for the red base banner and they are
 * right there. FollowerBanner is not red — it uses .info, the calm raised
 * surface — and on the three light themes the white overlays wash out.
 *
 * WITHOUT THIS RULE, on Silver Lining — the lightest theme — the raised
 * surface is pure white, so a white overlay on it is nothing at all: the only
 * control that gets a user out of a dashboard another device has locked would
 * be an invisible box carrying the words "Edit here instead".
 *
 * Adding the ordinary button class does not rescue it: `.manifold-banner
 * button` is (0,1,1) and `.manifold-button` is (0,1,0), so the inherited rule
 * wins. This selector is (0,3,1) and beats it. The modifier class is also
 * what keeps the rule off safe mode's notice, the signed-out-page editor's
 * notice and the backup-auth notice, which all ride on the same family.
 *
 * DO NOT DELETE THIS AS REDUNDANT. It only looks redundant on the nine dark
 * themes, where the white overlays happen to work.
 *
 * The token choices and the two percentages are deliberately IDENTICAL to the
 * Undo button's above. Two buttons doing the same job on the same surface
 * should not look different, and a second set of numbers is a second thing to
 * keep in step. tests/223-follower-banner-contrast.test.js pins them together.
 *
 * Every value here is a theme token or a mix against one. No hex literal and
 * no raw colour, so all twelve themes get this for free.
 * ========================================================================= */
.manifold-banner.info.manifold-follower-banner button {
  background: color-mix(in srgb, var(--mt-fg) 8%, transparent);
  border: 1px solid var(--mt-border-strong);
  color: var(--mt-fg);
}

.manifold-banner.info.manifold-follower-banner button:hover {
  background: color-mix(in srgb, var(--mt-fg) 16%, transparent);
}
/* ── end Phase 223 gap closure ──────────────────────────────────────────── */
