/* ============================================================================
 * card-builder helpers — component-scoped CSS for card-builder.jsx section 1.
 * ----------------------------------------------------------------------------
 * Phase 19 Plan 04a (2026-05-25). These classes host the deliberately custom
 * style values that live inside HelpHint / HelpHintInline / SummaryBar /
 * BindingRow / FailureModeGroup / BindingDriftTableRenderer — values that
 * pre-Phase-19 sat as inline `style={{...}}` blocks containing FORBIDDEN_KEYS
 * (background, color, fontSize, padding, etc.) and were therefore audit-
 * flagged. Moving them here removes the audit hits without altering visuals.
 *
 * Why a separate file (not theme.css, not utilities.css):
 *   - utilities.css contract forbids hex literals and component-scoped names.
 *   - theme.css is per-theme; duplicating these declarations across 4 themes
 *     would bloat the migration and create drift hazard. The values here are
 *     intentionally theme-agnostic until a future visual-review pass
 *     promotes specific ones into per-theme overrides (the original Plan 07
 *     HelpHint comment frames `#0f1418 → var(--mt-bg-deep)` as exactly that
 *     follow-up — out of scope for the mechanical migration).
 *   - Mirrors the existing pattern at core/dashboard/components/Button.css /
 *     Field.css / Modal.css / Panel.css.
 *
 * Loaded BETWEEN utilities.css and theme.css (see index.html). Bump the
 * `?v=` cache-buster on edits.
 * ============================================================================
 */

/* === HelpHint badge ====================================================== *
 * The ? / ! glyph itself — small circle of currentColor that opens the
 * popover on hover/click. Default state is a dim ring (opacity 0.45); the
 * warning variant goes full-opacity in --mt-warn so it reads as a guard.
 * --------------------------------------------------------------------- */

.cb-help-hint-badge {
  border: 1px solid currentColor;
  color: inherit;
  opacity: 0.45;
}

.cb-help-hint-badge.is-warn {
  border: 1px solid var(--mt-warn);
  color: var(--mt-warn);
  opacity: 1;
}

/* === HelpHint popover ===================================================== *
 * The ? / ! badge that opens a small popover next to inspector fields. The
 * badge itself is a small ring of currentColor; the popover holds a darker
 * surface and a subtle 1px frame. Discretion Note 1 from the HelpHint
 * UI-SPEC (preserved verbatim from card-builder.jsx pre-migration): the
 * hardcoded #0f1418 popover background is INHERITED unchanged in this
 * plan — promotion to var(--mt-bg-deep) is a flagged follow-up that
 * changes 58 popovers' look at once and deserves its own visual review.
 * --------------------------------------------------------------------- */

.cb-help-hint-popover {
  min-width: 220px;
  max-width: 320px;
  padding: 8px 10px;
  background: #0f1418;
  border: 1px solid #2a3540;
  border-radius: 4px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
  color: #cbd5e1;
  font-family: var(--m-font-mono);
  font-size: 10px;
  line-height: 1.55;
  white-space: normal;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}

.cb-help-hint-popover.is-warn {
  border-left: 2px solid var(--mt-warn);
}

/* === Inner-advanced toggle ("⏎ N advanced (M customized)") ============= *
 * The pill that sits at the top of each open accordion section. 5px 8px
 * asymmetric padding has no clean token decomposition (5px is not on the
 * mt-space-* ladder), so it lives here.
 * --------------------------------------------------------------------- */

.cb-advanced-toggle {
  padding: 5px 8px;
  border: var(--cb-toggle-border);
  background: var(--cb-toggle-bg);
  color: var(--cb-toggle-color);
}

/* === HelpHintInline disclosure body (walkthrough variant) =============== *
 * Multi-paragraph worked-example body that sits below the Field's input,
 * read in place. Connected to the field above via a 1px left accent.
 * --------------------------------------------------------------------- */

.cb-help-hint-inline-body {
  margin-top: 6px;
  padding: 8px 10px;
  border-left: 1px solid var(--mt-border);
  background: var(--mt-bg-deep);
  color: var(--mt-fg);
  font-family: var(--m-font-sans);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.5;
}

/* === Header renderer placeholder ("brand (loading…)" etc.) ============== *
 * Tiny dim label shown while a deferred header widget global resolves.
 * --------------------------------------------------------------------- */

.cb-header-placeholder {
  opacity: 0.5;
  font-size: 11px;
}

/* === Audit / binding-drift table renderer ============================== *
 * Renders the catalog binding-drift audit response. Mostly hardcoded
 * pre-token rgba() fallbacks paired with --mt-* primary values — preserved
 * as-is to keep the visual identical to the pre-migration baseline.
 * --------------------------------------------------------------------- */

.cb-binding-drift-summary {
  font-family: var(--m-font-mono, monospace);
  font-size: 12px;
  padding: 6px 10px;
  background: var(--mt-surface, rgba(255, 255, 255, 0.04));
  border-bottom: 1px solid var(--mt-border, rgba(255, 255, 255, 0.10));
  display: flex;
  flex-wrap: wrap;
  gap: 0 12px;
  user-select: text;
}

/* Per-mode count cell inside the summary bar; colour + weight injected
 * by JSX via --cb-cell-* CSS vars (mode-specific). */
.cb-summary-bar-cell {
  color: var(--cb-cell-color);
  font-weight: var(--cb-cell-weight);
}

.cb-binding-row {
  padding: 4px 12px 4px 24px;
  border-bottom: 1px solid var(--mt-border-lo, rgba(255, 255, 255, 0.05));
  font-size: 11px;
  font-family: var(--m-font-mono, monospace);
  line-height: 1.7;
}

.cb-binding-row__title {
  font-weight: 600;
  color: var(--mt-fg);
  display: flex;
  align-items: center;
  gap: 6px;
}

.cb-binding-row__crosshost {
  font-size: 9px;
  padding: 1px 4px;
  background: var(--mt-warn, #f59e0b);
  color: var(--mt-bg, #0f172a);
  border-radius: 3px;
  font-weight: 700;
}

.cb-binding-row__meta {
  color: var(--mt-fg-muted, rgba(255, 255, 255, 0.55));
}

.cb-failure-mode-group {
  border-bottom: 1px solid var(--mt-border, rgba(255, 255, 255, 0.10));
}

.cb-failure-mode-group__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  cursor: pointer;
  user-select: none;
  background: var(--mt-surface-lo, rgba(255, 255, 255, 0.02));
}

.cb-failure-mode-group__chevron {
  font-size: 10px;
  color: var(--mt-fg-muted, rgba(255, 255, 255, 0.45));
}

.cb-failure-mode-group__label {
  font-weight: 600;
  font-family: var(--m-font-mono, monospace);
  font-size: 12px;
  color: var(--cb-mode-color, var(--mt-fg));
}

.cb-failure-mode-group__desc {
  font-size: 11px;
  color: var(--mt-fg-muted, rgba(255, 255, 255, 0.55));
}

.cb-failure-mode-group__count {
  margin-left: auto;
  font-size: 11px;
  font-family: var(--m-font-mono, monospace);
  font-weight: 400;
  color: var(--mt-fg-muted, rgba(255, 255, 255, 0.35));
}

.cb-failure-mode-group__count.has-rows {
  font-weight: 700;
  color: var(--cb-mode-color, var(--mt-fg));
}

/* Top-level shell for BindingDriftTableRenderer. */
.cb-binding-drift-shell {
  font-size: 12px;
  font-family: var(--m-font-mono, monospace);
  overflow: auto;
}

.cb-binding-drift-empty {
  padding: 16px;
  color: var(--mt-fg-muted, rgba(255, 255, 255, 0.4));
  font-size: 12px;
}

.cb-binding-drift-error {
  padding: 16px;
  color: var(--mt-err, #ef4444);
  font-size: 12px;
}

.cb-binding-drift-warning {
  padding: 4px 10px;
  background: var(--mt-warn-bg, rgba(245, 158, 11, 0.12));
  color: var(--mt-warn, #f59e0b);
  font-size: 11px;
  border-bottom: 1px solid var(--mt-border, rgba(255, 255, 255, 0.10));
}

.cb-binding-drift-footer {
  padding: 4px 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.cb-binding-drift-footer__timestamp {
  font-size: 10px;
  color: var(--mt-fg-muted, rgba(255, 255, 255, 0.4));
}

.cb-binding-drift-footer__refresh {
  font-size: 10px;
  padding: 2px 8px;
  cursor: pointer;
  background: var(--mt-surface, rgba(255, 255, 255, 0.07));
  color: var(--mt-fg);
  border: 1px solid var(--mt-border, rgba(255, 255, 255, 0.15));
  border-radius: 3px;
}

/* === Action-result modal body ("Invoking adapter.action…" etc.) ========= *
 * Sits inside `<div class="manifold-modal-body">` for the
 * ModalActionResultRenderer; carries the pre-migration explicit font
 * stack + token-aligned size/color so the modal body text is theme-aware
 * but visually identical to the inline baseline.
 * --------------------------------------------------------------------- */

.cb-modal-result-body {
  font-family: "Rubik", sans-serif;
  font-size: var(--mt-text-md-size, 13px);
  line-height: 1.5;
  white-space: pre-wrap;
  color: var(--m-text, var(--mt-fg));
}

.cb-modal-result-body.is-loading {
  color: var(--m-text-muted, var(--mt-muted));
  white-space: normal;
}

.cb-modal-result-body__code {
  font-family: var(--m-font-mono, "JetBrains Mono", monospace);
}

.cb-modal-result-headline {
  font-weight: 600;
  color: var(--m-error, var(--mt-warn));
  margin-bottom: 6px;
}

.cb-modal-result-hint {
  color: var(--m-text-muted, var(--mt-muted));
  font-size: var(--mt-text-sm-size, 12px);
}

.cb-modal-result-error-bg {
  background: color-mix(in srgb, var(--m-error, var(--mt-warn)) 18%, transparent);
}

/* === CardRenderer outer frame ========================================== *
 * The root <div> of every rendered card. All four aesthetic keys are
 * runtime-driven (background = cardBg, border = cardBorder, radius =
 * cardRadius, padding = headerMode ? 0 : cardStyle.padding ?? 6), so the
 * class reads them through CSS custom properties the JSX writes into the
 * inline-style block. Pattern parallels .cb-advanced-toggle (19-04a) and
 * .cb-summary-bar-cell — keeps the className purely structural while
 * letting state inject the dynamic value.
 *
 * Defaults: bg=transparent / border=none / radius=0 / padding=0 so a
 * card that omits any of the props degrades safely (caller almost always
 * sets bg + border + radius; padding has its own headerMode branch).
 * --------------------------------------------------------------------- */

.cb-card-frame {
  background: var(--cb-card-bg, transparent);
  border: var(--cb-card-border, none);
  border-radius: var(--cb-card-radius, 0);
  padding: var(--cb-card-padding, 0);
}

/* === Cell-gradient backdrop ============================================ *
 * 1×1 grid item painted behind widgets at a specific cell to host a
 * gradient background. The background string is state-driven (gradient
 * CSS computed per cell) — flows through --cb-cell-gradient-bg.
 * --------------------------------------------------------------------- */

.cb-card-cell-gradient {
  background: var(--cb-cell-gradient-bg, transparent);
  border-radius: var(--m-radius-sm, 6px);
}

/* === Repeat-mode catalog message ======================================= *
 * The "loading service catalog…" / "sign in to see services" / "service
 * catalog fetch failed" banner painted into the card grid when
 * card.repeat.kind === 'services' and the catalog isn't ready or errored.
 * Per-state color overrides ride --cb-repeat-msg-color so the same shell
 * handles muted (loading), dim (auth gate), warn (fetch fail).
 * --------------------------------------------------------------------- */

.cb-repeat-msg {
  padding: var(--mt-space-md, 12px);
  color: var(--cb-repeat-msg-color, var(--mt-muted));
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
}

/* === Zebra-stripe backdrop ============================================= *
 * Painted across a row band of the card grid to provide alternating row
 * fill in repeat-mode tables. The fill colour is state-driven (theme
 * token resolved per card) — flows through --cb-zebra-bg.
 * --------------------------------------------------------------------- */

.cb-card-zebra {
  background: var(--cb-zebra-bg, transparent);
}

/* === Scaled-content wrapper border ===================================== *
 * When card.style.contentScale or contentBorder is set on a widget, the
 * renderer wraps the inner content in a transform-scaled <div>. The
 * border colour is state-driven (resolved from token name) so flows
 * through --cb-scale-border-color; corner radius is the standard 6px.
 * --------------------------------------------------------------------- */

.cb-scale-wrapper {
  /* Default to border:none so the wrapper doesn't claim 1px of layout
   * when no border is requested. When --cb-scale-border-color is set
   * by JSX, the modifier rule below paints the visible border. */
  border: none;
  border-radius: var(--m-radius-sm, 6px);
}

.cb-scale-wrapper.has-border {
  border: 1px solid var(--cb-scale-border-color, currentColor);
}

/* === Card title (CardWithHeader, CardCell header bands) ================ *
 * The card-name header typography — Space Grotesk 14px / weight 700 /
 * tight tracking / fg colour. Appears in four places: the standalone
 * CardWithHeader chrome, plus three branches of CardCell (rest_discover
 * status tile, missing-instance tile, normal-card header band). One
 * class shared by all four so a future typography change (font swap,
 * size bump) lands in one place.
 *
 * The .with-flex modifier adds `flex:1` + ellipsis behavior used by
 * CardCell's header band where the title shares the row with edit
 * affordances; CardWithHeader's standalone use omits .with-flex.
 * --------------------------------------------------------------------- */

.cb-card-title {
  font-family: "Space Grotesk", "Rubik", sans-serif;
  font-size: 14px;
  font-weight: 700;
  color: var(--mt-fg);
  letter-spacing: -0.01em;
}

.cb-card-title.with-flex {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* === CardWithHeader top-padding band =================================== *
 * The 2px-vertical / 4px-horizontal padding around the standalone
 * CardWithHeader title (no flex-row chrome, just a name above the card
 * body). Tight enough that mt-pad-xs (4px all-axis) would visually
 * over-pad it — keep the asymmetric 2px/4px shape.
 * --------------------------------------------------------------------- */

.cb-card-with-header-title {
  padding: 2px 4px;
}

/* === CardCell header row =============================================== *
 * The flex band hosting the card title plus optional drag-handle / edit
 * / remove affordances. Sits at the top of CardCell when showHeader is
 * true. Same shape in all three CardCell branches.
 * --------------------------------------------------------------------- */

.cb-card-header-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 2px 4px;
}

/* === Drag handle ⠿ ==================================================== *
 * Six-dot grab handle on the left edge of CardCell's header row when
 * dnd is wired. Visible only in edit mode.
 * --------------------------------------------------------------------- */

.cb-drag-handle {
  cursor: grab;
  user-select: none;
  font-size: 13px;
  line-height: 1;
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  padding: 2px 4px;
  flex-shrink: 0;
}

/* === Mini chrome button (✎ edit / × remove) =========================== *
 * Small mono-typeset button used at the right edge of CardCell's header
 * row. Identical shape to the "Edit → inspect" / "Edit → reassign"
 * affordance below; the difference is a block-display + auto-margin
 * wrapper applied via the .is-block modifier.
 * --------------------------------------------------------------------- */

.cb-mini-button {
  padding: 3px 8px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-muted);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
}

.cb-mini-button.is-block {
  display: block;
  margin: 4px auto 0;
}

/* === Missing-instance caption ========================================== *
 * The italic small-print "instance 'X' no longer exists" line shown in
 * CardCell's missing-instance branch under the placeholder tile.
 * --------------------------------------------------------------------- */

.cb-missing-instance-caption {
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-style: italic;
  color: var(--mt-muted);
  padding: 4px 8px;
  text-align: center;
}

/* === CardCell drop-indicator wrapper =================================== *
 * CardCell's outer wrapper carries a `box-shadow: inset 3px 0 0 accent`
 * during drag-over to mark the drop target. State-driven (only painted
 * when dnd.isDragOver), so flows through --cb-card-drop-shadow.
 * --------------------------------------------------------------------- */

.cb-card-cell {
  box-shadow: var(--cb-card-drop-shadow, none);
}

/* === Warn-tinted error frame =========================================== *
 * Mono-font, warn-coloured callout used for two siblings: (a) the
 * card-validation error list shown when validateCard() returns errs, and
 * (b) the "Missing card" placeholder rendered by _renderCardCell when a
 * tab references a card id that no longer exists. Same warn ring + warn
 * text + small mono body — distinct messages, identical chrome.
 *
 * The 14px and 12px paddings from the pre-migration sites both round to
 * --mt-space-md (12px). The 2px shift in the validation-errors site is
 * inside the 19-SCREENSHOT-MANIFEST tolerance rubric.
 * --------------------------------------------------------------------- */

.cb-warn-frame {
  padding: var(--mt-space-md);
  border: 1px solid var(--mt-warn);
  border-radius: var(--m-radius-md, 8px);
  color: var(--mt-warn);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  line-height: var(--mt-text-xs-lh, 1.4);
}

.cb-warn-frame__heading {
  font-weight: var(--mt-weight-bold, 700);
  margin-bottom: var(--mt-space-xs, 4px);
}

/* === Action widget — anonymous / insufficient-role placeholder ========= *
 * Replaces the action button with a small lock-icon caption when the
 * viewer's role falls below the action's declared minRole.
 * Also worn by SignInPlaceholder (Phase 19-04b) — same dashed-frame
 * lock-icon shape signals "sign in / wrong role" identically in both
 * cases. SignInPlaceholder additionally wears .cb-sign-in-placeholder
 * as a no-op marker so anyone grepping for the component finds it.
 * --------------------------------------------------------------------- */

.cb-sign-in-placeholder {
  /* No styling — marker class; behaviour comes from .cb-action-locked
   * plus the JS hover handlers in card-builder.jsx::SignInPlaceholder. */
}

.cb-action-locked {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  color: var(--m-text-muted, var(--mt-dim));
  background: var(--m-bg, var(--mt-bg));
  border: 1px dashed var(--m-border, var(--mt-border));
  border-radius: 8px;
  padding: 8px;
}

.cb-action-locked__inner {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-direction: column;
  text-align: center;
}

.cb-action-locked__label {
  font-family: "JetBrains Mono", monospace;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  line-height: 1.3;
}

/* === Action widget — inline-result "clear" affordance ================== *
 * Tiny × top-right of an inline action result that re-runs the gateway
 * action on click.
 * --------------------------------------------------------------------- */

.cb-action-clear-result {
  position: absolute;
  top: 6px;
  right: 6px;
  font-family: "JetBrains Mono", monospace;
  font-size: 10px;
  padding: 2px 8px;
  background: transparent;
  color: var(--m-text-muted, var(--mt-muted));
  border: 1px solid var(--m-border, var(--mt-border));
  border-radius: 3px;
  cursor: pointer;
}

/* === Action widget — primary button override =========================== *
 * The button picks up `.manifold-button` shape from theme.css; this rule
 * applies the per-action typography that was previously inline.
 * --------------------------------------------------------------------- */

.cb-action-button {
  font-family: "JetBrains Mono", monospace;
  font-weight: 600;
  padding: 6px 14px;
  font-size: var(--cb-action-button-font-size, 13px);
}

/* ============================================================================
 * Phase 19 Plan 04c — CardTabRenderer / section chrome / SectionToolbarButton
 * ----------------------------------------------------------------------------
 * These classes host the section-layout chrome that wraps every dashboard
 * card cluster: the section frame, the section header bar (label + toolbar
 * buttons + HEADER pin badge), the inline section editor frame + close
 * button, the empty-section drop placeholder, the cell-hover drop overlay,
 * the loose-zone caption + grid, and the "+Add section" / "+Add a card here"
 * / SectionToolbarButton family of edit-mode buttons.
 *
 * CardTabRenderer is the wrapper every dashboard card walks through in the
 * read path, so any regression here shows on every card simultaneously. Keep
 * the structural classes pure (no per-state values) and route per-section /
 * per-state values through the --cb-section-* / --cb-drop-* / --cb-overlay-*
 * CSS custom properties so a single class set covers every section.
 * ============================================================================ */

/* === CardTabRenderer top-level loading message ======================== *
 * The "Loading cards…" placeholder shown while CardTabRenderer's initial
 * fetchTabSections / fetchTabLooseCards are in flight. One-shot, short-
 * lived; mono + muted + xs to match the rest of the inspector's loading
 * idiom (cb-binding-drift-empty / cb-header-placeholder are siblings).
 * --------------------------------------------------------------------- */

.cb-card-tab-loading {
  padding: var(--mt-space-lg, 16px);
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  line-height: var(--mt-text-xs-lh, 1.4);
}

/* === Empty-tab placeholder ============================================ *
 * Rendered when totalCards === 0 in read mode (no manage perms). Dashed
 * frame with centered "No cards on this tab yet." headline + optional
 * caller-supplied hint paragraph. Larger than the warn-frame chrome:
 * 24px vertical / 18px horizontal padding, 10px radius, larger surface
 * because the tab is otherwise empty.
 * --------------------------------------------------------------------- */

.cb-empty-tab-placeholder {
  padding: 24px 18px;
  margin: var(--mt-space-md, 14px);
  border: 1px dashed var(--mt-border-strong, var(--mt-border));
  border-radius: 10px;
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 12px);
  text-align: center;
}

.cb-empty-tab-placeholder__headline {
  font-weight: var(--mt-weight-bold, 700);
  margin-bottom: var(--mt-space-xs, 6px);
  color: var(--mt-dim);
}

/* === Section wrap (the per-section outer frame) ======================== *
 * Every section in CardTabRenderer is wrapped in a div whose three
 * aesthetic keys (margin-top, border, background) are state-driven by the
 * section's optional .style block (border on/off, marginTop number,
 * backgroundGradient css). State flows through --cb-section-* custom
 * props the JSX writes into the inline-style block; the sticky/zIndex
 * keys stay inline (state-driven layout, not aesthetic).
 *
 * Defaults are no-op so a section with no .style renders identical to
 * pre-migration. The .has-border modifier paints the actual border +
 * top-corner radius (border:none plus radius>0 would draw nothing but
 * still claim a paint layer — modifier-gated keeps the wrapper truly
 * transparent until border is requested).
 * --------------------------------------------------------------------- */

.cb-section-wrap {
  background: var(--cb-section-bg, transparent);
  /* Optional negative-or-positive offset between adjacent sections. Default
   * is 0 (no gap) to match pre-migration `marginTop: sMargin || undefined`
   * — when the JSX omits the custom property the cascade falls through. */
  margin-top: var(--cb-section-margin-top, 0);
}

.cb-section-wrap.has-border {
  border: 1px solid var(--cb-section-border-color, var(--mt-border));
  border-top-left-radius: var(--cb-section-radius, 0);
  border-top-right-radius: var(--cb-section-radius, 0);
}

/* overflow:hidden is only painted when the radius is non-zero (matches the
 * pre-migration `overflow: borderOn && sRadius > 0 ? 'hidden' : undefined`).
 * A 0-radius border doesn't need clipping; a positive-radius border does so
 * the inner grid content doesn't paint past the rounded corners. */
.cb-section-wrap.has-border.has-radius {
  overflow: hidden;
}

/* === Section header bar (label + toolbar) ============================ *
 * Flex row above each section: section label on the left, toolbar
 * buttons on the right (move-up / move-down / edit / delete). The label
 * typography is Space Grotesk uppercase tracked label-style; same shape
 * Karl uses elsewhere for section names. Italic when no label is set
 * (placeholder "(unnamed section)"); normal when labeled.
 * --------------------------------------------------------------------- */

.cb-section-header-bar {
  display: flex;
  align-items: center;
  gap: var(--mt-space-sm, 8px);
  padding: 10px 18px 4px;
}

.cb-section-header-label {
  font-family: "Space Grotesk", "Rubik", sans-serif;
  font-size: 13px;
  font-weight: var(--mt-weight-bold, 700);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--mt-dim);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cb-section-header-label.is-unlabeled {
  color: var(--mt-muted);
  font-style: italic;
}

/* HEADER pin badge sits inline with the label when the section is
 * pinned to the top of the tab. */
.cb-section-header-pin-badge {
  margin-left: var(--mt-space-sm, 8px);
  font-size: 9px;
  font-weight: var(--mt-weight-semibold, 600);
  padding: 1px 5px;
  border-radius: 3px;
  color: var(--mt-muted);
  border: 1px solid var(--mt-border);
}

/* === Section toolbar (right side of header bar) ====================== *
 * The flex row of move-up / move-down / edit / delete buttons. Idle
 * opacity 0.6, hover 1.0 — gives section chrome a "fade out unless you
 * mean it" affordance.
 * --------------------------------------------------------------------- */

.cb-section-toolbar {
  display: flex;
  gap: var(--mt-space-xs, 4px);
  opacity: 0.6;
}

.cb-section-toolbar:hover {
  opacity: 1;
}

/* === Section toolbar button (used by SectionToolbarButton component) === *
 * Subdued mono square button: 24×22, mono 12px, transparent bg, muted
 * color when disabled / dim when enabled, 1px border, 4px radius.
 * Matches the widget z-reorder buttons stylistically.
 * --------------------------------------------------------------------- */

.cb-section-toolbar-button {
  width: 24px;
  height: 22px;
  padding: 0;
  font-size: 12px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
}

.cb-section-toolbar-button:disabled {
  color: var(--mt-muted);
  cursor: not-allowed;
  opacity: 0.4;
}

/* === Inline section-editor frame ====================================== *
 * Shown beneath the section header bar when the user clicks ✎ on a
 * section. Hosts the SectionEditor.SectionSettingsForm. Card-elev
 * background, bordered, rounded, padded 12/14, with a tight top-right
 * close button.
 * --------------------------------------------------------------------- */

.cb-inline-section-editor {
  margin: 4px 18px 14px;
  padding: 12px 14px;
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-md, 8px);
  background: var(--mt-bg-elev);
  font-size: var(--mt-text-xs-size, 12px);
  position: relative;
}

.cb-inline-section-editor__close {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  line-height: 20px;
  padding: 0;
  font-size: 14px;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-sm, 4px);
  cursor: pointer;
}

/* === Section drop grid (the actual grid that hosts cards) ============= *
 * The grid <div> inside each section that lays out card cells. Padding
 * lands inline via `padded` (currently 14px); migrated by routing the
 * value through --cb-section-grid-pad so the class can apply it without
 * the audit flagging `padding` as a literal style key. Layout keys
 * (display, grid-template-columns, grid-auto-rows, gap, etc.) stay
 * inline — they're state-driven layout, not aesthetic.
 * --------------------------------------------------------------------- */

.cb-section-grid {
  padding: var(--cb-section-grid-pad, 14px);
}

/* === Cell-hover drop overlay ========================================== *
 * Painted at the cell the user is currently hovering with a dragged
 * card. Position/left/top/width/height/zIndex stay inline (state-driven
 * layout — allowed). The aesthetic ring + background tint + text color
 * + mono typography all come from this class; the colour token is the
 * accent token (always var(--mt-accent)) so no custom-prop wiring needed.
 * --------------------------------------------------------------------- */

.cb-cell-hover-overlay {
  border: 2px dashed var(--mt-accent);
  background: color-mix(in srgb, var(--mt-accent) 20%, transparent);
  border-radius: var(--m-radius-sm, 4px);
  color: var(--mt-accent);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  font-weight: var(--mt-weight-bold, 700);
}

/* === Empty-section drop placeholder =================================== *
 * Rendered in place of card cells when a section is empty AND the user
 * has edit perms. Dashed-frame drop target with an idle + hover state.
 * The "+Add a card here" button sits inside it (uses .cb-add-card-here).
 * State (idle vs hovered) flips border colour + background tint + text
 * colour via the .is-hovered modifier.
 * --------------------------------------------------------------------- */

.cb-empty-section-drop {
  margin: 4px 18px 14px;
  padding: 18px 14px;
  border: 1px dashed var(--mt-border);
  border-radius: var(--m-radius-md, 8px);
  background: transparent;
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  transition: border-color 80ms, background 80ms, color 80ms;
}

.cb-empty-section-drop.is-hovered {
  border-color: var(--mt-accent);
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
  color: var(--mt-accent);
}

/* === "+Add a card here" affordance inside empty-section drop ========== *
 * Subdued mono button. Same chrome family as SectionToolbarButton +
 * "+Add section": transparent bg, dim text, 1px border, small radius,
 * mono typography. 5/12 padding (slightly tighter than .cb-add-section).
 * --------------------------------------------------------------------- */

.cb-add-card-here {
  padding: 5px 12px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-sm, 4px);
  cursor: pointer;
}

/* === Loose-zone caption ============================================== *
 * Tiny italic subdued caption that introduces the loose-zone (cards
 * that live outside any section). Only shown in edit mode so users
 * know the unlabeled grid below is the "outside-sections" zone, not a
 * glitch.
 * --------------------------------------------------------------------- */

.cb-loose-zone-caption {
  padding: 10px 18px 4px;
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  font-style: italic;
  letter-spacing: 0.04em;
}

/* === "+Add section" affordance (bottom of CardTabRenderer) =========== *
 * Wrap div + dashed-frame button that mints a new section at the
 * bottom of the tab. Same family as .cb-add-card-here but uses a
 * dashed border + 6px radius + 6/14 padding to read as a slightly
 * heavier "add" affordance (matches the empty-section drop visually).
 * --------------------------------------------------------------------- */

.cb-add-section-wrap {
  padding: 4px 18px 18px;
  display: flex;
  justify-content: flex-start;
}

.cb-add-section {
  padding: 6px 14px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px dashed var(--mt-border);
  border-radius: var(--m-radius-md, 6px);
  cursor: pointer;
  letter-spacing: 0.03em;
}

/* ============================================================================
 * Phase 19 Plan 04d (2026-05-26). Catalog tree (CatalogTree / LeafRow /
 * ActionRow), PreviewPane chrome (header, empty-cell ghost, linked-cell
 * toolbar, diagnostic row), SeriesEditor rows, LabelIconPickerInline grid,
 * and the BrandInspectorForm / ClockInspectorForm mode-switch buttons get
 * structural classes here so the JSX drops its inline aesthetic-key sites.
 *
 * Hover affordances on catalog rows and the +Add series button move from
 * inline onMouseEnter/onMouseLeave handlers to :hover pseudo-classes —
 * same pattern 19-04c established for the section toolbar.
 * ============================================================================
 */

/* === CatalogTree left-pane wrapper =================================== *
 * The outer pane that hosts the search input, add-* buttons, and the
 * scrollable provider list. Right border + bg-deep so the picker reads
 * as a distinct rail from the preview area.
 * --------------------------------------------------------------------- */

.cb-catalog {
  border-right: 1px solid var(--mt-border);
  background: var(--mt-bg-deep);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.cb-catalog-loading {
  padding: var(--mt-space-md, 12px);
  color: var(--mt-muted);
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
}

.cb-catalog-search-bar {
  padding: 8px 10px;
  border-bottom: 1px solid var(--mt-border);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.cb-catalog-search-input {
  width: 100%;
  padding: 5px 8px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-sm, 4px);
  box-sizing: border-box;
}

/* === CatalogTree add-* buttons (Static text / Range picker / SVG / …) === *
 * Base class is the dashed-border / transparent-bg / dim-text shape; two
 * modifiers cover the accent variants ("+ Header widget" uses the accent
 * dashed style; "+ Service Table…" uses a filled-accent style).
 * --------------------------------------------------------------------- */

.cb-catalog-add-button {
  width: 100%;
  padding: 5px 8px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  font-weight: 600;
  background: transparent;
  color: var(--mt-dim);
  border: 1px dashed var(--mt-border);
  border-radius: var(--m-radius-sm, 4px);
  cursor: pointer;
  text-align: left;
}

.cb-catalog-add-button.is-accent-dashed {
  color: var(--mt-accent);
  border: 1px dashed color-mix(in srgb, var(--mt-accent) 33%, transparent);
}

.cb-catalog-add-button.is-accent-solid {
  background: color-mix(in srgb, var(--mt-accent) 6%, transparent);
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 40%, transparent);
}

/* "+ Header widget" submenu: 6-row sub-picker beneath the toggle */
.cb-catalog-header-picker {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 4px 4px 6px 12px;
}

.cb-catalog-header-picker-item {
  width: 100%;
  padding: 4px 8px;
  font-size: var(--mt-text-xs-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;
  text-align: left;
}

.cb-catalog-header-picker-item__name {
  font-weight: 600;
}

.cb-catalog-header-picker-item__desc {
  opacity: 0.6;
  margin-left: 8px;
  font-size: 10px;
}

/* === CatalogTree scrollable list ===================================== */

.cb-catalog-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 0;
}

/* === CatalogTree provider row (top-level expand/collapse header) ===== */

.cb-catalog-provider-row {
  padding: 5px 12px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  font-weight: 700;
  color: var(--mt-fg);
  cursor: pointer;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 6px;
}

.cb-catalog-provider-chev {
  color: var(--mt-muted);
  font-size: 9px;
  width: 8px;
}

.cb-catalog-provider-count {
  color: var(--mt-muted);
  font-size: 9px;
  font-weight: 400;
}

/* === CatalogTree Leaves/Actions sub-tab strip ======================== */

.cb-catalog-subtab-strip {
  display: flex;
  gap: 0;
  padding: 0 12px;
  border-bottom: 1px solid var(--mt-border);
  margin-bottom: 2px;
}

.cb-catalog-subtab {
  padding: 3px 8px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--mt-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  cursor: pointer;
  user-select: none;
}

.cb-catalog-subtab.is-active {
  color: var(--m-accent, var(--mt-accent));
  border-bottom: 2px solid var(--m-accent, var(--mt-accent));
}

/* === CatalogTree subsection row (nested expand/collapse header) ====== */

.cb-catalog-subsection-row {
  padding: 4px 12px 4px 28px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
  cursor: pointer;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 6px;
}

.cb-catalog-bulk-pill {
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 9px;
  font-weight: 700;
  font-family: var(--m-font-mono);
  letter-spacing: 0.03em;
  background: color-mix(in srgb, var(--mt-accent) 9%, transparent);
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 33%, transparent);
}

.cb-catalog-subsection-count {
  color: var(--mt-muted);
  font-size: 9px;
}

/* === Catalog row (LeafRow + ActionRow share base chrome) ============= *
 * Indent is state-driven (depends on tree depth), so it rides --cb-row-
 * indent. The :hover affordance moves from inline JS handlers to a CSS
 * :hover so the cascade owns it (matches 19-04c .cb-section-toolbar
 * pattern).
 * --------------------------------------------------------------------- */

.cb-catalog-row {
  padding: 3px 12px 3px var(--cb-row-indent, 28px);
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
  cursor: pointer;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 6px;
  background: transparent;
}

.cb-catalog-row:hover {
  background: color-mix(in srgb, var(--mt-accent) 9%, transparent);
}

.cb-catalog-row.is-static {
  cursor: default;
}

.cb-catalog-row__glyph {
  display: inline-block;
  width: 14px;
  text-align: center;
  color: var(--mt-muted);
  font-size: 10px;
  font-weight: 700;
}

.cb-catalog-row__name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cb-catalog-row__min-role {
  color: var(--mt-muted);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 4px;
  border-radius: 2px;
  border: 1px solid var(--mt-border);
}

.cb-catalog-row__unit {
  color: var(--mt-muted);
  font-size: 9px;
}

/* Frequency pill on LeafRow — colour rides --cb-freq-color so the
 * fast/live distinction can flip per leaf without per-state classes. */
.cb-catalog-row__freq {
  color: var(--cb-freq-color, var(--mt-muted));
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 4px;
  border-radius: 2px;
  border: 1px solid color-mix(in srgb, var(--cb-freq-color, var(--mt-muted)) 33%, transparent);
}

/* === PreviewPane outer wrapper ======================================= *
 * Centers the preview content vertically + horizontally so a narrow
 * card doesn't pool against the top-left of the preview area.
 * --------------------------------------------------------------------- */

.cb-preview-pane {
  flex: 1;
  min-height: 0;
  padding: var(--mt-space-lg, 16px);
  overflow: auto;
  background: var(--mt-bg);
  display: flex;
  flex-direction: column;
  gap: 8px;
  justify-content: center;
}

.cb-preview-pane.is-dragging {
  user-select: none;
}

.cb-preview-header {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  margin-bottom: 4px;
  display: flex;
  gap: 12px;
}

.cb-preview-header__status {
  font-weight: 700;
  color: var(--cb-status-color, var(--mt-muted));
}

/* PreviewPane grid frame — dashed-border / radius / padding are static.
 * Background flows through --cb-preview-grid-bg (per-card gradient or
 * token); grid-template-columns / width / gridAutoRows stay inline
 * (state-driven layout, allowed). Default falls back to bg-elev so a
 * card with no background prop paints the same as pre-migration. */
.cb-preview-grid {
  border: 1px dashed var(--mt-border);
  border-radius: 10px;
  padding: 6px;
  display: grid;
  gap: 6px;
  align-self: center;
  position: relative;
  background: var(--cb-preview-grid-bg, var(--mt-bg-elev));
}

.cb-preview-empty-cell {
  border: 1px dashed var(--mt-border);
  border-radius: var(--m-radius-md, 6px);
  background: color-mix(in srgb, var(--mt-bg-deep) 33%, transparent);
  pointer-events: none;
}

/* Per-cell gradient backdrop (rendered behind widgets at the same cell).
 * Background rides --cb-cell-grad-bg from the JSX; pointer-events:none +
 * zIndex:0 so it never intercepts widget interactions. */
.cb-preview-cell-grad {
  background: var(--cb-cell-grad-bg, transparent);
  border-radius: var(--m-radius-md, 6px);
  pointer-events: none;
  z-index: 0;
}

/* Per-widget wrapper inside PreviewPane. Background / radius / border /
 * shadow are all state-driven (PiP vs transparent vs normal; dragging;
 * selected); they ride --cb-widget-* custom props the JSX writes. The
 * structural pointer-events / position keys stay on the class. */
.cb-preview-widget {
  pointer-events: auto;
  position: relative;
  background: var(--cb-widget-bg, var(--mt-bg));
  border-radius: var(--cb-widget-radius, var(--m-radius-md, 6px));
  border: var(--cb-widget-border, none);
  box-shadow: var(--cb-widget-shadow, none);
}

/* contentScale wrapper (mirrors live CardRenderer behavior). Border only
 * paints when the modifier is present (--cb-content-border-color set). */
.cb-preview-content-scale {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}

.cb-preview-content-scale.has-border {
  border: 1px solid var(--cb-content-border-color, currentColor);
  border-radius: var(--m-radius-md, 6px);
}

/* Resize handles — only mount on the selected widget. Three variants
 * cover right edge / bottom edge / bottom-right corner. */
.cb-preview-resize-handle {
  position: absolute;
  background: color-mix(in srgb, var(--mt-accent) 50%, transparent);
}

.cb-preview-resize-handle.is-r {
  top: 30%;
  bottom: 30%;
  right: 0;
  width: 8px;
  cursor: ew-resize;
  border-radius: 3px 0 0 3px;
}

.cb-preview-resize-handle.is-b {
  left: 30%;
  right: 30%;
  bottom: 0;
  height: 8px;
  cursor: ns-resize;
  border-radius: 3px 3px 0 0;
}

.cb-preview-resize-handle.is-br {
  right: 0;
  bottom: 0;
  width: 14px;
  height: 14px;
  cursor: nwse-resize;
  background: var(--mt-accent);
  border-radius: 4px 0 0 0;
}

.cb-preview-repeat-msg {
  padding: var(--mt-space-md, 12px);
  color: var(--mt-muted);
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
}

/* PreviewPane linked-cell toolbar (cut / copy / paste / delete) ====== *
 * Three variants: default linked-cell toolbar, placement-mode banner
 * (live-accent ring), clipboard-ready fallback. They share padding /
 * radius / mono-typography so the surface reads consistent.
 * --------------------------------------------------------------------- */

.cb-preview-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-md, 6px);
}

.cb-preview-toolbar.is-placement {
  background: color-mix(in srgb, var(--mt-live) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-live) 33%, transparent);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  color: var(--mt-live);
}

.cb-preview-toolbar-label {
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.cb-preview-toolbar-hint {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
}

.cb-preview-toolbar-clipboard-label {
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  color: var(--mt-dim);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.cb-preview-toolbar-link-label {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
  cursor: pointer;
}

.cb-preview-toolbar-link-label.is-linked {
  color: var(--mt-live);
}

.cb-preview-toolbar-link-text {
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* === Linked-cell toolbar buttons ====================================== *
 * Base shape shared across cut/copy/paste/delete/cancel/discard. Modifier
 * classes flip background + colour + border for accent/error/dim/dim-soft.
 * Disabled state for Paste uses :disabled.
 * --------------------------------------------------------------------- */

.cb-preview-toolbar-btn {
  padding: 5px 10px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-sm, 4px);
  cursor: pointer;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.cb-preview-toolbar-btn.is-accent {
  background: color-mix(in srgb, var(--mt-accent) 15%, transparent);
  color: var(--mt-accent);
  border-color: color-mix(in srgb, var(--mt-accent) 33%, transparent);
}

.cb-preview-toolbar-btn.is-error {
  background: color-mix(in srgb, var(--mt-error) 8%, transparent);
  color: var(--mt-error);
  border-color: color-mix(in srgb, var(--mt-error) 33%, transparent);
}

.cb-preview-toolbar-btn:disabled {
  background: transparent;
  color: var(--mt-muted);
  border-color: var(--mt-border);
  cursor: not-allowed;
  opacity: 0.5;
}

/* === PreviewPane diagnostic row (cell / widgets / leaves / renderers) === */

.cb-preview-diag-row {
  display: grid;
  grid-template-columns: auto auto auto 1fr;
  gap: 24px;
  padding: 10px 12px;
  margin-top: 6px;
  border-top: 1px solid var(--mt-border);
  width: 100%;
  max-width: 100%;
}

.cb-preview-diag-block {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.cb-preview-diag-key {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  color: var(--mt-muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.cb-preview-diag-value {
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  color: var(--mt-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* === SeriesEditor (multi-series widget row list) ===================== */

.cb-series-empty-msg {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
}

.cb-series-row {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 6px 8px;
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-sm, 4px);
  background: var(--mt-bg);
}

.cb-series-row__head {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* Color swatch on each series row. Background rides --cb-series-swatch-bg
 * (resolved palette colour, per-series). 10×10 square, 2px radius. */
.cb-series-row__swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex-shrink: 0;
  background: var(--cb-series-swatch-bg, var(--mt-accent));
}

/* The remove-× button on each series row. Inherits the iconBtn() shape
 * (mono / muted / pill) but pins padding + fontWeight so the series-row
 * version is slightly tighter than the inspector default. */
.cb-series-row__remove {
  width: auto;
  padding: 1px 6px;
  font-weight: 700;
}

/* Zebra-stripe row background (service-table repeat mode). State-driven
 * paint colour rides --cb-zebra-paint; pointer-events:none + zIndex:0
 * so it never intercepts widget clicks. */
.cb-preview-zebra {
  background: var(--cb-zebra-paint, transparent);
  z-index: 0;
  pointer-events: none;
}

.cb-series-row__name {
  flex: 1;
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cb-series-checkbox-row {
  display: flex;
  gap: 4px;
  align-items: center;
  font-size: 10px;
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
}

.cb-series-fill-opacity-row {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 10px;
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
}

.cb-series-params-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 6px;
  margin-left: 12px;
}

/* "+ Add series" button — hover affordance via :hover not JS handlers. */
.cb-series-add-btn {
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  padding: 7px 12px;
  margin-top: 6px;
  background: transparent;
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 33%, transparent);
  border-radius: 3px;
  cursor: pointer;
  text-align: center;
  transition: background 120ms linear, border-color 120ms linear;
}

.cb-series-add-btn:hover {
  background: color-mix(in srgb, var(--mt-accent) 8%, transparent);
  border-color: var(--mt-accent);
}

/* === LabelIconPickerInline (label-icon picker grid) ================== */

.cb-icon-picker-label {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  margin-top: 6px;
  margin-bottom: 3px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.cb-icon-picker-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  padding: 5px;
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-sm, 4px);
  background: var(--mt-bg-deep);
  max-height: 144px;
  overflow-y: auto;
}

/* Icon-picker button (one square per icon). Selected variant has accent
 * tint + accent border; unselected is transparent + border. */
.cb-icon-btn {
  width: 24px;
  height: 24px;
  padding: 0;
  cursor: pointer;
  background: transparent;
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--mt-dim);
}

.cb-icon-btn.is-selected {
  background: color-mix(in srgb, var(--mt-accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--mt-accent) 53%, transparent);
  color: var(--mt-accent);
}

.cb-icon-btn.is-none {
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  font-size: 9px;
}

.cb-icon-picker-break {
  flex-basis: 100%;
  height: 0;
}

.cb-icon-picker-imported-header {
  font-family: var(--m-font-mono);
  font-size: 8px;
  color: var(--mt-muted);
  width: 100%;
  margin-top: 4px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.7;
  display: flex;
  align-items: center;
  gap: 6px;
}

.cb-icon-picker-upload-btn {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  background: color-mix(in srgb, var(--mt-accent) 8%, transparent);
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 33%, transparent);
  border-radius: 3px;
  padding: 1px 6px;
  cursor: pointer;
}

.cb-icon-picker-upload-btn:disabled {
  background: transparent;
  color: var(--mt-muted);
  border-color: var(--mt-border);
  cursor: default;
}

.cb-icon-imported-thumb {
  width: 14px;
  height: 14px;
  display: block;
  object-fit: contain;
}

/* Overlay × on imported assets — flips to error colour on hover so users
 * realise it's destructive. */
.cb-icon-imported-delete {
  position: absolute;
  top: -4px;
  right: -4px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--mt-bg);
  color: var(--mt-muted);
  border: 1px solid var(--mt-border);
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  line-height: 10px;
  text-align: center;
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.cb-icon-imported-delete:hover {
  color: var(--mt-error);
  border-color: var(--mt-error);
}

/* === BrandInspectorForm / ClockInspectorForm shared mini-label ======= *
 * Tiny uppercase mono header used above every prop field in those two
 * forms ("Mode", "Text", "Format", "Timezone", "HTML").
 * --------------------------------------------------------------------- */

.cb-inspector-mini-label {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  color: var(--mt-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 4px;
  margin-top: 8px;
}

.cb-inspector-mode-btn {
  flex: 1;
  padding: 6px 10px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  cursor: pointer;
  border-radius: var(--m-radius-sm, 4px);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
}

.cb-inspector-mode-btn.is-active {
  background: color-mix(in srgb, var(--mt-accent) 15%, transparent);
  color: var(--mt-accent);
  border-color: var(--mt-accent);
}

.cb-inspector-mode-checkbox-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  font-size: var(--mt-text-xs-size, 11px);
  font-family: var(--m-font-mono);
  color: var(--mt-fg);
  cursor: pointer;
}

/* === BrandInspector custom-html textarea =========================== *
 * Overrides the standard fieldInput() spread to make the textarea read
 * as a code block (mono, smaller text, larger min-height, vertical-only
 * resize). The two FORBIDDEN keys (fontSize / fontFamily already in
 * fieldInput via mfont) get baked into this class so the inline-style
 * block can drop them. */
.cb-inspector-html-textarea {
  width: 100%;
  box-sizing: border-box;
  min-height: 120px;
  resize: vertical;
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  line-height: 1.4;
}

/* === ClockInspector timezone input ================================= *
 * Just a width:100% + mono-family override on the standard fieldInput
 * shape so a timezone string ("America/New_York") fits comfortably. */
.cb-inspector-tz-input {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
}

/* === SeriesEditor per-row label input ============================== *
 * Tightens fieldInput's padding so the inline label override sits closer
 * to its neighbours in the per-series card. */
.cb-series-row__label-input {
  padding: 3px 6px;
  font-size: var(--mt-text-xs-size, 11px);
}

/* === Service-picker status messages =================================
 * Three states share the same mono / 10px / dim or warn / 6px-margin
 * shape: catalog-loading, auth-required, server-side load-errors. The
 * colour rides --cb-svc-msg-color so the same class covers both dim
 * (loading) and warn (auth/load-error) variants without per-state
 * duplication. */
.cb-svc-picker-msg {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--cb-svc-msg-color, var(--mt-dim));
  margin-top: 6px;
}

.cb-svc-picker-msg__errors {
  margin: 3px 0 0 14px;
  padding: 0;
}

/* === Section 5 (19-04e) — inspector / pickers / forms ============== *
 * Shared chrome for the icon-picker family (ServiceIconPickerField,
 * BundledIconPickerField, IconPickerField), ParameterPicker /
 * _InstancePickerInput, the _Rd*Badge cluster, SectionedInspector toggle
 * button, and LeafPicker header.
 *
 * Picker grid scroll area: shared frame for the four picker-grid scrolls
 * (bundled / brand / imported / service-icon). Padding + radius + dim
 * bg + 1px border are the same 6px+4px+--mt-bg-deep+--mt-border shape;
 * vertical max-height (240 vs 280) flows through --cb-picker-grid-max so
 * one class skins both heights.
 * --------------------------------------------------------------------- */

.cb-picker-scroll {
  max-height: var(--cb-picker-grid-max, 280px);
  overflow-y: auto;
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  padding: 6px;
  background: var(--mt-bg-deep);
}

/* Imported-tab variant — no max-height on the outer (the inner has its
 * own scroll); same border/radius/padding/bg. */
.cb-picker-frame {
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  padding: 6px;
  background: var(--mt-bg-deep);
}

/* Filter row above a picker scroll (filter input + optional Clear).
 * Pre-migration: display:flex, gap:6, alignItems:center, marginBottom:6.
 * 3 sites — Service/Bundled/IconPicker. */
.cb-picker-filter-row {
  display: flex;
  gap: 6px;
  align-items: center;
  margin-bottom: 6px;
}

/* Filter input override on top of fieldInput(t): tighter padding +
 * smaller text. Mounts on the type=text inside .cb-picker-filter-row. */
.cb-picker-filter-input {
  flex: 1;
  padding: 3px 6px;
  font-size: 10px;
}

/* Clear button beside the filter input — sits on top of btnGhost(t)
 * spread; just dials padding + font-size down. */
.cb-picker-clear-btn {
  padding: 3px 8px;
  font-size: 10px;
}

/* Uppercase mono category header above each grouped section in a
 * picker (Bundled, Brand, Imported, etc.). The {...mfont} spread on
 * the JSX still wins for font-family if mfont differs across themes;
 * but in practice mfont resolves to var(--m-font-mono) everywhere. */
.cb-picker-category {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 4px;
  margin-top: 2px;
}

/* Wrap around a picker category + its tile row — pre-migration
 * marginBottom:8 to gap successive groups. */
.cb-picker-category-wrap {
  margin-bottom: 8px;
}

/* Inline count "(12)" sitting after a category title. Dim text, no
 * uppercase, normal weight (overrides .cb-picker-category bold). */
.cb-picker-category-count {
  color: var(--mt-dim);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}

/* Tile row inside a picker — wrap, gap 4px. */
.cb-picker-tile-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* Mono dim status row (loading… / no source / empty caption). 10px
 * mono, --mt-dim, no padding. Pre-migration sites: ParameterPicker
 * loading/empty rows, _InstancePickerInput "no instances configured",
 * picker "loading /icons/ list…" / "loading /feed/svg/ uploads…". */
.cb-picker-status {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-dim);
  padding: 4px 0;
}

/* Italic-muted variant — used by the "no icons match …" emptyState
 * inside a picker frame. Adds padding:8 and italic. */
.cb-picker-empty {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  font-style: italic;
  padding: 8px;
}

/* Centered-loading variant of the empty state (IconPickerField
 * Imported tab "Loading…" + the empty-tab onboarding prose). */
.cb-picker-empty-centered {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  padding: 8px;
  text-align: center;
}

/* Larger empty-prose variant with line-height for the "Nothing
 * imported yet" onboarding block. */
.cb-picker-empty-prose {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  padding: 8px;
  text-align: center;
  line-height: 1.6;
}

/* === IconPickerField — tab strip ==================================== *
 * The 5-tab strip (Bundled / Brand / Imported / URL / Inline) at top
 * of the picker. Each tab inherits segButton(t, active) shape and
 * adds padding + font-size. */
.cb-icon-tab-strip {
  display: flex;
  gap: 4px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}

/* Per-tab override on top of segButton — smaller padding + 10px text. */
.cb-icon-tab-btn {
  padding: 4px 10px;
  font-size: 10px;
}

/* Clear-everything button at the right end of the tab strip. */
.cb-icon-tab-clear {
  padding: 4px 8px;
  font-size: 10px;
  margin-left: auto;
}

/* Imported-tab header bar (uppercase title + upload + refresh).
 * Same vocabulary as .cb-picker-category but adds flex layout +
 * marginBottom:6 (vs 4) so the controls sit on one row with the
 * title. */
.cb-icon-imported-header {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 6px;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Flex spacer between title and the upload+refresh buttons in the
 * header bar. Replaces inline <span style={{flex:1}}/>. */
.cb-icon-imported-spacer {
  flex: 1;
}

/* Upload button in the Imported-tab header. Two-state via .is-busy
 * modifier: idle = accent-tinted; busy = transparent + muted text +
 * neutral border + default cursor. */
.cb-icon-upload-btn {
  background: color-mix(in srgb, var(--mt-accent) 8%, transparent);
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 35%, transparent);
  border-radius: 3px;
  padding: 2px 6px;
  cursor: pointer;
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.cb-icon-upload-btn.is-busy {
  background: transparent;
  color: var(--mt-muted);
  border-color: var(--mt-border);
  cursor: default;
}

/* Refresh button — muted dim text, neutral border, smaller font. */
.cb-icon-refresh-btn {
  background: transparent;
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  padding: 2px 6px;
  cursor: pointer;
  color: var(--mt-dim);
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 600;
}

/* Per-thumb override on top of thumb(selected): tighten padding +
 * fix to 38×38 (used by Imported + Brand grids). */
.cb-icon-thumb-fixed {
  padding: 4px;
  width: 38px;
  height: 38px;
}

/* Inner scroll wrapper inside the Imported tab — fixed max-height +
 * overflow so the tile grid scrolls without taking over the panel. */
.cb-icon-imported-scroll {
  max-height: 240px;
  overflow-y: auto;
}

/* Import-result message line — colour rides --cb-icon-msg-color so
 * the same class covers warn (fail) and live (ok) variants. */
.cb-icon-msg {
  font-family: var(--m-font-mono);
  font-size: 9px;
  margin-top: 6px;
  color: var(--cb-icon-msg-color, var(--mt-muted));
}

/* Status bar at the bottom of IconPickerField — "icon: X" / "url: …"
 * / "inline (N chars)" / "no source set". */
.cb-icon-status-bar {
  margin-top: 6px;
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  padding: 4px 6px;
  border-top: 1px solid var(--mt-border);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

/* Inline-flex pill inside the status bar showing the picked bundled
 * icon + its name. */
.cb-icon-status-icon-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* Dim text inside status bar. */
.cb-icon-status-dim {
  color: var(--mt-dim);
}

/* Word-break variant for long URLs in status bar. */
.cb-icon-status-url {
  color: var(--mt-dim);
  word-break: break-all;
}

/* "no source set" italic placeholder. */
.cb-icon-status-empty {
  font-style: italic;
}

/* URL-tab prose block — multi-line muted help text with bold accents. */
.cb-icon-url-help {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  margin-top: 6px;
  line-height: 1.5;
}

.cb-icon-url-help > div {
  margin-bottom: 3px;
}

.cb-icon-url-help > div:last-child {
  margin-bottom: 0;
}

/* Inline-tab prose (smaller, no per-line gap). */
.cb-icon-inline-help {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  margin-top: 3px;
  line-height: 1.4;
}

/* === IconPickerField input overrides ================================ *
 * Filter / URL / inline-textarea inputs each spread fieldInput(t) and
 * then add a couple of tweaks (smaller padding, mono font on the
 * textarea, smaller line-height). One class per shape so the JSX
 * inline-style block can drop the FORBIDDEN keys. */

/* "Filter brand icons…" / "filter…" 3px/6px input. Mounts on top of
 * .cb-picker-filter-input shape but adds margin-bottom:6 — used when
 * the input is standalone (not inside .cb-picker-filter-row). */
.cb-icon-standalone-filter {
  padding: 3px 6px;
  font-size: 10px;
  margin-bottom: 6px;
}

/* URL-tab input — 4/6 padding. */
.cb-icon-url-input {
  padding: 4px 6px;
}

/* Inline-tab textarea — mono / 10px / 1.4 line-height / 80px min. */
.cb-icon-inline-textarea {
  min-height: 80px;
  font-family: var(--m-font-mono);
  font-size: 10px;
  line-height: 1.4;
}

/* Brand-tab grid scroll — same .cb-picker-scroll bones plus inline
 * flex/wrap because the brand tab paints thumbnails directly (no
 * grouped subsections). */
.cb-picker-scroll.is-flat {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* Inputs with mono+fontSize:11 override on top of fieldInput(t).
 * Used by ServiceElementPicker's Custom-icon-name / Custom-icon-URL /
 * audit-template inputs at 6500-6600 + the wide-width variant. */
.cb-mono-input {
  font-family: var(--m-font-mono);
  font-size: 11px;
}

/* Wide variant (sets width:100% + box-sizing). Used by the audit
 * template input. */
.cb-mono-input.is-wide {
  width: 100%;
  box-sizing: border-box;
}

/* === ServiceElementPicker checkbox rows ============================= *
 * "Icon only — hide the text label" / "record an audit entry every
 * time this link is clicked". Same shape: flex row, 6px gap, 11px
 * mono text, dim colour, marginTop:6. */
.cb-mini-checkbox-row {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
  margin-top: 6px;
}

/* === SectionedInspector — section toggle button ===================== *
 * The "DATA ▼ 7" / "FORMAT ▶ 3" disclosure buttons in the sectioned
 * inspector view. Two states via .is-open: closed (transparent bg,
 * dim text), open (accent-tinted bg, fg text). */
.cb-inspector-section-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 5px 8px;
  font-size: 10px;
  font-weight: 700;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0;
}

.cb-inspector-section-toggle.is-open {
  background: color-mix(in srgb, var(--mt-accent) 8%, transparent);
  color: var(--mt-fg);
  margin-bottom: 6px;
}

/* Right-side count + chevron inside the toggle. */
.cb-inspector-section-count {
  font-size: 9px;
  color: var(--mt-muted);
}

/* === _Rd*Badge cluster (Phase 26 Plan 02 status badges) =============
 * Pending / Unreachable / Missing / Stale badges painted alongside
 * rest_discover-backed cards. Same 11px mono base; per-variant overrides
 * for colour, alignment, and padding. */
.cb-rd-badge {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
  padding: 4px 8px;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Unreachable = orange. Last-known-good payload is still available;
 * card is degraded, not dead. */
.cb-rd-badge.is-unreachable {
  color: var(--mt-warn);
}

/* Missing = centered italic (vanished row caption). */
.cb-rd-badge.is-missing {
  display: block;
  text-align: center;
  font-style: italic;
}

/* Spinner dot inside the pending badge. */
.cb-rd-spinner {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 2px solid var(--mt-muted);
  border-top-color: var(--mt-accent);
  animation: spin 1s linear infinite;
}

/* Solid colored dot inside the unreachable badge. */
.cb-rd-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--mt-warn);
  flex-shrink: 0;
}

/* Inline "stale · 5m ago" pill overlaid on a value tile. */
.cb-rd-stale-pill {
  display: inline-block;
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 600;
  color: var(--mt-muted);
  background: color-mix(in srgb, var(--mt-warn) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-warn) 35%, transparent);
  border-radius: 3px;
  padding: 1px 4px;
  margin-left: 4px;
  vertical-align: middle;
}

/* === ParameterPicker / _InstancePickerInput ========================= *
 * Uppercase mono parameter label above the select. 10px / muted /
 * tracked. Same shape used twice (once per component). */
.cb-param-label {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Wrap around the label + select + status messages. 4px gap, marginTop:6. */
.cb-param-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 6px;
}

/* Status line below the parameter select (italic for loading/empty;
 * non-italic for "pick instance to continue"). Colour rides
 * --cb-param-msg-color so warn variant (ghost-removed) and default
 * muted variant share one class. */
.cb-param-status {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--cb-param-msg-color, var(--mt-muted));
}

.cb-param-status.is-italic {
  font-style: italic;
}

/* Marker class on the parameter <select>; only the .is-ghost modifier
 * paints aesthetics (warn colour + warn border) when the saved value
 * is no longer in the current valid-options list. Mounting it on top
 * of fieldInput(t) so the base shape stays consistent with sibling
 * inputs. */
.cb-param-select.is-ghost {
  color: var(--mt-warn);
  border-color: var(--mt-warn);
}

/* === LeafPicker — header band + search summary =====================
 * Title row (sectionLabel spread + accent override + bottom border)
 * and search summary line. */
.cb-leaf-picker-header {
  padding: 12px 14px 6px 14px;
  border-bottom: 1px solid var(--mt-border);
}

.cb-leaf-picker-subtitle {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  margin-top: 3px;
  letter-spacing: 0.04em;
}

.cb-leaf-picker-search-row {
  padding: 10px 14px 8px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.cb-leaf-picker-summary {
  display: flex;
  justify-content: space-between;
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  padding-top: 2px;
}

/* LeafPickerEmpty (Leaves rail mode with no open caller). Soft-muted
 * onboarding panel — centered, mono, generous line-height. */
.cb-leaf-picker-empty {
  padding: 28px 20px;
  text-align: center;
  font-family: var(--m-font-mono);
  font-size: 12px;
  color: var(--mt-muted);
  line-height: 1.7;
}

.cb-leaf-picker-empty__title {
  margin-bottom: 10px;
  color: var(--mt-accent);
}

/* Per-group leaf-row wrap inside the LeafPicker body. Border-left
 * accent for indented group rows. */
.cb-leaf-picker-group-body {
  padding-left: 18px;
  margin-left: 6px;
  border-left: 1px solid color-mix(in srgb, var(--mt-accent) 19%, transparent);
  padding-top: 3px;
  padding-bottom: 6px;
}

/* Group header inside LeafPicker. */
.cb-leaf-picker-group-row {
  display: grid;
  grid-template-columns: 12px 1fr auto;
  gap: 8px;
  align-items: center;
  padding: 7px 8px;
  cursor: pointer;
  border-bottom: 1px solid var(--mt-border);
  transition: background 120ms linear;
}

.cb-leaf-picker-group-wrap {
  margin-bottom: 2px;
}

/* Group chevron — colour flips by .is-open. */
.cb-leaf-picker-group-chev {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
  text-align: center;
}

.cb-leaf-picker-group-chev.is-open {
  color: var(--mt-accent);
}

/* Group name (uppercase tracked mono). */
.cb-leaf-picker-group-name {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  color: var(--mt-fg);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* Right-side count pill. */
.cb-leaf-picker-group-count {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  background: var(--mt-bg-deep);
  border: 1px solid var(--mt-border);
  border-radius: 2px;
  padding: 1px 6px;
  min-width: 24px;
  text-align: center;
}

/* RailFrame outer (background + left-border separator from preview).
 * Layout keys (display:grid, grid-template-rows, min-height) stay inline
 * since they're structural — the class hosts only the FORBIDDEN background
 * + border-left. */
.cb-rail-frame {
  background: var(--mt-bg);
  border-left: 1px solid var(--mt-border);
}

/* RailFrame tab strip — bottom border + elevated bg. */
.cb-rail-tab-strip {
  border-bottom: 1px solid var(--mt-border);
  background: var(--mt-bg-elev);
}

/* SectionedInspector advanced-disclosure body wrapper — flex column with
 * 10px gap + small bottom margin. */
.cb-inspector-section-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 6px;
}

/* Per-leaf row inside LeafPicker. Layout (grid template + gap) stays
 * inline since it's structural; this class hosts the cursor / padding /
 * transition + the highlight-and-selection state via custom-prop hooks. */
.cb-leaf-picker-row {
  padding: 5px 8px;
  border-radius: 2px;
  transition: background 120ms linear;
}

/* Status hint inside the LeafPicker body for "No leaves match …" /
 * "No compatible leaves" empty states. */
.cb-leaf-picker-empty-state {
  padding: 20px 12px;
  text-align: center;
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
}

/* Bold accent inside the search-summary line ("showing X of Y"). */
.cb-leaf-picker-summary-strong {
  color: var(--mt-fg);
}

/* LeafPicker body — scrollable group list container. */
.cb-leaf-picker-body {
  overflow: auto;
  padding: 0 6px 10px 6px;
}

/* Per-leaf clickable row — wrapper around name + parameterized-badge +
 * kind chip + add/remove action. Hover state painted via JS handlers
 * (different colour per highlighted-state). Border + background are
 * state-driven via inline custom-prop hatch so the highlighted-and-not
 * cases share one class. */
.cb-leaf-picker-leaf-row {
  padding: 5px 8px;
  border-radius: 2px;
  border: 1px solid var(--cb-leaf-row-border, transparent);
  background: var(--cb-leaf-row-bg, transparent);
  transition: background 120ms linear;
}

/* Leaf name typography — mono, 12px, fg by default, muted when added.
 * Multi-line clamp + line-height stay inline since they're structural
 * (WebkitLineClamp / WebkitBoxOrient are ALLOWED display-related keys
 * and don't trigger the audit). */
.cb-leaf-picker-leaf-name {
  font-family: var(--m-font-mono);
  font-size: 12px;
  color: var(--cb-leaf-name-color, var(--mt-fg));
}

/* Inline muted "subsection · " prefix inside the leaf name. */
.cb-leaf-picker-leaf-sub {
  color: var(--mt-muted);
}

/* Parameterized-leaf badge {·} — small bordered pill in muted colour
 * with 0.85 opacity. */
.cb-leaf-picker-param-badge {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  border: 1px solid var(--mt-muted);
  border-radius: 3px;
  padding: 0 4px;
  margin-left: 6px;
}

/* LeafPicker footer — Done button row + selection count. Border-top,
 * elevated bg, 10/14 padding, two-column grid. */
.cb-leaf-picker-footer {
  border-top: 1px solid var(--mt-border);
  padding: 10px 14px;
  background: var(--mt-bg-elev);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  align-items: center;
}

/* Status line inside the footer ("N selected · keep adding, or click Done"). */
.cb-leaf-picker-footer-msg {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
}

/* Accent bold for the count number inside the footer message. */
.cb-leaf-picker-footer-strong {
  color: var(--mt-accent);
}

/* ════════════════════════════════════════════════════════════════════
 * Plan 19-04f — Section 6: leaf-picker + preset modal chrome
 * (lines 8200-9500 of card-builder.jsx)
 *
 * Covers: ScopePicker (grouped checkbox panels), PresetModalShell
 * (sizing/flex chrome inside the .manifold-modal-* className stack
 * which D-19-02 swaps to <Modal> in 19-09 — class names stay),
 * SavePresetModal (name input + scope picker form), ApplyPresetPreview
 * (Before/After scaled card preview), ManagePresetsModal (list with
 * inline rename / JSON expander / delete), StylePresetsSection (three-
 * state reveal), and the InspectorPane edges within band (card-frame
 * section wrapper, gradient preview, row-alignment buttons, visibility
 * checkbox, widget list rows). Reuses existing .cb-mini-label-style
 * vocabulary where possible. New classes scoped .cb-* per 19-04 family.
 * ════════════════════════════════════════════════════════════════════ */

/* ── ScopePicker (SavePresetModal embed) ─────────────────────────── */

/* Outer vertical stack of group panels. mono font carried in. */
.cb-scope-picker {
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-family: var(--m-font-mono);
}

/* One group panel = bordered box with elevated bg, padding for the
 * header row + key rows inside. */
.cb-scope-group {
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  padding: 8px 10px;
  background: var(--mt-bg-elev);
}

/* Header row: three-state checkbox + label + on/total count.
 * Border-bottom separates it from key rows below. */
.cb-scope-group__header {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  font-weight: 700;
  color: var(--mt-fg);
  padding-bottom: 6px;
  border-bottom: 1px solid var(--mt-border);
  margin-bottom: 8px;
}

/* Native checkbox styled with the accent token. Used for both group
 * header (three-state) and per-key rows. */
.cb-scope-checkbox {
  accent-color: var(--mt-accent);
  cursor: pointer;
}

/* Right-side "3/7" pill in the group header. tabular-nums for stable
 * alignment as counts change. */
.cb-scope-group__count {
  color: var(--mt-muted);
  font-size: 10px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

/* Body container for per-key rows (vertical stack inside a panel). */
.cb-scope-group__body {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* One key row = checkbox + label + level chip. Color flips with state
 * via .is-on modifier (fg) vs default (muted). */
.cb-scope-key {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  font-size: 12px;
  color: var(--mt-muted);
  padding: 2px 0;
}
.cb-scope-key.is-on {
  color: var(--mt-fg);
}

/* Right-aligned "level" chip ('card' / 'widget' / etc.) inside a key row. */
.cb-scope-key__level {
  font-size: 9px;
  color: var(--mt-muted);
  opacity: 0.7;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

/* Flex-1 label inside scope header / key rows. */
.cb-scope-flex-label {
  flex: 1;
}

/* ── PresetModalShell + form chrome ──────────────────────────────── */

/* The .manifold-modal box inside the shell needs flex column for
 * header / body / footer sizing. maxWidth varies per modal so it
 * stays inline (custom-property below). */
.cb-preset-modal-box {
  width: 92vw;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  max-width: var(--cb-preset-modal-max-width, 560px);
}

/* The modal body needs auto overflow + flex:1 so it scrolls inside
 * the height-capped shell while header/footer stay pinned. */
.cb-preset-modal-body {
  overflow: auto;
  flex: 1;
}

/* Disabled primary button in SavePresetModal (and friends): half-opacity
 * + not-allowed cursor without retouching the manifold-button class. */
.cb-preset-btn-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Vertical form stack inside SavePresetModal body. Larger gap (16) than
 * default since rows are bigger (label + input, scope picker block). */
.cb-preset-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Single column label + input pair (Name field). Smaller gap (6) than
 * the form rows themselves. */
.cb-preset-form-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* The "NAME" / "WHICH PROPERTIES TO INCLUDE" small uppercase header
 * above each form row. Mono, 10px, bold, muted. */
.cb-preset-form-label {
  font-family: var(--m-font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  font-weight: 700;
  color: var(--mt-muted);
}
.cb-preset-form-label.has-mb {
  margin-bottom: 8px;
}

/* Primary text input inside SavePresetModal. Sans-serif (matches the
 * preset-name surface elsewhere). */
.cb-preset-name-input {
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  padding: 8px 10px;
  font-family: var(--m-font-sans);
  font-size: 14px;
  color: var(--mt-fg);
}

/* ── ApplyPresetPreviewModal ─────────────────────────────────────── */

/* Two-column Before/After row inside the preview modal. */
.cb-preset-preview-row {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}

/* Each Before/After column (flex:1, vertical stack of label + preview). */
.cb-preset-preview-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* "BEFORE" / "AFTER" small heading above each preview. Same shape as
 * cb-preset-form-label but kept separate so the form-label can carry
 * its own .has-mb modifier without leaking. */
.cb-preset-preview-label {
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--mt-muted);
}

/* Scaled-card preview surface: 160px tall, rounded, bordered, deep bg. */
.cb-preset-preview-frame {
  height: 160px;
  overflow: hidden;
  border-radius: 4px;
  border: 1px solid var(--mt-border);
  background: var(--mt-bg-deep);
}

/* Inner scale wrapper (0.6× transform, 167% size to compensate). */
.cb-preset-preview-scale {
  transform: scale(0.6);
  transform-origin: top left;
  width: 167%;
  height: 167%;
}

/* ── ManagePresetsModal ──────────────────────────────────────────── */

/* Empty-state row when no presets saved. Centered, mono, italic. */
.cb-presets-empty {
  padding: 24px 12px;
  text-align: center;
  font-family: var(--m-font-mono);
  font-size: 12px;
  color: var(--mt-muted);
  font-style: italic;
}

/* Vertical list of preset rows. */
.cb-presets-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* One preset row = bordered card on elev bg. Inner header row + optional
 * expanded JSON block below. */
.cb-presets-row {
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  background: var(--mt-bg-elev);
}

/* Header inside a preset row: name button (or rename input) +
 * meta + JSON toggle + delete. */
.cb-presets-row__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
}

/* Inline rename input. Sans-serif, accent border for "you're editing"
 * affordance. flex:1 to fill the row. */
.cb-presets-rename-input {
  flex: 1;
  background: var(--mt-bg);
  border: 1px solid var(--mt-accent);
  border-radius: 3px;
  padding: 4px 6px;
  font-size: 13px;
  font-family: var(--m-font-sans);
  color: var(--mt-fg);
}

/* The clickable preset-name button (transparent, looks like text, but
 * carries cursor:text to advertise inline-rename on click). */
.cb-presets-name-btn {
  flex: 1;
  background: transparent;
  border: none;
  text-align: left;
  cursor: text;
  padding: 0;
  font-family: var(--m-font-sans);
  font-size: 12px;
  color: var(--mt-fg);
  font-weight: 400;
}

/* Meta line ("3 keys · 5/26/2026") in the preset row. mono, tabular. */
.cb-presets-meta {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  font-variant-numeric: tabular-nums;
}

/* Compact .manifold-button override for the JSON toggle. Same shape
 * as cb-preset-row-btn but kept separate so future styling can
 * differentiate "view" vs "destructive". */
.cb-preset-row-btn {
  padding: 4px 8px;
  font-size: 10px;
}

/* Destructive variant: error-color border + text. Applied as a
 * modifier on .cb-preset-row-btn. */
.cb-preset-row-btn.is-danger {
  color: var(--mt-error);
  border-color: var(--mt-error);
}

/* Expanded JSON block under a preset row. Inherits border-top so it
 * visually separates from the row head; max-height scroll for long
 * presets. */
.cb-presets-json {
  margin: 0;
  padding: 8px 10px;
  border-top: 1px solid var(--mt-border);
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 240px;
  overflow: auto;
}

/* ── StylePresetsSection (three-state reveal inside Inspector) ───── */

/* Outer vertical stack: button OR (select + buttons) row. */
.cb-style-presets {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Full-width Save-style button (zero-preset state). */
.cb-style-presets__save-full {
  width: 100%;
}

/* Compact horizontal row: select + Save another + Manage. */
.cb-style-presets__row {
  display: flex;
  gap: 6px;
  align-items: center;
}

/* The Apply <select>. Flex:1 to fill the row leaving room for the
 * adjacent buttons. */
.cb-style-presets__select {
  flex: 1;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  padding: 6px 8px;
  color: var(--mt-fg);
  font-family: var(--m-font-sans);
  font-size: 12px;
  cursor: pointer;
}

/* Compact "Save another" / "Manage" buttons inside the row. */
.cb-style-presets__btn {
  padding: 6px 10px;
}

/* ── InspectorPane edges within the 8200-9500 band ───────────────── */

/* Section wrapper above Card frame and Style presets blocks inside
 * the InspectorPane. mt + pt + border-top form the separator. */
.cb-inspector-block {
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--mt-border);
}

/* Gradient preview swatch inside Card frame > Background controls.
 * 42px tall, rounded, bordered, deep bg backplate. The actual gradient
 * paints into background-image via inline custom property because the
 * value is computed per-card from gradientToCss(). */
.cb-card-grad-preview {
  margin-top: 4px;
  height: 42px;
  border-radius: 6px;
  border: 1px solid var(--mt-border);
  background-color: var(--mt-bg-deep);
  background-image: var(--cb-grad-preview-image);
  background-size: var(--cb-grad-preview-size, auto);
}

/* Pin-to-row-height checkbox row inside Card frame. Flex row with
 * top-aligned checkbox + label. */
.cb-pin-row-label {
  display: flex;
  gap: 6px;
  align-items: flex-start;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
}

/* The checkbox itself gets margin-top:2 so it visually centers against
 * the wrapping label text. */
.cb-pin-row-checkbox {
  margin-top: 2px;
}

/* Row of left/center/right alignment buttons. */
.cb-align-row {
  display: flex;
  gap: 4px;
}

/* One alignment button. Reads --cb-align-bg / --cb-align-fg /
 * --cb-align-border to switch between active and inactive states. */
.cb-align-btn {
  flex: 1;
  padding: 4px 8px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: var(--cb-align-bg, transparent);
  color: var(--cb-align-fg, var(--mt-muted));
  border: 1px solid var(--cb-align-border, var(--mt-border));
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

/* Glyph (arrow icon) inside the alignment button. */
.cb-align-btn__glyph {
  font-size: 13px;
}

/* Visibility (requires-auth) checkbox row inside Card frame. Same
 * shape as cb-pin-row-label but center-aligned (no wrap). */
.cb-visibility-row {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
}

/* ── Widget list (above the bodyJsx) ─────────────────────────────── */

/* Scroll container for the list of widgets in the inspector. */
.cb-widget-list {
  max-height: 140px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* "(empty card)" placeholder line. */
.cb-widget-list__empty {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  padding: 4px 0;
}

/* One widget row: button (label + z-pill) + optional up/down arrows. */
.cb-widget-list__row {
  display: flex;
  align-items: stretch;
  gap: 2px;
  padding: 0;
}

/* Up/down arrow stack on the right side of a widget row. */
.cb-widget-list__arrows {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* Label text inside the widget-row button. Ellipsis on overflow. */
.cb-widget-list__label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Right-aligned "z3" indicator showing the widget's z-index. */
.cb-widget-list__z {
  color: var(--mt-muted);
  font-size: 9px;
  flex-shrink: 0;
}

/* "Widgets (N)" header row + filter pill. */
.cb-widget-list-header {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
}

/* Stretch spacer inside the header (pushes pill to right). */
.cb-widget-list-header__spacer {
  flex: 1;
}

/* Empty-inspector wrapper (when no widget selected). Border-left,
 * deep bg, padding, vertical stack. */
.cb-inspector-empty {
  border-left: 1px solid var(--mt-border);
  background: var(--mt-bg-deep);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  min-height: 0;
}

/* "Click a widget above…" hint inside the empty inspector. */
.cb-inspector-hint {
  color: var(--mt-muted);
  font-size: 11px;
  font-family: var(--m-font-mono);
  line-height: 1.6;
  margin-top: 8px;
}

/* ── Frequency advisory + Leaf header (inside bodyJsx, in band) ──── */

/* "Bind to leaf (optional)" sub-label row used inside text/hyperlink
 * renderer body. Uses subLabel() pattern but as a class to remove the
 * inline-style fragment. */
.cb-leaf-bind-label {
  margin-top: 6px;
  margin-bottom: 3px;
}

/* Mono input shared by text inputs in the renderer bodies (text-area,
 * URL field, link text). Differs from cb-mono-input by carrying
 * width:100% + box-sizing — sized for full-row form inputs. */
.cb-form-input-mono {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--m-font-mono);
  font-size: 11px;
}

/* Sans-serif full-width form input variant (link text field). */
.cb-form-input-sans {
  width: 100%;
  box-sizing: border-box;
  font-family: "Rubik", sans-serif;
  font-size: 12px;
}

/* Multi-line text area shared by text + hyperlink fallback. */
.cb-form-textarea {
  min-height: 60px;
  resize: vertical;
  font-family: "Rubik", sans-serif;
  font-size: 12px;
  line-height: 1.4;
}

/* Generic checkbox-row pattern: row of checkbox + label, mono dim
 * text. Used by Invisible / Audit-clicks / Pin-to-row / Visibility /
 * "use a gradient". */
.cb-form-check-row {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-dim);
}

/* Wrap-on-top variant (used when the label can span 2 lines). */
.cb-form-check-row.is-top {
  align-items: flex-start;
}

/* "use a gradient" row sits between Background sub-label and the
 * gradient block — needs 6px bottom margin to space cleanly. */
.cb-form-check-row.has-mb-sm {
  margin-bottom: 6px;
}

/* Audit-clicks template input wrapper (extra mt-top so it doesn't
 * touch the parent checkbox row). */
.cb-form-audit-template-wrap {
  margin-top: 6px;
}

/* "Chrome widget placeholder" hint line inside gear/auth_state/spacer
 * renderer info-only field. */
.cb-chrome-hint {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
  line-height: 1.5;
}

/* Action widget header: adapter accent + bullet + action-name. */
.cb-action-header {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-dim);
  line-height: 1.5;
}
.cb-action-header__adapter {
  color: var(--mt-accent);
  font-weight: 700;
}
.cb-action-header__name {
  color: var(--mt-fg);
}

/* Action widget parameter list — vertical stack with mt-top. */
.cb-action-params {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
}

/* One action parameter row (fallback inputs for non-instance_picker kinds). */
.cb-action-param {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 6px;
}

/* The PARAM-NAME label above an action-param input. */
.cb-action-param__label {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

/* Single-leaf inspector leaf-header (the leaf-name + leaf-id display
 * inside the Leaf field). Reads --cb-leaf-header-fg for the leaf-name
 * bold color (default fg, warn when missing). */
.cb-leaf-inspector-header {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-dim);
  line-height: 1.4;
  word-break: break-all;
}
.cb-leaf-inspector-header__name {
  color: var(--mt-fg);
}
.cb-leaf-inspector-header__missing {
  color: var(--mt-warn);
}

/* Frequency advisory chip (fast/normal/slow) under the leaf header.
 * Reads --cb-freq-color and --cb-freq-bg for the per-state coloring
 * (fast = live, normal/slow = muted). */
.cb-freq-chip {
  padding: 2px 6px;
  border-radius: 3px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  background: var(--cb-freq-bg, color-mix(in srgb, var(--mt-muted) 13%, transparent));
  color: var(--cb-freq-color, var(--mt-muted));
  border: 1px solid var(--cb-freq-border, color-mix(in srgb, var(--mt-muted) 33%, transparent));
}

/* Frequency wrap (margin-top:4 above the chip). */
.cb-freq-wrap {
  margin-top: 4px;
}

/* Inline ParameterPicker stack inside single-leaf Leaf field. ml:12
 * indents the picker rows under the leaf header. */
.cb-leaf-param-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 6px;
  margin-left: 12px;
}

/* Walkthrough <p> spacing inside hint blocks. .has-mb-md applies the
 * 8px bottom margin between paragraphs; default has no margin (used
 * by trailing paragraph in a multi-p walkthrough). */
.cb-walkthrough-p {
  margin: 0;
}
.cb-walkthrough-p.has-mb-md {
  margin: 0 0 8px 0;
}

/* Sub-label margin pair used by gradient/border/corner sub-headings
 * inside Card frame. Pairs with subLabel(t) inline-style spread —
 * .cb-sub-label-margin replaces the `marginTop:8, marginBottom:3`
 * pair (mt-mt-sm + 3px-bottom). Visual tolerance: 3px → 3px (same). */
.cb-sub-label-margin {
  margin-top: 8px;
  margin-bottom: 3px;
}

/* First sub-label in a section — no top margin (the section header
 * already separates), only the 3px bottom margin. */
.cb-sub-label-margin.is-first {
  margin-top: 0;
}

/* Angle sub-label has the HelpHint icon next to it, so it needs
 * flex/center on top of the margin pair. */
.cb-sub-label-margin.is-row {
  display: flex;
  align-items: center;
}

/* Chrome-widget info-only placeholder is a flush mono row inside
 * the gear/auth_state/spacer/tab_bar Field bodies. */
.cb-chrome-placeholder {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
  line-height: 1.5;
}

/* Tiny override for segButton-style buttons (this-cell-only toggle inside
 * widget-list header). Wraps the segButton(t,active) inline-style without
 * adding a Pass-1 inline override. */
.cb-seg-btn-tiny {
  padding: 2px 6px !important;
  font-size: 9px !important;
}

/* ── 19-04g (InspectorPane / Section 7, lines 9500-11900) ────────────────────
 *
 * InspectorPane is the right-hand pane of the card builder modal — every
 * field of every card kind is edited here. The classes below collapse the
 * dense, repetitive inline-style aesthetic blocks that the per-Field bodies
 * carried (segmented choice rows, 3×3 anchor grids, content-scale preview,
 * threshold textareas, accent-tinted CTA buttons, warn strips, etc.) onto a
 * coherent vocabulary. The InspectorPane shape didn't change — only the
 * style aesthetics moved off inline. Every state-driven color treatment
 * (.is-active) injects the accent token via CSS-defined fallbacks so the
 * cascade owns light/dark theme swaps without per-site re-render.
 */

/* Segmented-choice rows used by sweep / sortDir / direction / showAs / stack
 * flow toggles. Two-or-more buttons in a flex row with a small gap. Each
 * .cb-toggle-pill cell is uniform; .is-active swaps to the accent-tinted
 * treatment. Distinct from the seg-button helper (segButton(t,active))
 * because this is a more compact rendering used inside per-Field bodies. */
.cb-toggle-pill-row {
  display: flex;
  gap: 4px;
}
.cb-toggle-pill {
  flex: 1;
  padding: 4px 6px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  cursor: pointer;
  border-radius: 4px;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  transition: background 120ms linear, color 120ms linear,
              border-color 120ms linear;
}
.cb-toggle-pill.is-active {
  background: color-mix(in srgb, var(--mt-accent) 19%, transparent);
  color: var(--mt-accent);
  border-color: var(--mt-accent);
}

/* Compact variant used by the stack-flow row (smaller padding/font for the
 * cell-stacking sub-row). */
.cb-toggle-pill.is-compact {
  padding: 3px 8px;
}

/* 9-position anchor grid — used by PiP-anchor (Picture-in-picture position)
 * and the Alignment field. Both render as a 3×3 grid of small square cells
 * with a dot indicator showing the active position. */
.cb-anchor-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 3px;
  width: 80px;
}
.cb-anchor-cell {
  width: 24px;
  height: 18px;
  padding: 0;
  cursor: pointer;
  border-radius: 3px;
  background: transparent;
  border: 1px solid var(--mt-border);
  transition: background 120ms linear, border-color 120ms linear;
}
.cb-anchor-cell.is-active {
  background: color-mix(in srgb, var(--mt-accent) 19%, transparent);
  border-color: var(--mt-accent);
}
.cb-anchor-dot {
  display: inline-block;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--mt-muted);
}
.cb-anchor-cell.is-active .cb-anchor-dot {
  background: var(--mt-accent);
}

/* PiP container — vertical stack of PiP sub-controls with gap:5 and an
 * mt-top:6 separator from the parent "render as overlay" checkbox. */
.cb-pip-stack {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: 6px;
}

/* % vs px toggle inside PiP size sub-row. Two buttons in a flex row with
 * tight bottom margin to the slider beneath. */
.cb-unit-toggle-row {
  display: flex;
  gap: 4px;
  margin-bottom: 4px;
}
.cb-unit-toggle {
  flex: 1;
  padding: 3px 6px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  cursor: pointer;
  border-radius: 4px;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  transition: background 120ms linear, color 120ms linear,
              border-color 120ms linear;
}
.cb-unit-toggle.is-active {
  background: color-mix(in srgb, var(--mt-accent) 19%, transparent);
  color: var(--mt-accent);
  border-color: var(--mt-accent);
}

/* PiP / scale advisory text — small muted footnote under a slider. */
.cb-pip-hint {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  margin-top: 3px;
  line-height: 1.4;
}

/* Z-index controls (Stacking field) — back/front buttons + numeric input
 * in a single flex row. */
.cb-z-row {
  display: flex;
  gap: 4px;
  align-items: center;
}
.cb-z-btn {
  flex: 1;
  padding: 4px 8px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
}
.cb-z-input {
  width: 50px;
  padding: 4px 6px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  box-sizing: border-box;
}

/* Position grid (col / row / w / h) — 4-column grid for the four ints. */
.cb-pos-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  gap: 4px;
}
.cb-pos-input {
  width: 100%;
  padding: 4px 6px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  box-sizing: border-box;
}

/* Position legend ("col, row, w, h · drag widget body to move…"). */
.cb-pos-legend {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  margin-top: 2px;
}

/* Compact numeric input — used by the precision / multiplyBy / addOffset /
 * unit / max-rows / row-gap / heatmap-range fields. Same shape as
 * fieldInput but a touch narrower on padding. */
.cb-num-input {
  width: 100%;
  padding: 4px 6px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  box-sizing: border-box;
}
.cb-num-input.is-narrow {
  max-width: 80px;
}
.cb-num-input.is-medium {
  max-width: 90px;
}

/* Range slot — the paired min→max input cluster ("min → max" with an
 * arrow span between). Used by Range, Heatmap value range. */
.cb-range-row {
  display: flex;
  gap: 6px;
  align-items: center;
}
.cb-range-arrow {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
}

/* Slow-on-fast warning strip — shown when a slow renderer is bound to a
 * fast-frequency leaf. Warn-tinted bordered card under the renderer select. */
.cb-warn-strip {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-warn);
  margin-top: 4px;
  line-height: 1.4;
  padding: 4px 6px;
  background: color-mix(in srgb, var(--mt-warn) 8%, transparent);
  border-radius: 3px;
  border: 1px solid color-mix(in srgb, var(--mt-warn) 25%, transparent);
}

/* States / labels chip row — used by the Possible-states and
 * Possible-labels read-only previews. Each chip is a small pill with
 * accent-tinted background, set via inline CSS custom prop so the chip's
 * color can be data-driven. */
.cb-state-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.cb-state-chip {
  padding: 2px 7px;
  border-radius: 999px;
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  background: var(--cb-state-chip-bg, color-mix(in srgb, var(--mt-muted) 13%, transparent));
  color: var(--cb-state-chip-fg, var(--mt-muted));
  border: 1px solid var(--cb-state-chip-border, color-mix(in srgb, var(--mt-muted) 33%, transparent));
}
.cb-state-chip.is-icon-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  text-transform: none;
  letter-spacing: 0;
}
.cb-state-chip-overflow {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
}

/* Time range — leaf-default trigger button + reset chevron. Mainly used
 * inside the "Time range" Field. */
.cb-time-range-row {
  display: flex;
  gap: 4px;
  align-items: center;
}
.cb-time-range-btn {
  flex: 1;
  padding: 5px 9px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
}
.cb-time-range-btn__label {
  flex: 1;
  text-align: left;
}
.cb-time-range-reset {
  padding: 5px 9px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-muted);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
}

/* Thresholds / iconMap / valueMap / heatmap-colors JSON textareas. Mono
 * with tight line-height; minHeight varies by field. */
.cb-json-textarea {
  width: 100%;
  padding: 5px 7px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  box-sizing: border-box;
  line-height: 1.4;
  resize: vertical;
  min-height: 46px;
}
.cb-json-textarea.is-tall {
  min-height: 90px;
  font-size: 11px;
}
.cb-json-textarea.is-short {
  min-height: 36px;
}

/* Accent-tinted CTA pill — used by the "wire up live weather icon" button
 * and "Use weather codes" preset trigger. Wider treatment than .cb-toggle-pill
 * (carries the persuasive accent fill). */
.cb-cta-pill {
  width: 100%;
  padding: 6px 10px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 33%, transparent);
  border-radius: 4px;
  cursor: pointer;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin-bottom: 8px;
}
.cb-cta-pill.is-inline {
  width: auto;
  padding: 4px 10px;
  margin-bottom: 0;
}

/* Muted ghost-CTA — secondary "Override with WMO preset", "Clear",
 * "Clear override" buttons sitting beside an accent CTA. */
.cb-ghost-cta {
  padding: 4px 10px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

/* Map-button-row — "Use weather codes" + "Clear" row under the JSON
 * textarea. flex with gap:6 and wrap. */
.cb-cta-row {
  display: flex;
  gap: 6px;
  margin-top: 6px;
  flex-wrap: wrap;
}

/* "Using leaf's built-in iconMap" live notice — small live-tinted strip
 * under the JSON textarea. */
.cb-iconmap-notice {
  margin-top: 6px;
  margin-bottom: 6px;
  padding: 5px 8px;
  background: color-mix(in srgb, var(--mt-live) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-live) 33%, transparent);
  border-radius: 4px;
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-live);
  line-height: 1.4;
}

/* Content-scale preview cluster — left tile + right slider column. */
.cb-content-scale-row {
  display: flex;
  gap: 10px;
  align-items: center;
}
.cb-content-scale-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
/* Schematic preview tile. Outer cell stays at a fixed 70×44 box with
 * dashed-border + clipping; inner Aa renders at the current scale
 * (consumed by --cb-content-scale-transform / --cb-content-scale-border). */
.cb-content-scale-tile {
  width: 70px;
  height: 44px;
  flex-shrink: 0;
  border: 1px dashed var(--mt-border);
  border-radius: 4px;
  overflow: hidden;
  position: relative;
  background: var(--mt-bg);
}
.cb-content-scale-inner {
  width: 100%;
  height: 100%;
  transform: var(--cb-content-scale-transform, scale(1));
  transform-origin: center center;
  border: var(--cb-content-scale-border, none);
  border-radius: var(--cb-content-scale-radius, 0);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
  color: var(--mt-accent);
  font-size: 16px;
  font-weight: 700;
  font-family: "Space Grotesk", "Rubik", sans-serif;
  letter-spacing: -0.5px;
}

/* "Send to back / Bring to front" muted button row inside z-index editor —
 * already covered by .cb-z-btn above. (Kept the doc here for grep.) */

/* Aggregate / format / dedup / fit / chartAspect / defaultAfter / iconSize /
 * heatmap sortMode / size /headerStyle / valueRefreshHz / textSize / label-size /
 * lineThickness — all use the same `fieldInput(t)` chrome with a small padding
 * tweak. Many sites still spread fieldInput(t) for typography; the structural
 * tweaks below carry the consistent padding/font-size variant. */
.cb-select-compact {
  padding: 4px 6px !important;
}

/* Small "px" / "Hz" advisory label sat next to the slider value. */
.cb-axis-suffix {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
}
.cb-axis-suffix.is-narrow {
  min-width: 32px;
}

/* Centered 64px fixed-width numeric input used by the icon-size px field
 * (paired with a range slider). */
.cb-num-input-fixed {
  width: 64px;
  padding: 4px 6px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  text-align: center;
}

/* InspectorPane outer scroll wrapper — borderLeft + deep bg + flex column,
 * scrolls vertically. Replaces the inline at the InspectorPane root. */
.cb-inspector-shell {
  border-left: 1px solid var(--mt-border);
  background: var(--mt-bg-deep);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  min-height: 0;
}

/* InspectorPane footer — Reset style + Delete bottom row. mt-auto pins to
 * the bottom of the scroll, two buttons share the row evenly. */
.cb-inspector-footer {
  margin-top: auto;
  display: flex;
  gap: 6px;
}

/* Ghost-style "Reset style" button (flex:1, muted outline). */
.cb-inspector-reset-btn {
  flex: 1;
  padding: 6px 10px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
}

/* Error-tinted "Delete" button (flex:1, error-color outline + tint). */
.cb-inspector-delete-btn {
  flex: 1;
  padding: 6px 10px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: color-mix(in srgb, var(--mt-error) 13%, transparent);
  color: var(--mt-error);
  border: 1px solid color-mix(in srgb, var(--mt-error) 33%, transparent);
  border-radius: 4px;
  cursor: pointer;
}

/* PiP-size slider row — flex row of range slider + numeric input. */
.cb-pip-size-row {
  display: flex;
  gap: 6px;
  align-items: center;
}

/* ShowWhenEditor / VisibilityEditor enable button — ghost button used to
 * open the conditional-visibility flow. */
.cb-visibility-enable {
  padding: 5px 12px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

/* Help-text muted line at top of conditional-visibility editor. */
.cb-visibility-help {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  line-height: 1.5;
  margin-bottom: 8px;
}

/* ShowWhenEditor constraint sub-label ("Bind to leaf" / "Constraints"). */
.cb-visibility-sublabel {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  color: var(--mt-muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  margin-bottom: 3px;
}
.cb-visibility-sublabel.has-mt {
  margin-top: 10px;
  margin-bottom: 4px;
}

/* Constraint row inside ShowWhenEditor. 4-column grid: comparator |
 * operand-mode | operand-input | remove button. */
.cb-constraint-row {
  display: grid;
  grid-template-columns: 90px 80px 1fr auto;
  gap: 4px;
  margin-bottom: 4px;
  align-items: center;
}

/* ConstraintRow remove (×) button. */
.cb-constraint-remove {
  padding: 4px 8px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  cursor: pointer;
}

/* Constraint row "Add constraint" + "Remove rule" footer buttons. */
.cb-visibility-cta-row {
  display: flex;
  gap: 6px;
  margin-top: 6px;
  flex-wrap: wrap;
}
.cb-visibility-add-btn {
  padding: 4px 10px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  border-radius: 3px;
  cursor: pointer;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.cb-visibility-add-btn.is-enabled {
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 33%, transparent);
}
.cb-visibility-add-btn.is-disabled {
  color: var(--mt-muted);
  border: 1px solid var(--mt-border);
  cursor: not-allowed;
}
.cb-visibility-remove-btn {
  padding: 4px 10px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  cursor: pointer;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

/* Live-evaluation preview strip below constraint editor. Three tone
 * variants: pending (muted), passing (accent/success), failing (warn). */
.cb-visibility-preview {
  margin-top: 10px;
  padding: 7px 10px;
  border-radius: 4px;
  font-family: var(--m-font-mono);
  font-size: 10px;
  line-height: 1.4;
  background: var(--cb-visibility-preview-bg, color-mix(in srgb, var(--mt-muted) 8%, transparent));
  border: 1px solid var(--cb-visibility-preview-border, var(--mt-border));
}
.cb-visibility-preview-pill {
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 99px;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  background: var(--cb-visibility-pill-bg, color-mix(in srgb, var(--mt-muted) 19%, transparent));
  color: var(--cb-visibility-pill-fg, var(--mt-muted));
}

/* LeafIconMapDefaultEditor outer wrapper — dashed-top border separator. */
.cb-leaf-iconmap-shell {
  margin-top: 12px;
  padding-top: 8px;
  border-top: 1px dashed var(--mt-border);
}

/* Small-value modifiers used to pin a margin onto an existing classed
 * element without re-introducing the inline-style aesthetic. Pair with
 * `.cb-form-check-row`, `.cb-chrome-placeholder`, `.cb-cta-row`, etc. */
.has-mt-0  { margin-top:    0  !important; }
.has-mt-3  { margin-top:    3px !important; }
.has-mt-4  { margin-top:    4px !important; }
.has-mt-6  { margin-top:    6px !important; }
.has-mb-3  { margin-bottom: 3px !important; }
.has-mb-4  { margin-bottom: 4px !important; }
.has-mb-6  { margin-bottom: 6px !important; }

/* Tweaks for cb-chrome-placeholder used in iconMap header (smaller font +
 * tighter line height than the default). */
.cb-chrome-placeholder.is-tight {
  font-size: 9px;
  line-height: 1.4;
}

/* Tweak for cb-pip-hint when it sits inside an editor footer (smaller
 * fonts; same family). */

/* Constraint-row stack-flow opacity wrapper (cell-stacking flow row when
 * the widget is not the anchor — dims controls). */
.cb-stack-row-dim {
  opacity: 0.55;
}

/* Primary CTA pill used inside LeafIconMapDefaultEditor's "Save user
 * default" button — accent-fill rather than accent-tint. One-off; if more
 * primary actions land here, this becomes the canonical accent-fill. */
.cb-primary-cta {
  padding: 4px 10px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: var(--mt-accent);
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

/* ColorSwatch component — small square accent-tinted swatch button used
 * in the ColorTokenSelect row. Reads --cb-swatch-fg / --cb-swatch-bg /
 * --cb-swatch-border for the active state. */
.cb-color-swatch {
  padding: 4px 6px;
  font-size: 9px;
  font-family: var(--m-font-mono);
  font-weight: 600;
  cursor: pointer;
  border-radius: 4px;
  background: var(--cb-swatch-bg, transparent);
  color: var(--cb-swatch-fg, var(--mt-dim));
  border: 1px solid var(--cb-swatch-border, var(--mt-border));
  display: flex;
  align-items: center;
  gap: 4px;
  justify-content: center;
}
.cb-color-swatch__dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--cb-swatch-fg, var(--mt-dim));
}

/* HexColorInput — paired color picker + hex input row. Used inside
 * ColorTokenSelect for arbitrary hex values. */
.cb-hex-row {
  display: flex;
  gap: 4px;
  align-items: center;
  margin-top: 3px;
}
.cb-hex-picker {
  width: 24px;
  height: 22px;
  padding: 0;
  cursor: pointer;
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  background: transparent;
}
.cb-hex-text {
  flex: 1;
  padding: 3px 6px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
}

/* ============================================================================
 * Phase 19-04h: Chrome helpers — Slider / _ColorBar / ColorTokenSelect /
 *               CardLibrary class vocabulary.
 *
 * Replaces ~189 in-band inline-style sites in card-builder.jsx lines
 * 11900-13400. Each rule below has a doc comment identifying its
 * consumer site. State-driven values (per-card tab tints, per-instance
 * sizes, dynamic widths) flow through --cb-* custom properties.
 * ============================================================================ */

/* === Slider — paired range + number input row ============================= *
 *
 * Used by the `Slider` component (card-builder.jsx ~11946). The number
 * input's width is per-instance (caller's `inputWidth` prop), so it flows
 * via --cb-slider-input-width. The unit suffix and number input read
 * `fontSize` from props as --cb-slider-font-size.
 */
.cb-slider-row {
  display: flex;
  gap: 6px;
  align-items: center;
  flex: 1;
  min-width: 0;
}
.cb-slider-range {
  flex: 1;
  min-width: 0;
}
.cb-slider-input {
  width: var(--cb-slider-input-width, 48px);
  padding: 2px 4px;
  font-size: var(--cb-slider-font-size, 10px);
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  text-align: right;
}
.cb-slider-unit {
  font-size: var(--cb-slider-unit-font-size, 9px);
  font-family: var(--m-font-mono);
  color: var(--mt-muted);
  min-width: 14px;
}

/* === _ColorBar — small square swatch with placeholder fallback =========== *
 *
 * Used by ColorTokenSelect's row/trigger and by the "custom hex" button.
 * `color` flows via --cb-color-bar-bg (null → diagonal-stripe fallback).
 * `size` flows via --cb-color-bar-size for the per-instance pixel size.
 */
.cb-color-bar {
  display: inline-block;
  width: var(--cb-color-bar-size, 14px);
  height: var(--cb-color-bar-size, 14px);
  border-radius: 3px;
  flex-shrink: 0;
  background: var(--cb-color-bar-bg, transparent);
  border: 1px solid var(--mt-border);
}
/* When no color is set, paint the diagonal-stripe placeholder so users
 * can distinguish "auto / off / transparent" from a real color. */
.cb-color-bar.is-empty {
  background-image: linear-gradient(135deg,
    transparent 45%,
    var(--mt-muted) 45%,
    var(--mt-muted) 55%,
    transparent 55%);
}

/* === ColorTokenSelect — dropdown shell + popover ========================= *
 *
 * Used by the ColorTokenSelect component (card-builder.jsx ~11988).
 * State-driven values: hover background passes through --cb-cts-row-hover-bg
 * (computed from theme fg as `${t.fg}10`). The popover's "selected" tint
 * is a fixed token mix, so no custom prop needed.
 */
.cb-cts-shell {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.cb-cts-trigger-wrap {
  display: flex;
  gap: 6px;
  align-items: center;
  position: relative;
}
.cb-cts-trigger {
  flex: 1;
  padding: 3px 24px 3px 6px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  cursor: pointer;
  text-align: left;
  position: relative;
}
.cb-cts-chevron {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%) rotate(0deg);
  transition: transform 120ms;
  font-size: 9px;
  color: var(--mt-muted);
  pointer-events: none;
}
.cb-cts-chevron.is-open {
  transform: translateY(-50%) rotate(180deg);
}
.cb-cts-popover {
  position: absolute;
  top: calc(100% + 4px);
  left: 20px;
  right: 0;
  z-index: 1000;
  max-height: 280px;
  overflow-y: auto;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border-strong, var(--mt-border));
  border-radius: 4px;
  padding: 4px 0;
  box-shadow: 0 6px 18px rgba(0,0,0,0.35);
}
.cb-cts-row {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 4px 8px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-fg);
}
.cb-cts-row.is-selected {
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
}
.cb-cts-row-label {
  flex: 1;
}
.cb-cts-group-label {
  padding: 6px 8px 2px;
  font-size: 9px;
  font-family: var(--m-font-mono);
  color: var(--mt-muted);
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
/* Custom-hex button at the bottom of the popover — same shape as a row
 * but with a top divider so it reads as a separate "escape hatch" tier. */
.cb-cts-hex-btn {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 6px 8px;
  width: 100%;
  text-align: left;
  margin-top: 4px;
  border-top: 1px solid var(--mt-border);
  background: transparent;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-fg);
}

/* === CardLibrary — library list, header bar, filter, rows, deploy menu ==== *
 *
 * Used by the CardLibrary component (card-builder.jsx ~12176). The
 * library is the top-of-Custom-tab list and is the catalog-side surface
 * section 8 owns. Per-card row-active tints flow through inline
 * `style={{background: …}}` only where a per-row state (expanded /
 * menu-open / current-header) requires it — those use coupled
 * --cb-cl-row-* custom properties.
 */
.cb-cl-shell {
  padding: 14px 14px 0;
}
.cb-cl-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}
/* Tighter bottom margin when the placement-hint subtitle is rendered
 * right below the header (so the hint sits adjacent to the title). */
.cb-cl-header.has-hint {
  margin-bottom: 4px;
}
.cb-cl-title {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.cb-cl-filter-wrap {
  position: relative;
  flex: 1 1 200px;
  min-width: 140px;
  max-width: 360px;
}
.cb-cl-filter-input {
  width: 100%;
  padding: 5px 26px 5px 10px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  background: var(--mt-bg-elev);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  outline: none;
  box-sizing: border-box;
}
/* Accent border when filter has a term — visual cue that filter is
 * active. */
.cb-cl-filter-input.is-active {
  border-color: var(--mt-accent);
}
.cb-cl-filter-clear {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
  color: var(--mt-muted);
  border: none;
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
}
.cb-cl-new-btn {
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 700;
  font-family: var(--m-font-mono);
  background: var(--mt-accent);
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.cb-cl-placement-hint {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-dim);
  margin-bottom: 10px;
  font-style: italic;
}
.cb-cl-error {
  padding: 8px 12px;
  background: color-mix(in srgb, var(--mt-error) 15%, transparent);
  color: var(--mt-error);
  border: 1px solid color-mix(in srgb, var(--mt-error) 33%, transparent);
  border-radius: 4px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  margin-bottom: 10px;
}
.cb-cl-loading {
  padding: 20px 0;
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  font-size: 11px;
}
.cb-cl-empty {
  padding: 24px 18px;
  border: 1px dashed var(--mt-border-strong, var(--mt-border));
  border-radius: 10px;
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  font-size: 12px;
  text-align: center;
}
.cb-cl-empty__title {
  font-weight: 700;
  margin-bottom: 4px;
  color: var(--mt-dim);
}
.cb-cl-no-match {
  padding: 18px 14px;
  border: 1px dashed var(--mt-border);
  border-radius: 8px;
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  font-size: 11px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.cb-cl-no-match__term {
  color: var(--mt-dim);
}
.cb-cl-no-match__clear {
  padding: 4px 10px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
}
.cb-cl-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  border: 1px solid var(--mt-border);
  border-radius: 6px;
  /* No overflow:hidden — that would clip the deploy dropdown when it
   * extends past the row's bottom edge. */
  background: var(--mt-bg-elev);
}
.cb-cl-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-top: 1px solid var(--mt-border);
  background: transparent;
}
/* First row in the list — no top border (the list wrapper carries it). */
.cb-cl-row.is-first {
  border-top: none;
}
/* Expanded row — light accent tint shows which row's preview is open. */
.cb-cl-row.is-expanded {
  background: color-mix(in srgb, var(--mt-accent) 3%, transparent);
}
/* Inline preview-toggle chevron button (▸ / ▾). Reused base from
 * the row's ICON_BTN local const that the JSX previously defined inline. */
.cb-cl-preview-toggle {
  padding: 4px 7px;
  font-size: 13px;
  line-height: 1;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-muted);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  min-width: 28px;
}
.cb-cl-preview-toggle.is-expanded {
  color: var(--mt-accent);
  border-color: var(--mt-accent);
}
.cb-cl-row-name {
  flex: 1;
  font-family: "Rubik", sans-serif;
  font-weight: 600;
  font-size: 13px;
  color: var(--mt-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-cl-row-size {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  font-weight: 600;
  flex-shrink: 0;
}
.cb-cl-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  justify-content: flex-end;
  max-width: 220px;
  min-width: 0;
}
.cb-cl-tab-unassigned {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  font-style: italic;
}
.cb-cl-tab-chip {
  font-family: var(--m-font-mono);
  font-size: 9px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 99px;
  background: color-mix(in srgb, var(--mt-accent) 15%, transparent);
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 25%, transparent);
}
.cb-cl-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}
/* "+ Place here" primary button — only mounts when CardLibrary is in
 * placement mode. Accent fill, same shape as the cb-cl-new-btn but with
 * the per-row icon-button height. */
.cb-cl-place-btn {
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 700;
  font-family: var(--m-font-mono);
  background: var(--mt-accent);
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
/* Per-row icon button (✎ edit / ⎘ duplicate / × delete). Same as the
 * preview toggle chrome but without the min-width override. */
.cb-cl-icon-btn {
  padding: 4px 7px;
  font-size: 13px;
  line-height: 1;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
}
/* Delete variant uses a muted color (vs the row's normal dim) so it
 * reads as the destructive action without screaming red. */
.cb-cl-icon-btn.is-delete {
  color: var(--mt-muted);
}
.cb-cl-deploy-wrap {
  position: relative;
}
.cb-cl-deploy-btn {
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 700;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 4px;
}
.cb-cl-deploy-btn.is-open {
  background: color-mix(in srgb, var(--mt-accent) 15%, transparent);
  color: var(--mt-accent);
  border-color: var(--mt-accent);
}
.cb-cl-deploy-arrow {
  font-size: 9px;
}
.cb-cl-deploy-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 50;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border-strong, var(--mt-border));
  border-radius: 6px;
  padding: 6px 4px;
  min-width: 200px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
}
.cb-cl-deploy-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  cursor: pointer;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-fg);
  background: transparent;
  border-radius: 3px;
}
.cb-cl-deploy-row.is-checked {
  background: color-mix(in srgb, var(--mt-accent) 9%, transparent);
}
.cb-cl-deploy-row__spacer {
  flex: 1;
}
.cb-cl-deploy-row__hint {
  color: var(--mt-muted);
  font-size: 9px;
}
.cb-cl-deploy-divider {
  height: 1px;
  background: var(--mt-border);
  margin: 4px 6px;
}
.cb-cl-preview-body {
  border-top: 1px solid var(--mt-border);
  background: var(--mt-bg-deep);
  padding: 14px;
}
.cb-cl-preview-inner {
  /* Cap the preview height so big cards don't run off-screen. */
  max-height: 420px;
  overflow: auto;
}
.cb-cl-preview-loading {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
  text-align: center;
  padding: 12px 0;
}


/* ============================================================================
 * Section 9 — CardBuilderModal orchestrator + final pickers
 * (Phase 19 Plan 04i — band 13400-14275 of card-builder.jsx)
 * ----------------------------------------------------------------------------
 * Component anchors: CardBuilderModal (outer chrome + body), TabsPicker,
 * AlignSelector, PlacementSelector, LinkBelowIndicator, SizeSelector,
 * LeafOverridesBrowser. All theme-agnostic; reads --mt-* tokens.
 * ============================================================================ */

/* === CardBuilderModal — outer chrome ====================================== */

/* Full-viewport scrim. Click outside the modal box closes the modal
 * (CardBuilderModal's onClick handler). */
.cb-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.72);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
/* Modal frame. Width clamped to keep the picker rails readable on
 * narrow viewports while still using the screen on wide displays. */
.cb-modal-box {
  width: min(96vw, 1800px);
  height: min(92vh, 1000px);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--mt-border-strong);
  display: flex;
  flex-direction: column;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
/* Sticky header strip — name input + size/align/placement/tabs pickers +
 * Save / Cancel cluster. flex-shrink:0 keeps it pinned when body scrolls. */
.cb-modal-header {
  padding: 10px 14px;
  border-bottom: 1px solid var(--mt-border);
  background: var(--mt-bg-elev);
  display: flex;
  gap: 10px;
  align-items: center;
  flex-shrink: 0;
}
/* "NEW CARD" / "EDIT" badge — mono caps. */
.cb-modal-mode-badge {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-muted);
  font-weight: 700;
  text-transform: uppercase;
}
/* Undo/Redo button cluster — two buttons sharing a border. */
.cb-modal-undo-group {
  display: flex;
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  overflow: hidden;
}
.cb-modal-undo-btn {
  padding: 6px 10px;
  font-size: 13px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-fg);
  border: none;
  cursor: pointer;
}
.cb-modal-undo-btn.is-disabled {
  color: var(--mt-muted);
  cursor: not-allowed;
  opacity: 0.4;
}
/* Right-most undo/redo button gets a left border (the +1 in the group). */
.cb-modal-undo-btn.is-second {
  border-left: 1px solid var(--mt-border);
}
/* Name input — flex:1 fills remaining space between badge and pickers. */
.cb-modal-name-input {
  flex: 1;
  padding: 6px 10px;
  font-size: 13px;
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  font-family: "Rubik", sans-serif;
  min-width: 200px;
}
/* Save / Save-as / Open-existing / Cancel button row — base shape lives
 * in btnPrimary(t) / btnGhost(t) helpers (out-of-band; see 19-04i deferral
 * note). These classes only carry the per-instance sizing the chrome adds. */
.cb-modal-save-btn {
  padding: 5px 16px;
  font-size: 11px;
}
.cb-modal-save-btn.is-disabled {
  cursor: not-allowed;
  opacity: 0.4;
}
.cb-modal-saveas-btn,
.cb-modal-openexist-btn,
.cb-modal-cancel-btn {
  padding: 5px 12px;
  font-size: 11px;
}
.cb-modal-saveas-btn {
  color: var(--mt-accent);
  border-color: color-mix(in srgb, var(--mt-accent) 33%, transparent);
}
.cb-modal-saveas-btn.is-disabled,
.cb-modal-openexist-btn.is-disabled {
  cursor: not-allowed;
  opacity: 0.4;
}
.cb-modal-cancel-btn {
  padding: 5px 14px;
}
/* Visual / JSON view-mode segment buttons in the header. Sit on top of
 * segButton(t, active) helper output — the helper owns theme-driven
 * background + border + color; these classes only carry the modal-header
 * sizing override (segGroup default padding/font-size is too generous for
 * the header). */
.cb-modal-segbtn {
  padding: 4px 10px;
  font-size: 11px;
}
/* Error strip under header. */
.cb-modal-error {
  padding: 7px 14px;
  background: color-mix(in srgb, var(--mt-error) 15%, transparent);
  color: var(--mt-error);
  border-bottom: 1px solid color-mix(in srgb, var(--mt-error) 33%, transparent);
  font-size: 11px;
  font-family: var(--m-font-mono);
  flex-shrink: 0;
}

/* === CardBuilderModal — Visual / JSON body ================================ */

/* 2-pane grid for visual mode: preview on the left, rail on the right.
 * Rail width scales with viewport between 380px and 480px. */
.cb-modal-body-visual {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr clamp(380px, 26vw, 480px);
}
/* Full-width JSON editor. */
.cb-modal-body-json {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 14px;
  gap: 10px;
  background: var(--mt-bg-deep);
}
/* "Local card JSON" disambiguation banner — warning-tinted info strip
 * at the top of JSON mode. */
.cb-modal-json-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 4px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  background: color-mix(in srgb, var(--mt-warn) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--mt-warn) 40%, transparent);
  color: var(--mt-fg);
  line-height: 1.4;
}
.cb-modal-json-banner-icon {
  font-size: 14px;
}
.cb-modal-json-banner-msg {
  flex: 1;
}
/* "↓ Export bundle" pill — sits in the banner. */
.cb-modal-json-export-btn {
  padding: 4px 12px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: var(--mt-accent);
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  white-space: nowrap;
}
/* Toolbar row above the textarea — "CARD JSON" label + Reload + Apply + Cancel. */
.cb-modal-json-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
}
.cb-modal-json-toolbar-label {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.cb-modal-json-toolbar-spacer {
  flex: 1;
}
.cb-modal-json-toolbar-btn {
  padding: 4px 10px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
}
.cb-modal-json-toolbar-btn.is-primary {
  padding: 4px 12px;
  background: var(--mt-accent);
  color: #fff;
  border: none;
}
/* JSON parse-error strip — multi-line preformatted error. */
.cb-modal-json-error {
  padding: 8px 12px;
  background: color-mix(in srgb, var(--mt-error) 15%, transparent);
  color: var(--mt-error);
  border: 1px solid color-mix(in srgb, var(--mt-error) 33%, transparent);
  border-radius: 4px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  white-space: pre-wrap;
  word-break: break-word;
}
/* Main JSON textarea — fills remaining vertical space. */
.cb-modal-json-textarea {
  flex: 1;
  min-height: 0;
  resize: none;
  padding: 12px;
  font-family: var(--m-font-mono);
  font-size: 12px;
  line-height: 1.5;
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 6px;
  white-space: pre;
  overflow-wrap: normal;
  overflow: auto;
  tab-size: 2;
}
/* Footer hint under textarea. */
.cb-modal-json-hint {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  line-height: 1.5;
}

/* === TabsPicker =========================================================== */

/* Anchor for the popover; trigger is a single button inside it. */
.cb-tabs-picker {
  position: relative;
}
.cb-tabs-picker-trigger {
  padding: 5px 10px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
}
.cb-tabs-picker-trigger-label {
  color: var(--mt-muted);
  font-size: 10px;
}
.cb-tabs-picker-trigger-count {
  color: var(--mt-fg);
}
.cb-tabs-picker-trigger-chev {
  color: var(--mt-muted);
  font-size: 9px;
}
/* Floating popover with the checkbox list. */
.cb-tabs-picker-popover {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 50;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border-strong);
  border-radius: 6px;
  padding: 8px 4px;
  min-width: 180px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
}
.cb-tabs-picker-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  cursor: pointer;
  font-size: 11px;
  font-family: var(--m-font-mono);
  color: var(--mt-fg);
  background: transparent;
  border-radius: 3px;
}
.cb-tabs-picker-row.is-checked {
  background: color-mix(in srgb, var(--mt-accent) 9%, transparent);
}
.cb-tabs-picker-row-spacer {
  flex: 1;
}
.cb-tabs-picker-row-id {
  color: var(--mt-muted);
  font-size: 9px;
}
.cb-tabs-picker-hint {
  font-family: var(--m-font-mono);
  font-size: 9px;
  color: var(--mt-muted);
  margin-top: 6px;
  padding: 0 10px;
  line-height: 1.4;
}

/* === AlignSelector ======================================================== */

.cb-align-selector {
  display: flex;
  gap: 0;
  align-items: center;
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-dim);
}
.cb-align-selector-label {
  color: var(--mt-muted);
  font-size: 10px;
  margin-right: 4px;
}
/* Compact segmented group — wraps segGroup() but overrides radius/padding. */
.cb-align-selector-group {
  border-radius: 3px;
  overflow: hidden;
  padding: 0;
  gap: 0;
}
.cb-align-selector-btn {
  width: 28px;
  height: 22px;
  padding: 0;
  flex: none;
  font-size: 13px;
  font-weight: 700;
  border-radius: 0;
}
/* First button has no left border; subsequent ones get a 1px divider. */
.cb-align-selector-btn.is-not-first {
  border-left: 1px solid var(--mt-border);
}

/* === PlacementSelector ==================================================== */

/* Wrapping row: "AT [row] , [col] [clear]". Accent-tinted when placed. */
.cb-placement-selector {
  display: flex;
  gap: 4px;
  align-items: center;
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-dim);
}
.cb-placement-selector.is-placed {
  color: var(--mt-accent);
}
.cb-placement-selector-label {
  color: var(--mt-muted);
  font-size: 10px;
}
.cb-placement-selector-sep {
  color: var(--mt-muted);
}
.cb-placement-input {
  width: 38px;
  padding: 3px 4px;
  font-size: 11px;
  font-family: var(--m-font-mono);
  background: var(--mt-bg);
  color: var(--mt-fg);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  text-align: center;
}
.cb-placement-input.is-placed {
  border-color: var(--mt-accent);
}
.cb-placement-clear-btn {
  padding: 2px 6px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-muted);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  cursor: pointer;
}

/* === LinkBelowIndicator =================================================== */

.cb-linkbelow {
  display: flex;
  gap: 4px;
  align-items: center;
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-accent);
}
.cb-linkbelow-arrow {
  color: var(--mt-muted);
  font-size: 10px;
}
.cb-linkbelow-chip {
  padding: 2px 6px;
  border-radius: 3px;
  border: 1px solid var(--mt-accent);
  background: color-mix(in srgb, var(--mt-accent) 10%, transparent);
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-linkbelow-unlink-btn {
  padding: 2px 6px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  background: transparent;
  color: var(--mt-muted);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  cursor: pointer;
}

/* === SizeSelector ========================================================= */

.cb-size-selector {
  display: flex;
  gap: 4px;
  align-items: center;
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-dim);
}
.cb-size-selector-label {
  color: var(--mt-muted);
  font-size: 10px;
}
.cb-size-selector-x {
  color: var(--mt-muted);
}
/* ± stepper button — square 22x22, mono, sits beside the select. */
.cb-size-stepper {
  width: 22px;
  height: 22px;
  line-height: 20px;
  padding: 0;
  font-size: 12px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 3px;
  cursor: pointer;
}
.cb-size-stepper.is-disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* === LeafOverridesBrowser ================================================= */

/* The browser sits inside a manifold-modal-* shell — these classes
 * only own the body content (intro hint, filter input, per-leaf row,
 * "customized" badge, edit-toggle button). */
.cb-leafov-intro {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-muted);
  margin-bottom: 10px;
  line-height: 1.5;
}
.cb-leafov-filter {
  width: 100%;
  margin-bottom: 10px;
}
.cb-leafov-empty {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-dim);
  padding: 16px;
  text-align: center;
}
.cb-leafov-row {
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  margin-bottom: 6px;
  padding: 8px 10px;
  background: transparent;
}
.cb-leafov-row.is-open {
  background: color-mix(in srgb, var(--mt-accent) 3%, transparent);
}
.cb-leafov-row-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.cb-leafov-row-text {
  flex: 1;
  min-width: 0;
}
.cb-leafov-row-id {
  font-family: var(--m-font-mono);
  font-size: 12px;
  color: var(--mt-fg);
  font-weight: 600;
}
.cb-leafov-row-meta {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-dim);
  margin-top: 2px;
}
/* "CUSTOMIZED" pill next to the leaf id when an override is active. */
.cb-leafov-badge {
  margin-left: 8px;
  font-size: 9px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 99px;
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 33%, transparent);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
/* Edit / Close toggle. */
.cb-leafov-toggle-btn {
  padding: 4px 12px;
  font-size: 10px;
  font-family: var(--m-font-mono);
  font-weight: 700;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid var(--mt-border);
  border-radius: 4px;
  cursor: pointer;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.cb-leafov-toggle-btn.is-open {
  background: color-mix(in srgb, var(--mt-accent) 15%, transparent);
  color: var(--mt-accent);
  border-color: color-mix(in srgb, var(--mt-accent) 55%, transparent);
}

/* === ColumnsPanel (Phase 36 Plan 03) ====================================== *
 *
 * Inspector sub-panel for per-column presentation editing on list cards.
 * Lives inside a <Field label="Columns"> shell; these classes only own the
 * panel-body content (two groups + per-row controls + Advanced reveal +
 * Reset button).
 *
 * Token-only colors via var(--mt-*) per Phase 19 (no hex). Spacing follows
 * the locked Phase 15 scale: 4 / 8 / 14 / 20 px. UI-SPEC §Spacing Scale and
 * §Color hold the contract; this file is the visual landing point.
 *
 * Why no <hr> between Visible and Hidden groups: 20px (space.lg) of vertical
 * gap reads as a sub-grouping; an <hr> would compete with the existing
 * inspector section dividers and read as a new top-level section.
 *
 * Why the Reset button uses color-mix on accent: matches the existing
 * cb-leafov-toggle-btn.is-open / cb-leafov-badge patterns above — the single
 * accent affordance in the panel, per UI-SPEC §Color "60/30/10 split". */
.cb-cols-panel {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* Visible / Hidden groups. The Hidden group sits below with 20px of top
 * margin (space.lg) — the visual separator distance, no horizontal rule. */
.cb-cols-group {
  display: flex;
  flex-direction: column;
  gap: 14px; /* space.md — between rows within a group */
}
.cb-cols-group--hidden {
  margin-top: 20px; /* space.lg — the two-group separator distance */
}

/* Group label. Same shape as the existing cb-* group/section headings.
 * text.sm / weight.semibold per UI-SPEC §Typography. */
.cb-cols-group-label {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--mt-fg);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin-bottom: 4px;
}
.cb-cols-group-label--hidden {
  color: var(--mt-dim);
}

/* Empty-state line ("(none visible)" / "(none hidden)") — same dim treatment
 * as the rest of the inspector's empty-state copy. */
.cb-cols-empty {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-dim);
  padding: 6px 8px;
}
.cb-cols-empty--hidden {
  color: var(--mt-dim);
  opacity: 0.7;
}

/* One row. Holds the primary controls (Show / Header / Format / annotation)
 * on a single line, then the Advanced trigger button, then the conditional
 * Advanced reveal panel below. */
.cb-cols-row {
  display: flex;
  flex-direction: column;
  gap: 4px; /* space.xs — between primary line and the Advanced trigger */
  padding: 6px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--mt-border) 50%, transparent);
}
.cb-cols-row:last-child {
  border-bottom: none;
}
.cb-cols-row--hidden {
  opacity: 0.78;
}
.cb-cols-row__main {
  display: flex;
  align-items: center;
  gap: 8px; /* space.sm — between Show / Header / Format / annotation */
  flex-wrap: wrap;
}

/* Each control cell. Header takes the most width (the operator types into
 * it); Format is a compact dropdown; Show is just a checkbox + word. */
.cb-cols-cell {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
.cb-cols-cell-show {
  cursor: pointer;
  flex: 0 0 auto;
}
.cb-cols-cell-show__text {
  font-family: var(--m-font-mono);
  font-size: 11px;
  color: var(--mt-fg);
}
.cb-cols-cell-header {
  flex: 1 1 200px;
  min-width: 140px;
}
.cb-cols-cell-header__input {
  flex: 1 1 auto;
  min-width: 0;
}
.cb-cols-cell-format {
  flex: 0 1 180px;
  min-width: 120px;
}
.cb-cols-cell-format__select {
  width: 100%;
}
/* The field-key chip — shows the raw publisher field name next to the
 * editable Header input so the operator knows what they're aliasing.
 * Monospace, dim, small. */
.cb-cols-cell__key {
  font-family: var(--m-font-mono);
  font-size: 10px;
  color: var(--mt-dim);
  white-space: nowrap;
  max-width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Inline annotation on hidden rows. text.xs per UI-SPEC §Typography. Two
 * variants (rowlink vs unused) per UI-SPEC §Color. */
.cb-cols-annotation {
  font-family: var(--m-font-mono);
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 0 1 auto;
}
.cb-cols-annotation--rowlink {
  color: var(--mt-warn);
}
.cb-cols-annotation--unused {
  color: var(--mt-dim);
}

/* The "▸ Advanced" / "▾ Advanced" disclosure trigger. Small ghost button —
 * matches the inspector's other inline reveal patterns. Chevron is
 * character-substitution in JSX (▸ vs ▾), no CSS transform. */
.cb-cols-adv-trigger {
  align-self: flex-start;
  margin-top: 2px;
  padding: 2px 8px;
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-weight: 500;
  background: transparent;
  color: var(--mt-dim);
  border: 1px solid color-mix(in srgb, var(--mt-border) 70%, transparent);
  border-radius: 3px;
  cursor: pointer;
  letter-spacing: 0.4px;
}
.cb-cols-adv-trigger:hover {
  color: var(--mt-fg);
  border-color: var(--mt-border);
}
.cb-cols-adv-trigger.is-open {
  color: var(--mt-fg);
  background: color-mix(in srgb, var(--mt-fg) 6%, transparent);
}

/* The expanded Advanced panel — sits under the row's primary line.
 * 8px (space.sm) left inset + subtle bgElev tint per UI-SPEC §Color. */
.cb-cols-adv-panel {
  margin-top: 4px;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--mt-fg) 4%, transparent);
  border-radius: 4px;
  border-left: 2px solid color-mix(in srgb, var(--mt-border) 60%, transparent);
}
.cb-cols-adv-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-end;
}
.cb-cols-adv-cell {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 80px;
  flex: 0 1 auto;
}
.cb-cols-adv-cell--check {
  flex-direction: row;
  align-items: center;
  gap: 4px;
  min-width: 60px;
  cursor: pointer;
}
.cb-cols-adv-label {
  font-family: var(--m-font-mono);
  font-size: 10px;
  font-weight: 500;
  color: var(--mt-dim);
  letter-spacing: 0.3px;
  text-transform: uppercase;
}
.cb-cols-adv-input {
  font-family: var(--m-font-mono);
  font-size: 11px;
  min-width: 70px;
}

/* Reset-to-catalog-defaults button. Single accent affordance in the panel,
 * per UI-SPEC §Color "60/30/10 split" — every other element is fg/dim/warn.
 * The is-flash variant keeps the same color and just swaps the rendered
 * text to "Reset." in JSX. 800ms timer in the component. */
.cb-cols-reset-wrap {
  display: flex;
  justify-content: flex-end;
  margin-top: 16px;
}
.cb-cols-reset {
  padding: 6px 14px;
  font-family: var(--m-font-mono);
  font-size: 11px;
  font-weight: 600;
  background: transparent;
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 50%, transparent);
  border-radius: 4px;
  cursor: pointer;
  letter-spacing: 0.3px;
}
.cb-cols-reset:hover:not(:disabled) {
  background: color-mix(in srgb, var(--mt-accent) 12%, transparent);
  border-color: var(--mt-accent);
}
.cb-cols-reset:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.cb-cols-reset.is-flash {
  background: color-mix(in srgb, var(--mt-accent) 20%, transparent);
  border-color: var(--mt-accent);
}

/* === v1.7 "Restoring…" overlay (MIGRATE-06) =========================== *
 * Full-page cover shown after the gear → Design hub "Restore previous
 * layout" tile POSTs the rollback. The backend stops + restarts itself
 * to apply the rollback, so this overlay is plain DOM (built in
 * manifold.jsx with createElement, NOT a React component) — the React
 * tree behind it is about to be torn down by the full-page reload.
 *
 * z-index 2000 sits above the modal overlay (1000) and the edit FAB
 * (900) so nothing peeks through while the dashboard is mid-restart.
 * All aesthetics route through --m-*/--mt-* tokens (deploy-gate-safe:
 * no inline aesthetic styles in the JS that builds this). The accent
 * spinner reuses the canvas-chrome --mt-accent so the wait reads as
 * "Manifold is working," not a generic browser spinner.
 * --------------------------------------------------------------------- */

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

.manifold-restoring-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  max-width: 420px;
  padding: 32px 36px;
  background: var(--m-surface-2);
  border: 1px solid var(--m-border-hi);
  border-radius: var(--m-radius-md, 8px);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.7);
  text-align: center;
}

.manifold-restoring-spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--mt-accent) 22%, transparent);
  border-top-color: var(--mt-accent);
  animation: manifold-restoring-spin 0.8s linear infinite;
}

@keyframes manifold-restoring-spin {
  to { transform: rotate(360deg); }
}

.manifold-restoring-title {
  color: var(--m-text);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-md-size, 13px);
  font-weight: var(--mt-weight-bold, 700);
}

.manifold-restoring-detail {
  color: var(--m-text-muted);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-sm-size, 12px);
  line-height: 1.5;
}

@media (prefers-reduced-motion: reduce) {
  .manifold-restoring-spinner { animation-duration: 2.4s; }
}

/* === v1.7 section→gap notice banner (MIGRATE-07) ====================== *
 * One-time inline strip across the top of the canvas viewport, shown on
 * first canvas-mode load to explain that section labels became whitespace
 * gaps. Dismiss flips _meta.canvas_section_notice_dismissed server-side
 * so it never re-fires — here or on Karl's other device. The banner is a
 * module-scope React component (CanvasSectionNoticeBanner in
 * card-builder.jsx); these classes carry every aesthetic so the JSX stays
 * inline-aesthetic-style-free (deploy-gate-safe).
 *
 * Tone: informational, not alarming. A left accent rule + tinted accent
 * wash on the canvas surface, mono type, matching the canvas chrome. Not
 * a red/error banner — nothing broke, the layout just changed shape.
 * --------------------------------------------------------------------- */

.cb-canvas-notice {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 16px;
  padding: 12px 16px;
  background: color-mix(in srgb, var(--mt-accent) 12%, var(--m-surface-2));
  border: 1px solid color-mix(in srgb, var(--mt-accent) 35%, var(--mt-border));
  border-left-width: 3px;
  border-radius: var(--m-radius-md, 8px);
}

.cb-canvas-notice__text {
  flex: 1 1 auto;
  color: var(--m-text);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-sm-size, 12px);
  line-height: 1.5;
}

.cb-canvas-notice__dismiss {
  flex: 0 0 auto;
  padding: 5px 14px;
  background: transparent;
  color: var(--mt-accent);
  border: 1px solid color-mix(in srgb, var(--mt-accent) 45%, transparent);
  border-radius: var(--m-radius-sm, 4px);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 11px);
  font-weight: var(--mt-weight-bold, 700);
  cursor: pointer;
  transition: background 80ms, border-color 80ms;
}

.cb-canvas-notice__dismiss:hover {
  background: color-mix(in srgb, var(--mt-accent) 18%, transparent);
  border-color: var(--mt-accent);
}

.cb-canvas-notice__dismiss:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* === Canvas render (v1.7 Phase 42, view-only) ========================== *
 * The freeform layout. .cb-canvas-viewport scales the whole canvas as one
 * unit so it shrinks to fit narrow screens; .cb-canvas-surface is the
 * positioning context the cards sit on; .cb-canvas-empty is the friendly
 * hint shown when a tab has no cards.
 * --------------------------------------------------------------------- */

/* The fit-and-clip shell. A CSS transform shrinks the canvas visually but
 * leaves its real layout box at the full 1600px width and full height — that
 * leftover box is what would otherwise put a sideways scrollbar on a phone
 * and a tall run of empty space below the cards. width:100% + overflow:hidden
 * crop that dead box to the screen; the wrapper's height is pinned inline (in
 * the JS) to the shrunk height so the page scrolls to exactly the content. */
.cb-canvas-viewport {
  width: 100%;
  margin: 0 auto;
  overflow: hidden;
}

/* FIX B (v1.7 Phase 50-06) — in edit mode the inline-pinned height tracks the
 * lowest SAVED card, but a card dragged below that bottom moves via a DOM-only
 * transform with no per-frame state, so the saved-height clip would crop it
 * mid-drag until commit. The .is-editing class (added by CanvasViewport only
 * when showCanvasChrome is true) drops the clip so a low card stays visible
 * DURING the drag. View mode keeps the pinned height + overflow:hidden above
 * (cards there only render once their box is saved — nothing is ever clipped),
 * so the fit-and-clip behaviour on phones/narrow read-only views is unchanged. */
.cb-canvas-viewport.is-editing {
  overflow: visible;
}

/* The surface every card is absolutely positioned against — and the element
 * that actually scales. Built at a fixed 1600px width: on wide screens it
 * keeps that size and centers (margin auto) with empty side gaps; on narrow
 * screens it shrinks as one unit. transform-origin top-left keeps the shrunk
 * content pinned to the visible top-left corner so it never slides off the
 * side of a phone screen. The scale factor arrives as --cb-canvas-scale from
 * the wrapper (a state-driven layout number); the cap-at-1.0 logic lives in
 * the JS. Height is set inline from the lowest card's bottom edge. */
.cb-canvas-surface {
  position: relative;
  width: 1600px;
  margin: 0 auto;
  transform: scale(var(--cb-canvas-scale, 1));
  transform-origin: top left;
  transition: transform 120ms ease;
}

/* Honour a reader's "reduce motion" setting — no animated resizing. */
@media (prefers-reduced-motion: reduce) {
  .cb-canvas-surface {
    transition: none;
  }
}

/* === Portrait single-column reflow (v1.7 Phase 50, MOBILE-03 / D-13) === *
 * On a portrait phone a canvas tab renders CanvasColumn instead of the
 * scaled CanvasViewport: one full-width stack of cards in reading order.
 * Layout/flow only here — the per-item min-height (the card's designed h)
 * is a state-driven number set inline in the JSX, so the deploy aesthetic
 * gate stays green. View-only: no drag/resize/select chrome lives in this
 * branch. No new <link> tag, so no dist-template parity step needed. */
.cb-canvas-column {
  display: flex;
  flex-direction: column;
  width: 100%;
  gap: var(--mt-space-md, 14px);
  box-sizing: border-box;
}
.cb-canvas-column-item {
  width: 100%;
  /* content can grow past the designed-h min-height; never clipped (D-13) */
  height: auto;
  box-sizing: border-box;
}

/* Empty-tab hint — same muted, low-key, mono look as the section-flow
 * empty placeholder, centered within the canvas. */
.cb-canvas-empty {
  max-width: 320px;
  margin: 64px auto;
  padding: 24px 18px;
  border: 1px dashed var(--mt-border-strong, var(--mt-border));
  border-radius: 10px;
  color: var(--mt-muted);
  font-family: var(--m-font-mono);
  font-size: var(--mt-text-xs-size, 12px);
  text-align: center;
}

/* === Drag chrome (v1.7 Phase 43) ====================================== *
 * The three classes the drag mechanic (Plan 02) and the snap/grid plan
 * (Plan 03) wire onto. Defined here, in the already-loaded sheet, so no new
 * <link> tag and no dist-template parity step is needed.
 *
 * The whole card is the grab handle in edit mode (CONTEXT D-01) — there is
 * NO top-edge handle bar. The always-on affordance is just the move cursor.
 * --------------------------------------------------------------------- */

/* Move cursor over a card when edit chrome is on — the always-on "you can
 * grab this" affordance. Applied to the per-card box in edit mode only
 * (gated on showCanvasChrome by the JSX), so view-mode widgets keep their
 * normal cursor. */
.cb-card-grab {
  cursor: move;
}

/* Transparent full-card overlay that sits above the widget in edit mode so
 * a press lands on "drag the card," not "interact with the widget" (e.g. a
 * chart's click-to-isolate). It catches the pointer but paints nothing — no
 * colour token needed because it is fully transparent by design. In view
 * mode the JSX omits it entirely, so widgets stay interactive. */
.cb-card-clickguard {
  position: absolute;
  inset: 0;
  background: transparent;
  cursor: move;
  /* Above the card's own content but below any future selection chrome. */
  z-index: 1;
}

/* Hyperlink (and any widget) edit-mode inertness — v1.7 Phase 50, the complete
 * fix for "a hyperlink card launches its link mid-edit" (todo raised at the
 * Phase 44 human-verify).
 *
 * WHY a pointer-events guard and NOT a z-index bump: the Phase 43 clickguard
 * above (z-index:1, a later SIBLING of <CardRenderer>) is meant to swallow
 * widget clicks, and it does for most widgets. But the hyperlink renderer paints
 * an invisible full-card overlay anchor (position:absolute; inset:0 —
 * card-builder-renderers.jsx:2399) plus a visible inline anchor (:2427). Through
 * the anchor's own stacking context / the browser's anchor-activation path, the
 * navigation still fires past the guard. Raising the guard's z is a band-aid the
 * next overlay-renderer defeats again (Karl's no-band-aid rule). The robust fix:
 * in edit mode the box / guard / handles own ALL interaction, so the rendered
 * widget CONTENT is made inert.
 *
 * SCOPE — deliberately narrow so the three live things stay live:
 *   • the box itself (.cb-card-grab) keeps pointer-events (stays draggable) —
 *     no rule on it here;
 *   • the chrome siblings (.cb-card-clickguard, .cb-resize-handle*,
 *     .cb-card-kebab, .cb-card-lock-glyph) are SIBLINGS of <CardRenderer>, not
 *     descendants, so a rule scoped to the renderer subtree never touches them.
 * <CardRenderer> is the FIRST child of the box (card-builder.jsx ~:5735, before
 * any chrome), so `.cb-card-grab > :first-child` targets exactly the rendered
 * widget. We kill pointer events on it and — belt-and-suspenders — on its
 * a/button descendants. The clickguard still catches the press and turns it into
 * a drag; the link simply never activates.
 *
 * CSS-file rule (NOT an inline style) — the deploy aesthetic gate aborts on
 * inline aesthetic keys; pointer-events on the content subtree is the correct
 * seam (50-RESEARCH § 5, Pitfall 6). */
.cb-card-grab > :first-child {
  pointer-events: none;
}
.cb-card-grab > :first-child a,
.cb-card-grab > :first-child button {
  pointer-events: none;
}

/* Optional grid-line overlay on the canvas surface (default OFF, DRAG-05).
 * A pure-CSS background gradient — NO per-cell DOM nodes. The cell size
 * arrives as --cb-grid-size (Plan 03 drives 4/8/16) and the line colour is a
 * low-contrast theme token so it reads on any theme without an aesthetic
 * literal. Two repeating gradients draw the vertical + horizontal lines. */
.cb-canvas-grid {
  background-image:
    repeating-linear-gradient(
      to right,
      var(--mt-border-lo, var(--mt-border)) 0,
      var(--mt-border-lo, var(--mt-border)) 1px,
      transparent 1px,
      transparent var(--cb-grid-size, 4px)
    ),
    repeating-linear-gradient(
      to bottom,
      var(--mt-border-lo, var(--mt-border)) 0,
      var(--mt-border-lo, var(--mt-border)) 1px,
      transparent 1px,
      transparent var(--cb-grid-size, 4px)
    );
}

/* Snap indicator (v1.7 Phase 43, DRAG-04). Shown only WHILE a card is being
 * dragged: a faint full-surface grid hint sized to the active snap grid so
 * the snap target is visible as the card moves. It reuses the .cb-canvas-grid
 * gradient (pure CSS, no per-cell DOM) and sits absolutely behind the cards.
 * pointer-events:none so it never intercepts the drag. Its own opacity gives
 * the "indicator on" look even when the persistent grid overlay is off. */
.cb-snap-indicator {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0.5;
  pointer-events: none;
  transition: opacity 120ms ease;
}

/* Alt held during a drag turns snap OFF; the indicator visibly dims so the
 * user can see snapping is disabled (D-04). Lower opacity than the active
 * state above — the computed value is strictly smaller, which the browser
 * assertion checks. */
.cb-snap-indicator-dimmed {
  opacity: 0.15;
}

@media (prefers-reduced-motion: reduce) {
  .cb-snap-indicator { transition: none; }
}

/* ── Resize handles (v1.7 Phase 44, RESIZE-01 / RESIZE-02) ──────────────────
 *
 * Eight small grab targets pinned to a card box in edit mode — four corners
 * (nw/ne/sw/se, resize both dimensions) and four edge midpoints (n/s/e/w,
 * resize one dimension). Plan 02 wires the pointer math; this sheet only
 * paints the affordance and sets the directional cursor.
 *
 * D-01 — handles appear on HOVER OR FOCUS of the card box, not on a selection
 * state (selection is Phase 45). The card box already carries .cb-card-grab +
 * tabIndex from Phase 43, so :hover / :focus-within on the box is the natural
 * reveal trigger — no JS selection class is referenced here.
 *
 * Z-order — the base handle sits at z-index:2, strictly ABOVE the Phase 43
 * .cb-card-clickguard (z-index:1), so a press on a handle is hit-tested as a
 * resize, not a drag.
 *
 * Colour contract (v1.4) — fill via var(--mt-accent), edge via
 * var(--mt-border); NO hex / rgb literals. */
.cb-resize-handle {
  position: absolute;
  width: 10px;
  height: 10px;
  box-sizing: border-box;
  background: var(--mt-accent);
  border: 1px solid var(--mt-border);
  border-radius: 2px;
  /* Above the click-guard (z-index:1) so a handle press wins over the drag. */
  z-index: 2;
  /* Hidden until the card box is hovered or focused (revealed below, D-01). */
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}

/* D-01 reveal: any handle inside a hovered or keyboard-focused card box shows
 * and becomes grabbable. The card box is .cb-card-grab (edit-mode only), so
 * view-mode cards never reveal handles. */
.cb-card-grab:hover .cb-resize-handle,
.cb-card-grab:focus-within .cb-resize-handle {
  opacity: 1;
  pointer-events: auto;
}

/* Four corners — pinned to the box corner, centred on it (half the 10px size),
 * each with the diagonal resize cursor it drives. */
.cb-resize-handle--nw { top: -5px; left: -5px; cursor: nwse-resize; }
.cb-resize-handle--se { bottom: -5px; right: -5px; cursor: nwse-resize; }
.cb-resize-handle--ne { top: -5px; right: -5px; cursor: nesw-resize; }
.cb-resize-handle--sw { bottom: -5px; left: -5px; cursor: nesw-resize; }

/* Four edge midpoints — centred on each edge, each with its single-axis
 * resize cursor. */
.cb-resize-handle--n { top: -5px; left: 50%; transform: translateX(-50%); cursor: ns-resize; }
.cb-resize-handle--s { bottom: -5px; left: 50%; transform: translateX(-50%); cursor: ns-resize; }
.cb-resize-handle--e { right: -5px; top: 50%; transform: translateY(-50%); cursor: ew-resize; }
.cb-resize-handle--w { left: -5px; top: 50%; transform: translateY(-50%); cursor: ew-resize; }

/* "Can't shrink further" limit cursor (D-04). Plan 02's handler toggles this
 * class onto the resize surface when a dimension hits the renderer's pixel
 * floor. Hitting a floor is NORMAL, not an error — so this is ONLY a cursor
 * change: no red / alarm colour, no outline or border change (the section
 * editor's red valid=false treatment is deliberately NOT reused). */
.cb-resize-limit {
  cursor: not-allowed;
}
/* The limit cursor must WIN during the active drag. The box's own children
 * carry explicit cursors (each handle's directional resize cursor, the
 * click-guard's `move`) and the pointer sits over one of them while you push
 * inward past the floor — an explicit child cursor beats the box's inherited
 * one, so without these overrides the user keeps seeing the resize/move cursor
 * exactly when the floor is hit (the bug WR-01 caught: the not-allowed cursor
 * never actually showed). Force not-allowed on the cursor-bearing children of a
 * limited box too. Still cursor-only — no red, no outline (D-04). */
.cb-resize-limit .cb-resize-handle,
.cb-resize-limit .cb-card-clickguard {
  cursor: not-allowed;
}

@media (prefers-reduced-motion: reduce) {
  .cb-resize-handle { transition: none; }
}

/* === Phase 45 selection chrome START === */
/* ── Selection + marquee + always-on-top overlay (v1.7 Phase 45) ────────────
 *
 * The visual chrome for multi-select on the freeform canvas (SELECT-01/03,
 * D-01/D-02/D-04/D-10). Plan 02 (shift-click + selection state) and Plan 03
 * (marquee gesture) toggle these classes; this sheet only paints them.
 *
 * WHY a separate overlay layer (D-10): a selected card can be stacked UNDER
 * another card. If we drew the accent border on the card box itself, the
 * card on top would clip the highlight and the user couldn't tell what's
 * selected. So all selection chrome paints on ONE always-on-top layer
 * (.cb-canvas-overlay) that sits above every card but changes NO real card
 * z-order (real stacking is Phase 47). The overlay lives INSIDE the scaled
 * .cb-canvas-surface, so it shrinks with the canvas and its coordinates line
 * up with the cards with zero per-element scale math.
 *
 * Colour contract (v1.4, enforced by ESLint + deploy.sh): every value below
 * is var(--mt-accent) or a color-mix() of it — NO hex / rgb / hsl literals.
 * All four themes define --mt-accent, so selection re-skins on theme switch
 * for free (SELECT-03 / D-01 "survives theme switch").
 * ------------------------------------------------------------------------- */

/* (a) The always-on-top overlay layer (D-10). Fills the whole surface,
 * paints nothing of its own, and never intercepts a press — it is purely
 * the canvas that the outlines, the marquee rectangle, AND the alignment
 * toolbar are drawn on.
 *
 * WHY z-index:40 (gap-closure UAT #1, corrected Phase 47): the selection
 * rings, marquee, and alignment toolbar all paint on this one overlay, and
 * they MUST show over any card no matter how the user stacked it. A card's
 * "bring to front" raises that card's own STORED z (the sparse 10/20/110…
 * value persisted in layout[tabId].z), which has no real ceiling — the stored
 * value only clamps to 0..9999. There is NO 30-value card-render cap constant
 * anywhere (the old comment here claimed one; it never existed). Phase 47
 * closes the seam at the
 * RENDER step instead: DraggableCard renders its box at `Math.min(z, 39)`
 * (card-builder.jsx ~4914) so a fronted card's RENDERED zIndex never exceeds
 * 39 while its stored order survives reload. The overlay and the card boxes
 * are siblings inside .cb-canvas-surface, which is position:relative with NO
 * z-index, so it does NOT open its own stacking context — the children compete
 * on z directly. With the card render capped at 39, this overlay at 40 always
 * wins. FAILURE MODE if you drop this under 40 (or remove the Math.min(z,39)
 * render cap): the toolbar (and selection ring) vanish behind a fronted card
 * and the user can't reach them.
 *
 * Still safely UNDER the modal + the restoring overlay: the app modal is
 * z-index:1000 (position:fixed, root context) and the v1.7 restoring overlay
 * is z-index:2000 — both far above 40, so this canvas chrome never covers a
 * modal. (Selection chrome only renders in canvas edit mode anyway, which is
 * never meaningfully on-screen at the same time as an open modal.)
 *
 * (The +5px outline still shows over a higher-z card without touching that
 * card's real stacking order — real card stacking shipped in Phase 47.) */
.cb-canvas-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none; /* visual layer only (D-10); never intercepts a press */
  z-index: 40;          /* card boxes render at Math.min(z,39); 40 always wins. Under modal(1000)+restoring(2000) */
}

/* (b) One rectangle per selected card, positioned on the overlay in canvas
 * coords (Plan 02 sets left/top/width/height + border-radius inline from
 * placed[], so the ring hugs each card's own corner radius instead of reading
 * as a loose square box). Drawn OUTSIDE the card box (a small negative inset in
 * the JS) so it never covers content or shifts layout.
 *
 * UNMISTAKABLE-LOOK FIX (Karl 2026-05-29): the old 2px border sat right on the
 * card's own border and merged into the bounding box — too subtle. The selection
 * is now a banded HALO built entirely from box-shadow rings, lifted off the card
 * edge by a 2px gap in the canvas background so the accent never blends into the
 * card's border:
 *   ring 1 — 2px var(--mt-bg) gap        → separates the accent from the card edge
 *   ring 2 — 3px solid var(--mt-accent)  → the bold "this is selected" band
 *   ring 3 — soft 24px outer halo        → stays visible when a higher-z card
 *                                           overlaps the selected one (D-10)
 * Bold band + strong glow reads instantly and can NOT be confused with the thin
 * hover resize-dots or the keyboard focus ring. No fill (D-01: outline + glow,
 * not fill — a fill would fight live chart/gauge content; fill is the marquee's
 * job). Every value is var(--mt-accent)/var(--mt-bg) or a color-mix of accent,
 * so the whole ring re-skins on a theme switch (SELECT-03 / D-01). */
.cb-card-selected {
  position: absolute;
  /* Hug the card's own corner radius (set inline as --cb-sel-radius from the
   * card's borderRadius + outset); fall back to a gentle 8px if unset. */
  border-radius: var(--cb-sel-radius, 8px);
  box-shadow:
    0 0 0 2px var(--mt-bg),
    0 0 0 5px var(--mt-accent),
    0 0 24px 6px color-mix(in srgb, var(--mt-accent) 55%, transparent);
  pointer-events: none;
  transition: box-shadow 120ms ease-out;
}

/* (c) The rubber-band rectangle (D-02 / SELECT-01). One reused node, moved by
 * setting left/top/width/height inline during the sweep — no DOM allocation
 * per frame. 1px accent border (thinner than the 2px selection outline so the
 * two never read alike) + a 12% translucent accent fill (Finder/Figma style:
 * reads as "swept" without obscuring the cards underneath). */
.cb-marquee {
  position: absolute;
  border: 1px solid var(--mt-accent);
  background: color-mix(in srgb, var(--mt-accent) 12%, transparent);
  pointer-events: none;
}

/* (d) The 1-vs-2+ resize-dot gate (D-04 / Pitfall 5). The Phase 44 hover /
 * :focus-within reveal at :5744 stays as-is for the 0-or-1-selected case.
 * This adds two things on TOP of that:
 *
 *  - .cb-card-selected--solo : Plan 02 adds this to the single selected card
 *    when EXACTLY ONE is selected, forcing its resize dots visible even
 *    without a hover (selecting one card = ready to resize it). Mirrors the
 *    Phase 44 reveal rule; geometry untouched (the -5px handle insets stay
 *    Phase 44 values — D-04 only changes WHEN the dots show, never their size
 *    or position). */
.cb-card-selected--solo .cb-resize-handle {
  opacity: 1;
  pointer-events: auto;
}

/*  - .cb-group-mode : Plan 02 adds this to the surface when 2+ cards are
 *    selected. Group mode is move/align, not resize, so the dots must HIDE on
 *    every card — including a card that is also hovered or solo-flagged. These
 *    rules win because they are more specific (.cb-group-mode ancestor) than
 *    the bare hover/focus/solo reveals above, so a stray hover during a group
 *    drag can't flash a card's dots back on. */
.cb-group-mode .cb-card-grab:hover .cb-resize-handle,
.cb-group-mode .cb-card-grab:focus-within .cb-resize-handle,
.cb-group-mode .cb-card-selected--solo .cb-resize-handle {
  opacity: 0;
  pointer-events: none;
}

/* (e) The floating alignment toolbar (v1.7 Phase 46, ALIGN-01/02/03, D-02/D-03).
 *
 * A small elevated-glass pill of 8 icon-only buttons that floats above a
 * multi-card selection. It deliberately reads as a first-class member of the
 * existing edit chrome — same --mt-bg-elev material, border, and accent
 * language as the gear / tab-picker popovers and the selection ring above — so
 * it belongs to the same family rather than looking like a bolted-on widget.
 *
 * Colour contract (same as the rest of this block): every value is a theme
 * token or a color-mix() of one — NO hex / rgb / hsl literals (ESLint
 * manifold-local/no-inline-aesthetic-styles + deploy.sh enforced). All four
 * themes define these tokens, so the whole toolbar re-skins on a theme switch.
 *
 * The component sets ONLY layout/position numbers inline (left/top/zIndex/
 * display/opacity); every aesthetic value lives here on the classes.
 *
 * QUIET-UNTIL-REACHED: accent (--mt-accent) lights up ONLY the control under
 * the pointer (hover icon + wash, active wash, focus ring). The pill body,
 * dividers, resting icons, and tooltip never use accent — mirroring how the
 * selection ring uses accent only on what's selected. */

/* The pill itself. The overlay layer it sits on is pointer-events:none, so the
 * pill re-enables pointer events for its buttons. fade is opacity-only (D-05);
 * the instant hide during a gesture is the inline display:none (D-02). */
.cb-align-toolbar {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  pointer-events: auto;             /* the overlay is none; the pill is clickable */
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border-strong);
  border-radius: var(--cb-tb-radius, 8px);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--mt-bg-deep) 50%, transparent);
  transition: opacity 120ms ease-out;
}

/* The thin band between clusters (4 aligns | 2 centers | 2 distributes). */
.cb-tb-divider {
  width: 1px;
  height: 16px;
  margin: 0 8px;
  background: var(--mt-border);
}

/* One icon button. 28px dense-chrome hit target (mouse-only — the toolbar is
 * hidden below 768px, so the 44px touch minimum doesn't apply). Resting icons
 * are --mt-fg at 70% so they read as "available" not "shouting." position:
 * relative anchors the custom tooltip. */
.cb-tb-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: var(--m-radius-sm, 4px);
  background: transparent;
  color: color-mix(in srgb, var(--mt-fg) 70%, transparent);  /* icon = currentColor */
  cursor: pointer;
  transition: background 120ms ease-out, color 120ms ease-out;
}
/* The glyph fills/strokes from the button's color so one rule skins all 8. */
.cb-tb-btn svg { fill: currentColor; }
.cb-tb-btn svg line { stroke: currentColor; }

/* Hover: full accent on the icon under the pointer + the same subtle wash the
 * existing .is-active buttons use. */
.cb-tb-btn:hover {
  color: var(--mt-accent);
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
}
/* Pressed: wash deepens for the moment the mouse is down. */
.cb-tb-btn:active {
  background: color-mix(in srgb, var(--mt-accent) 22%, transparent);
}
/* Keyboard focus: the same accent-glow language as the selection ring, so focus
 * reads as "this is the live tool" consistently across the canvas. */
.cb-tb-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--mt-accent) 60%, transparent);
}

/* Disabled (the two distribute buttons below 3 selected, D-10). Clearly dimmer
 * than the 70% resting icons, no hover wash, not-allowed cursor. Pointer events
 * stay on (the component drops the click handler, not the events) so the
 * teaching tooltip can still explain WHY it's off. */
.cb-tb-btn--disabled {
  color: color-mix(in srgb, var(--mt-fg) 28%, transparent);
  cursor: not-allowed;
}
.cb-tb-btn--disabled:hover {
  color: color-mix(in srgb, var(--mt-fg) 28%, transparent);
  background: transparent;
}

/* The custom tooltip (not the browser title= attribute — that's slow,
 * unstyleable, and can't be theme-tokenized). Sits below the button, centered,
 * and never intercepts the pointer. */
.cb-tb-tooltip {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  padding: 4px 8px;
  font-size: 12px;
  font-weight: 500;
  color: var(--mt-fg);
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border);
  border-radius: var(--m-radius-sm, 4px);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--mt-bg-deep) 50%, transparent);
  pointer-events: none;
}

/* The align/distribute MOVE tween (D-04). A transient class added to each moved
 * card box at commit and removed ~160ms later. It transitions left/top ONLY —
 * NEVER transform: the live drag path writes transform per frame, so a transform
 * transition here would lag every drag (Pitfall 2). Scoping the tween to this
 * transient, commit-only class is the belt-and-suspenders guarantee that the
 * 150ms ease can never bleed into a drag. */
.cb-aligning {
  transition: left 150ms cubic-bezier(0.2, 0, 0, 1),
              top  150ms cubic-bezier(0.2, 0, 0, 1);
}

/* Honour "reduce motion" for the new chrome, mirroring :5703 / :5785. The
 * selection outline and marquee carry no transition by default; the Phase 46
 * toolbar fade, button accent, and align tween must all go instant for a
 * reduced-motion user. */
@media (prefers-reduced-motion: reduce) {
  .cb-card-selected,
  .cb-marquee,
  .cb-align-toolbar,
  .cb-tb-btn,
  .cb-aligning,
  .cb-card-menu,
  .cb-card-menu-item,
  .cb-card-kebab {
    transition: none;
  }
}
/* === Phase 45 selection chrome END === */

/* === Phase 47 card chrome: context menu, kebab, lock glyph, overlap ===
 *
 * Z-02 / LOCK-01 / LOCK-03 / Z-03. This is NOT a new surface style — it is the
 * established Phase 46 edit-chrome vocabulary (the .cb-align-toolbar glass
 * material + .cb-tb-btn--disabled dimming + .cb-tb-tooltip) applied to a new
 * per-card menu. Every value is a theme token or a color-mix() off one; there
 * are NO hex/rgb/hsl literals (ESLint manifold-local/no-inline-aesthetic-styles
 * + deploy.sh enforce this). Inline JSX styles for this chrome are limited to
 * layout/position numbers (left/top/zIndex/minWidth/opacity) — Plans 02/03 wire
 * the interactivity against these classes.
 *
 * The four tokens these reuse, same as the alignment toolbar:
 *   --mt-bg-elev      the elevated-glass surface
 *   --mt-border-strong  the popover border
 *   --mt-border       structural lines (the divider)
 *   --mt-fg / --mt-accent  resting text / pointer accent
 */

/* (1) The popover body. Same glass material as .cb-align-toolbar (:5942):
 * --mt-bg-elev surface, --mt-border-strong edge, --cb-tb-radius corner,
 * --mt-bg-deep shadow. z-index:50 puts it ABOVE the canvas-overlay (40) so the
 * menu always paints over the selection chrome — and still safely under the
 * modal (1000). JS sets left/top from the click point (inline, allowed). */
.cb-card-menu {
  /* fixed (not absolute): the menu is portaled to <body> and anchored at the
   * viewport clientX/clientY of the right-click or kebab press, so it must NOT
   * inherit the scaled .cb-canvas-surface transform (which would shift + scale
   * it). position:fixed reads left/top as viewport coords — exactly the
   * gear/tab-picker popover pattern (helpers.css:4618). */
  position: fixed;
  background: var(--mt-bg-elev);
  border: 1px solid var(--mt-border-strong);
  border-radius: var(--cb-tb-radius, 8px);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--mt-bg-deep) 50%, transparent);
  padding: 4px;
  min-width: 180px;
  z-index: 50;
  transition: opacity 120ms ease-out;
}

/* (2) A menu row at rest. 28px dense-chrome target (same as .cb-tb-btn);
 * 13px/400 label a hair under body so a 5-row column reads as compact edit
 * chrome. Resting label at --mt-fg 85% — slightly stronger than the toolbar's
 * 70% resting icons because text needs more contrast than a glyph to read. */
.cb-card-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 28px;
  padding: 6px 8px;
  border-radius: var(--m-radius-sm, 2px);
  font-size: 13px;
  font-weight: 400;
  color: color-mix(in srgb, var(--mt-fg) 85%, transparent);
  background: transparent;
  cursor: pointer;
  transition: background 120ms ease-out, color 120ms ease-out;
}
/* One rule skins every inline-SVG glyph in a row (currentColor drives the
 * resting / hover / disabled color rules with zero per-icon color code). */
.cb-card-menu-item svg {
  fill: currentColor;
  stroke: currentColor;
}
/* Hover: accent label + the same 13% accent wash the Phase 46 buttons use. */
.cb-card-menu-item:hover {
  color: var(--mt-accent);
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
}
/* Pressed: the wash deepens to 22% (same as .cb-tb-btn:active). */
.cb-card-menu-item:active {
  background: color-mix(in srgb, var(--mt-accent) 22%, transparent);
}
/* Keyboard focus: the accent-glow ring shared with the toolbar + selection. */
.cb-card-menu-item:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--mt-accent) 60%, transparent);
}
/* Disabled = the four z-order rows on a LOCKED card. Dimmed to --mt-fg 35%
 * (35% not 28%: text needs marginally more presence than a glyph to stay
 * legible-but-off), not-allowed cursor, NO accent wash. pointer-events stay
 * ON (the component drops the click handler, not the events) so the
 * "unlock first" teaching tooltip still fires — exactly the Phase 46
 * .cb-tb-btn--disabled pattern. The Lock/Unlock row is NEVER disabled. */
.cb-card-menu-item.is-disabled {
  color: color-mix(in srgb, var(--mt-fg) 35%, transparent);
  cursor: not-allowed;
}
.cb-card-menu-item.is-disabled:hover {
  color: color-mix(in srgb, var(--mt-fg) 35%, transparent);
  background: transparent;
}
/* Destructive row (Remove from tab, issue C). Resting state stays neutral so the
 * menu isn't a wall of red; intent turns it red on hover / press / focus. Scoped
 * :not(.is-disabled) so a LOCKED card's greyed remove row keeps the neutral dim
 * above instead of flashing red. --mt-err is the same danger token the
 * binding-drift error uses (this file, ~line 229). */
.cb-card-menu-item--danger:not(.is-disabled):hover {
  color: var(--mt-err, #ef4444);
  background: color-mix(in srgb, var(--mt-err, #ef4444) 14%, transparent);
}
.cb-card-menu-item--danger:not(.is-disabled):active {
  background: color-mix(in srgb, var(--mt-err, #ef4444) 22%, transparent);
}
.cb-card-menu-item--danger:not(.is-disabled):focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--mt-err, #ef4444) 60%, transparent);
}

/* (3) The cluster divider between the 4 z-order rows and Lock/Unlock. A
 * horizontal counterpart of .cb-tb-divider. */
.cb-card-menu-divider {
  height: 1px;
  margin: 4px 0;
  background: var(--mt-border);
}

/* (4) The ⋮ kebab button — discoverability + touch path. 24px corner
 * affordance, top-right, hidden until the card is hovered or selected (mirrors
 * the Phase 44/45 resize-handle reveal). Resting glyph at --mt-fg 70%; hover
 * lights it accent like a toolbar button. */
.cb-card-kebab {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--m-radius-sm, 2px);
  background: transparent;
  color: color-mix(in srgb, var(--mt-fg) 70%, transparent);
  opacity: 0;
  cursor: pointer;
  transition: opacity 120ms ease-out, background 120ms ease-out, color 120ms ease-out;
}
.cb-card-kebab svg {
  fill: currentColor;
}
/* Reveal on card hover or when the card is the solo selection (same selectors
 * the resize handles use). */
.cb-card-grab:hover .cb-card-kebab,
.cb-card-selected--solo .cb-card-kebab {
  opacity: 1;
}
.cb-card-kebab:hover {
  color: var(--mt-accent);
  background: color-mix(in srgb, var(--mt-accent) 13%, transparent);
}
.cb-card-kebab:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--mt-accent) 60%, transparent);
}

/* (5) The lock glyph (LOCK-03) — a small filled padlock, top-left, edit mode
 * only. Passive: non-interactive, quiet --mt-fg 70%, never accent (accent is
 * reserved for pointer interaction; this is a status mark). */
.cb-card-lock-glyph {
  position: absolute;
  top: 4px;
  left: 4px;
  width: 16px;
  height: 16px;
  pointer-events: none;
  color: color-mix(in srgb, var(--mt-fg) 70%, transparent);
}
.cb-card-lock-glyph svg {
  fill: currentColor;
}

/* (6) The overlap outline (Z-03) — 1px DASHED, drawn as an outline (not a
 * border) with a 2px offset so it sits just outside the card box and never
 * shifts the card's own layout or content. Dashed + neutral --mt-border-strong
 * (not accent) so it reads as "these are stacked," never as a selection ring. */
.cb-card-overlap {
  outline: 1px dashed color-mix(in srgb, var(--mt-border-strong) 80%, transparent);
  outline-offset: 2px;
}
/* === Phase 47 card chrome END === */

/* === Phase 48 undo/redo feedback (D-03) === *
 * .undo-highlight — the transient flash applied to a card box right after an
 * undo/redo re-applies, so the user SEES which card just changed. The JS adds
 * the class, scrolls the card into view, and strips the class after ~1s; the
 * CSS does the rest with a single keyframed pulse that starts bright and fades
 * to nothing on its own (so even if the strip were missed, the card settles
 * back to a calm ring). Built on --mt-accent — the same accent every pointer
 * interaction uses — so it reads as "the system did this," distinct from the
 * neutral overlap dashes and the passive lock glyph. The 2px outline-offset
 * matches .cb-card-overlap so the ring sits just outside the box without
 * nudging layout, and the box-shadow adds a soft glow an outline alone can't.
 * Honors prefers-reduced-motion: no pulse, just a brief steady ring. */
@keyframes cb-undo-flash {
  0% {
    outline-color: var(--mt-accent, #00d992);
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--mt-accent, #00d992) 60%, transparent),
                0 0 16px 2px color-mix(in srgb, var(--mt-accent, #00d992) 55%, transparent);
  }
  60% {
    outline-color: var(--mt-accent, #00d992);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--mt-accent, #00d992) 30%, transparent),
                0 0 22px 4px color-mix(in srgb, var(--mt-accent, #00d992) 35%, transparent);
  }
  100% {
    outline-color: transparent;
    box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent;
  }
}
.undo-highlight {
  outline: 2px solid var(--mt-accent, #00d992);
  outline-offset: 2px;
  border-radius: 2px;
  animation: cb-undo-flash 1s ease-out forwards;
  /* keep the glow above neighbouring boxes so the ring isn't clipped */
  z-index: 9000;
}
@media (prefers-reduced-motion: reduce) {
  .undo-highlight {
    animation: none;
    outline: 2px solid color-mix(in srgb, var(--mt-accent, #00d992) 70%, transparent);
    box-shadow: none;
  }
}

/* -------------------------------------------------------------------------
 * Recent-changes history panel (Phase 48-05, UNDO-04 / D-04).
 *
 * The gear → Design hub → "Recent changes" modal renders the active tab's
 * last 5 undoable actions as a TIMELINE: newest at the top, a thin vertical
 * spine threading the markers so the depth of a rewind reads at a glance
 * (click row 3 and you can see the two newer entries above it that go with
 * it). Each row is "label … undo-back-here". The revert button stays quiet
 * until you hover/focus the row, so the list reads as a calm changelog, not
 * a wall of buttons — then the action you want lights up in brand-amber.
 *
 * Cohesive with the existing dark / monospace chrome: lives inside the
 * shared .manifold-app-modal-body, uses --mt-accent (the same accent the
 * undo-flash and every pointer interaction use) so "undo" reads as the
 * same system gesture across the modal AND the canvas flash. No new font,
 * no new color scheme — restraint over novelty (the surrounding system is
 * already the design).
 * ------------------------------------------------------------------------- */
.manifold-undo-history-empty {
  font-family: var(--mt-mono, ui-monospace, monospace);
  font-size: 13px;
  line-height: 1.5;
  color: color-mix(in srgb, var(--mt-fg, #e5e7eb) 55%, transparent);
  padding: 8px 2px 4px;
}

.manifold-undo-history-list {
  list-style: none;
  margin: 0;
  padding: 4px 0 4px 2px;
  position: relative;
}
/* the timeline spine: a faint vertical line behind the markers, inset to
 * sit under the marker centers. Drawn from the first marker to the last. */
.manifold-undo-history-list::before {
  content: '';
  position: absolute;
  left: 6px;
  top: 14px;
  bottom: 14px;
  width: 1px;
  background: color-mix(in srgb, var(--mt-accent, #00d992) 28%, transparent);
}
/* while a rewind is in flight, dim + lock the whole list so a second click
 * can't fire mid-cascade. */
.manifold-undo-history-list[data-busy='true'] {
  opacity: 0.55;
  pointer-events: none;
}

.manifold-undo-history-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 4px 7px 22px;
  border-radius: 4px;
  transition: background 120ms ease;
}
.manifold-undo-history-row:hover,
.manifold-undo-history-row:focus-within {
  background: color-mix(in srgb, var(--mt-accent, #00d992) 8%, transparent);
}

/* the marker dot on the spine. The newest entry (first row) reads as the
 * "now" point — a filled amber dot; the rest are hollow rings. */
.manifold-undo-history-marker {
  position: absolute;
  left: 2px;
  top: 50%;
  transform: translateY(-50%);
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1.5px solid var(--mt-accent, #00d992);
  background: var(--mt-panel, #0c0f14);
  box-sizing: border-box;
}
.manifold-undo-history-row:first-child .manifold-undo-history-marker {
  background: var(--mt-accent, #00d992);
  box-shadow: 0 0 8px 0 color-mix(in srgb, var(--mt-accent, #00d992) 50%, transparent);
}

.manifold-undo-history-label {
  flex: 1 1 auto;
  font-family: var(--mt-mono, ui-monospace, monospace);
  font-size: 13px;
  color: var(--mt-fg, #e5e7eb);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.manifold-undo-history-revert {
  flex: 0 0 auto;
  font-family: var(--mt-mono, ui-monospace, monospace);
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--mt-accent, #00d992);
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--mt-accent, #00d992) 40%, transparent);
  border-radius: 3px;
  padding: 3px 8px;
  cursor: pointer;
  /* quiet until the row is hovered/focused — keeps the list a calm changelog */
  opacity: 0;
  transition: opacity 120ms ease, background 120ms ease, border-color 120ms ease;
}
.manifold-undo-history-row:hover .manifold-undo-history-revert,
.manifold-undo-history-row:focus-within .manifold-undo-history-revert,
.manifold-undo-history-revert:focus-visible {
  opacity: 1;
}
.manifold-undo-history-revert:hover {
  background: color-mix(in srgb, var(--mt-accent, #00d992) 18%, transparent);
  border-color: var(--mt-accent, #00d992);
}
.manifold-undo-history-revert:disabled {
  cursor: default;
  opacity: 0.4;
}
/* touch / no-hover devices can't reveal-on-hover; show the buttons always. */
@media (hover: none) {
  .manifold-undo-history-revert { opacity: 1; }
}
