/* Rules editor — design tokens + structural styles.

   Values lifted from docs/design-references/design_handoff_rules_editor/source/app.jsx
   :: useThemeVars (lines 18-69 there). The artifact applies them via a React inline-style
   prop on a wrapper div; in Blazor we apply them via a wrapper class so they cascade to
   every editor descendant without leaking to the rest of Threshold.

   Token names match the artifact 1:1 — translating the *decision* (small chip, off-white
   surface, tight padding) into Razor markup, not copying Tailwind class strings. The mono
   stack uses JetBrains Mono per the artifact; index.html already loads it via the existing
   Bootstrap fonts pipeline, so no new <link> needed beyond rules-editor.css itself.

   Density toggle and per-section pill palettes are deferred to later tasks (state 1+ density
   isn't surfaced in state 3; section pill colors will land when the state palette grows past
   the three sections state-3 contains). */

.cr-rules-editor {
    --re-fg:          #3B3F4C;
    --re-muted:       #7B8AA1;
    --re-muted-2:     #9BA6B7;
    --re-placeholder: #B7C0CC;
    --re-surface:     #FBFCFD;
    --re-surface-2:   #F4F6F9;
    --re-bd:          #E4E8EE;
    --re-bd-strong:   #C9D1DC;
    --re-row-hover:   #EEF2F7;

    /* Teal accent matches the artifact's TWEAK_DEFAULTS preset; line up with the existing
       Advant brand blue (--advant) only after design has signed off on the swap. */
    --re-accent:        #1AB394;
    --re-accent-strong: #0F8E76;
    --re-accent-soft:   #DDF2EC;

    --re-or:      #7B70EF;
    --re-or-soft: #EDEBFD;

    --re-field-bg:   #F1F6FC;
    --re-field-bd:   #CFE0F2;
    --re-field-bd-h: #A8C8EA;
    --re-field-fg:   #1F4F8B;

    --re-system-bg: #FFF4E2;
    --re-system-bd: #F5D9A8;
    --re-system-fg: #A06A1B;

    --re-not-bg: #FFEEEF;
    --re-not-bd: #F1C4C9;
    --re-not-fg: #C75766;

    --re-danger:      #E15B6E;
    --re-danger-soft: #FCE3E6;

    /* Warning palette — used by DiagInline (Warning), EmptyGroupCard (draft chrome), and the
       Warning-flavored save-with-warnings footer. Distinct from --info (which is the system
       palette for OffsetEditor) so the registers stay separable. */
    --re-warning:        #C7951C;
    --re-warning-soft:   #FBF3DC;
    --re-warning-strong: #7A5A0F;
    --re-warning-dashed: #C9A95B;
    --re-warning-card:   #FBF6E5;

    /* === DepthTints === per the design handoff's component-separation §3 (DepthTints).
       Four-step palette so depth 4 doesn't collapse with depth 2.

       Family: faint Advant-blue tinted off-whites (hue source = brand primary --advant).
       Earlier iterations tried teal-shifted (competed with the teal AND-marker on the
       group's left guide line) and warm-neutral sandstone (rejected). Advant blue is
       blue-leaning where the AND-marker teal is green-leaning, so the eye separates
       "teal = AND" from "blue-tinted = surface". Three distinct hue channels:
       surface = depth (Advant blue), teal = AND only, purple = OR only.

       No yellow / sandstone cast — OffsetEditor's system pill owns warm-yellow as the
       "system value" register; depth surfaces stay cool-neutral so they don't collide
       with that channel either.

       Tune as a series, not in isolation — adjust by opening Conditional Admission
       Track (state 6) and viewing all four levels at once. The right answer is the
       lightest scale that still gives clean perceptible depth steps. */
    --re-group-1: #F4FAFB;
    --re-group-2: #E8F4F7;
    --re-group-3: #DDEFF4;
    --re-group-4: #C7E5EC;

    --re-dep-bg:     #F1EEFB;
    --re-dep-bd:     #C8BEED;
    --re-dep-fg:     #5C45C7;
    --re-dep-strong: #3F2BA0;

    --re-info-bg: #FFF7E2;
    --re-info-bd: #F2D88B;
    --re-info-fg: #8A6313;

    /* Chip surface — the "white card" register used by the And/Or toggle, joiner pills,
       operator pills, value pills, and multi-chip inputs. Folded into a token so the
       dark-mode counterpart inverts in one place rather than five. */
    --re-chip-bg: #fff;

    /* === Task 5 (editor popovers + clickable chrome) ===

       Popover surface used by FieldPickerMenu / OperatorPickerMenu / OperandModeMenu —
       one set so the three popovers read identically. Independent from --re-chip-bg so
       the popover can have its own elevation register (slightly higher contrast against
       the page than a chip would). */
    --re-popover-bg:        #ffffff;
    --re-popover-border:    var(--re-bd);
    --re-popover-shadow:    0 6px 24px rgba(28, 39, 56, 0.12), 0 2px 6px rgba(28, 39, 56, 0.06);
    --re-popover-search-bg: var(--re-surface-2);

    /* Picker rows — match the row-hover register for consistency with the rest of the
       editor, plus an "active" tone for the currently-selected operator / mode. */
    --re-picker-row-bg-hover:   var(--re-row-hover);
    --re-picker-row-fg:         var(--re-fg);
    --re-picker-row-active-bg:  var(--re-accent-soft);
    --re-picker-row-active-fg:  var(--re-accent-strong);
    --re-picker-section-fg:     var(--re-muted);

    /* Placeholder chip ("Field…") — dashed border, neutral palette. Distinct from
       MissingFieldChip's --re-danger family because "waiting for input" is structurally
       different from "wrong / dangling reference". */
    --re-placeholder-fg:     var(--re-muted);
    --re-placeholder-border: var(--re-bd-strong);

    /* Dirty / saved indicators. The dirty dot reads as a quiet "edited" marker — uses
       --re-accent so it lines up with the affirmative-action color register without being
       loud. Saved pill uses a soft accent palette and auto-fades after ~2s via CSS
       animation. Both decoupled from the diagnostic palette (--re-danger, --re-warning) so
       the registers don't collide. */
    --re-dirty-dot-bg:   var(--re-accent);
    --re-saved-pill-bg:  var(--re-accent-soft);
    --re-saved-pill-fg:  var(--re-accent-strong);

    /* === Task 7b (arithmetic editing affordances) ===

       Hover/focus reveal tokens for the in-arithmetic editing UI: × per operand to
       remove a term, + at the right end of the arithmetic to add a term. Operator
       glyphs become clickable buttons; the operand chip is already click-aware via
       its existing FieldChip wiring. Reveal-on-hover semantics live in the cr-arith-node
       class rules below; these tokens just supply the palette so dark mode can adjust
       without touching the rules. */
    --re-arith-operand-hover-bg:  var(--re-row-hover);
    --re-arith-operator-hover-bg: var(--re-row-hover);
    --re-arith-term-remove-fg:    var(--re-danger);
    --re-arith-term-add-fg:       var(--re-accent-strong);

    /* Per-operand fx button (Task 7d) — light. Sits alongside the × per-operand remove
       button inside an arithmetic operand, hover-revealed in the same register. Click
       opens an OperandModeMenu restricted to Literal / FieldReference. */
    --re-arith-operand-fx-fg:     var(--re-muted);
    --re-arith-operand-fx-border: var(--re-token-border);

    /* Wrap / ungroup affordances — light. Wrap (…) sits between an operator and its
       rightward operand; ungroup × sits at the top-right of a group token. Both share
       the muted register with the +/× affordances so they read as secondary editing
       chrome rather than primary commands. */
    --re-arith-wrap-fg:           var(--re-muted);
    --re-arith-wrap-hover-bg:     var(--re-row-hover);
    --re-arith-ungroup-fg:        var(--re-muted);
    --re-arith-ungroup-hover-bg:  var(--re-row-hover);

    /* Editor-input chrome — used by every inline-editing branch (TextLiteral input,
       DateLiteral date input, OffsetEditor amount input + unit select). One register so
       a focused input reads identically across all editor primitives. */
    --re-editor-input-bg:         #ffffff;
    --re-editor-input-border:     var(--re-bd-strong);
    --re-editor-input-focus-ring: rgba(26, 179, 148, 0.32);

    /* Jump-to-AstPath flash — yellow-amber pulse so it reads as "attention here" without
       claiming the element is in an error state. Two-tone glow lifts the target out of
       the rule chrome briefly. */
    --re-flash-color: rgba(247, 195, 79, 0.7);

    /* === Task 6 (editor shell) ===
       Empty state surface. Used by the bare /rules-editor route and by /rules-editor/{miss}
       to show "Pick a rule to edit" / "Rule not found" with a + New rule button. Visual
       weight is subdued — empty states are quiet by design, the message is the content. */
    --re-empty-bg:        var(--re-surface-2);
    --re-empty-fg:        var(--re-fg);
    --re-empty-muted-fg:  var(--re-muted);

    /* EditorHeader surface. Sits above the rule body on the single-rule route; the
       breadcrumb, last-edited line, name, and description live here. The hover-hint
       affordance on the editable name/description uses --re-edit-hint-fg for the
       subtle pencil/underline cue when not editing. Inputs use the editor-input chrome
       family so a focused input reads identically to inline value editors. */
    --re-header-bg:              transparent;
    --re-header-breadcrumb-fg:   var(--re-muted-2);
    --re-header-last-edited-fg:  var(--re-muted);
    --re-header-name-fg:         var(--re-fg);
    --re-header-description-fg:  var(--re-muted);
    --re-header-input-bg:        var(--re-editor-input-bg);
    --re-header-input-border:    var(--re-editor-input-border);
    --re-header-input-focus-ring:var(--re-editor-input-focus-ring);
    --re-edit-hint-fg:           var(--re-muted-2);

    /* TargetTypePill surface. Subdued chip; intentionally NOT diagnostic-palette so it
       reads as a "tag" not as a status indicator. */
    --re-target-pill-bg:         var(--re-chip-bg, #f4f6fa);
    --re-target-pill-fg:         var(--re-fg);
    --re-target-pill-border:     var(--re-bd);
    --re-target-pill-hover-bg:   var(--re-row-hover);

    /* "Used by …" line. Slugs render in a monospace chip with a slightly tinted
       background so they read as developer-text identifiers (matching the header
       breadcrumb's slug aesthetic). The leading "Used by" label uses --re-muted; the
       slug pills use --used-by-slug-fg so they pop slightly above the muted register
       without claiming danger/warning palette. */
    --re-used-by-fg:         var(--re-muted);
    --re-used-by-slug-bg:    var(--re-chip-bg, #f4f6fa);
    --re-used-by-slug-fg:    var(--re-fg);
    --re-used-by-slug-border:var(--re-bd);

    /* Local typography. Threshold's body font (Hanken Grotesk via threshold-tokens.css's
       --f-sans) is inherited rather than redeclared; the artifact's Open Sans 13px
       translates 1:1 to Hanken Grotesk 13px without a per-editor font override. */
    color: var(--re-fg);
    font-size: 13px;
    line-height: 1.5;
}

/* Dark-mode counterpart. Convention: redeclare every theme-specific token under the
   [data-bs-theme="dark"] scope. Threshold's dark canvas (--paper / --paper-2 / --paper-3
   / --paper-sunk per threshold-tokens.css) is warm-dark at hue 60° per ADR-0035 §11.

   Direction of inversion per the design brief:
   - Surfaces / borders / text invert to dark equivalents that align with Threshold.
   - Accent / OR / danger stay vivid (vivid colors read well on both bgs).
   - Soft variants (--re-accent-soft, --re-or-soft, --re-danger-soft, etc.) become low-alpha
     overlays so the tinted treatment reads as "register marker" rather than "white island".
   - Depth tints invert direction: in light mode each step is darker than the page (lift
     downward from canvas); in dark mode each step is darker than the dark canvas, with
     the deepest depth at the most lift. Faint teal cast preserved through the series so
     the hue family stays consistent with light mode. Calibrate as a series — open Rule A
     in dark mode and adjust until each step is perceptibly distinct without reading as
     alarmed. */
[data-bs-theme="dark"] .cr-rules-editor {
    --re-fg:          #adb5bf;
    --re-muted:       rgba(173, 181, 191, 0.75);
    --re-muted-2:     rgba(173, 181, 191, 0.55);
    --re-placeholder: rgba(173, 181, 191, 0.4);
    --re-surface:     #17181e;
    --re-surface-2:   #1b1c22;
    --re-bd:          #2c2d38;
    --re-bd-strong:   #3a3b46;
    --re-row-hover:   rgba(255, 255, 255, 0.04);

    /* Teal accent stays vivid (matches --bs-primary in dark mode); --re-accent-strong
       lifts slightly so And-toggle text reads on the rgba accent-soft pill. */
    --re-accent:        #1AB394;
    --re-accent-strong: #2dd4ad;
    --re-accent-soft:   rgba(26, 179, 148, 0.18);

    --re-or:      #9089f5;
    --re-or-soft: rgba(123, 112, 239, 0.18);

    /* FieldChip palette — translucent blue overlays so the chip reads as elevated
       container rather than as a hard white island. --re-field-fg lightens to a readable
       blue against the rgba bg. */
    --re-field-bg:   rgba(58, 124, 200, 0.14);
    --re-field-bd:   rgba(58, 124, 200, 0.38);
    --re-field-bd-h: rgba(58, 124, 200, 0.55);
    --re-field-fg:   #82b6f0;

    /* OffsetEditor / system-pill — translucent amber for the "system value" register. */
    --re-system-bg: rgba(245, 217, 168, 0.10);
    --re-system-bd: rgba(245, 217, 168, 0.32);
    --re-system-fg: #f0c068;

    /* NotWrapper — translucent pink. Tab uses --re-not-fg; bumped slightly so the tab
       still reads with white text against the dark canvas. */
    --re-not-bg: rgba(241, 196, 201, 0.08);
    --re-not-bd: rgba(241, 196, 201, 0.30);
    --re-not-fg: #d56c79;

    /* Danger stays vivid (matches --bs-danger / --risk in dark mode). */
    --re-danger:      #fb5858;
    --re-danger-soft: rgba(251, 88, 88, 0.16);

    /* Warning palette — vivid amber on dark canvas; soft surfaces become low-alpha overlays
       so DiagInline / EmptyGroupCard read as register markers rather than as bright tan
       islands. --re-warning-strong lifts to a brighter amber so the WORD prefix reads against
       the dark background. */
    --re-warning:        #f2c24c;
    --re-warning-soft:   rgba(242, 194, 76, 0.14);
    --re-warning-strong: #f3c66e;
    --re-warning-dashed: rgba(242, 194, 76, 0.55);
    --re-warning-card:   rgba(242, 194, 76, 0.07);

    /* Depth tints — darker than the dark canvas, deeper depth = more contrast away
       from canvas. Calibrate by eye against Rule A. Starting values; tune as a series. */
    --re-group-1: #15171c;
    --re-group-2: #13141a;
    --re-group-3: #111218;
    --re-group-4: #0f1016;

    /* Deferred palettes (dependency, info) — dark counterparts so task 4+ doesn't
       inherit a light-only token and end up rendering as a white island. */
    --re-dep-bg:     rgba(123, 112, 239, 0.16);
    --re-dep-bd:     rgba(123, 112, 239, 0.40);
    --re-dep-fg:     #b1a9f5;
    --re-dep-strong: #d6cefb;

    --re-info-bg: rgba(242, 216, 139, 0.14);
    --re-info-bd: rgba(242, 216, 139, 0.38);
    --re-info-fg: #f2c24c;

    /* Chip surface — slightly elevated above the canvas so the And/Or toggle, joiner
       pills, operator pills, value pills, and multi-chip-input read as containers
       rather than as floating white shapes. */
    --re-chip-bg: #1f2128;

    /* === Task 5 (editor popovers + clickable chrome) — dark mode ===

       Popover surface lifts above --re-surface-2 so the popover reads as elevated against
       the editor pane rather than blending into it. Shadow uses higher alpha black —
       on a dark canvas a soft drop shadow is the only thing that gives the popover
       layering past the background. */
    --re-popover-bg:        #23252e;
    --re-popover-border:    var(--re-bd-strong);
    --re-popover-shadow:    0 8px 28px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.30);
    --re-popover-search-bg: rgba(255, 255, 255, 0.04);

    --re-picker-row-bg-hover:   var(--re-row-hover);
    --re-picker-row-fg:         var(--re-fg);
    --re-picker-row-active-bg:  var(--re-accent-soft);
    --re-picker-row-active-fg:  var(--re-accent-strong);
    --re-picker-section-fg:     var(--re-muted);

    --re-placeholder-fg:     var(--re-muted);
    --re-placeholder-border: var(--re-bd-strong);

    --re-dirty-dot-bg:   var(--re-accent);
    --re-saved-pill-bg:  var(--re-accent-soft);
    --re-saved-pill-fg:  var(--re-accent-strong);

    /* === Task 7b (arithmetic editing affordances) — dark counterpart === */
    --re-arith-operand-hover-bg:  rgba(255, 255, 255, 0.05);
    --re-arith-operator-hover-bg: rgba(255, 255, 255, 0.05);
    --re-arith-term-remove-fg:    var(--re-danger);
    --re-arith-term-add-fg:       var(--re-accent-strong);

    /* Per-operand fx button (Task 7d) — dark counterpart. */
    --re-arith-operand-fx-fg:     var(--re-muted);
    --re-arith-operand-fx-border: var(--re-token-border);

    /* Wrap / ungroup affordances — dark counterpart. */
    --re-arith-wrap-fg:           var(--re-muted);
    --re-arith-wrap-hover-bg:     rgba(255, 255, 255, 0.05);
    --re-arith-ungroup-fg:        var(--re-muted);
    --re-arith-ungroup-hover-bg:  rgba(255, 255, 255, 0.05);

    /* Editor-input chrome — dark counterpart. Background lifts above --re-surface so a
       focused input reads as a distinct surface against the dark canvas. */
    --re-editor-input-bg:         rgba(255, 255, 255, 0.06);
    --re-editor-input-border:     var(--re-bd-strong);
    --re-editor-input-focus-ring: rgba(45, 212, 173, 0.32);

    /* Jump-to-AstPath flash — brighter amber on the dark canvas. */
    --re-flash-color: rgba(242, 194, 76, 0.55);

    /* === Task 6 (editor shell) — dark counterparts ===
       Empty state surface sits one step elevated from the dark page canvas so the card
       still reads as "container" rather than as flush canvas. Inherits the same --re-fg
       / --re-muted family that drives the rest of the dark editor. */
    --re-empty-bg:        #20212a;
    --re-empty-fg:        var(--re-fg);
    --re-empty-muted-fg:  var(--re-muted);

    /* EditorHeader — dark counterpart. Surface stays transparent (the page canvas
       provides the dark backdrop); breadcrumb / last-edited / description text use
       the dark --re-muted family so they read as muted-on-dark rather than muted-on-light. */
    --re-header-bg:              transparent;
    --re-header-breadcrumb-fg:   var(--re-muted-2);
    --re-header-last-edited-fg:  var(--re-muted);
    --re-header-name-fg:         var(--re-fg);
    --re-header-description-fg:  var(--re-muted);
    --re-header-input-bg:        var(--re-editor-input-bg);
    --re-header-input-border:    var(--re-editor-input-border);
    --re-header-input-focus-ring:var(--re-editor-input-focus-ring);
    --re-edit-hint-fg:           var(--re-muted-2);

    /* TargetTypePill — dark counterpart. Slightly elevated chip surface against the
       dark canvas, hover lifts it via --re-row-hover. */
    --re-target-pill-bg:         var(--re-chip-bg);
    --re-target-pill-fg:         var(--re-fg);
    --re-target-pill-border:     var(--re-bd);
    --re-target-pill-hover-bg:   var(--re-row-hover);

    /* "Used by …" line — dark counterpart. Slug chip uses the elevated chip surface
       above the dark canvas so the slugs read as containers rather than as flush text. */
    --re-used-by-fg:         var(--re-muted);
    --re-used-by-slug-bg:    var(--re-chip-bg);
    --re-used-by-slug-fg:    var(--re-fg);
    --re-used-by-slug-border:var(--re-bd);
}

/* Two component-level overrides where the original hardcoded color can't cleanly be a
   token (one is depth-chip translucent-white, the other is a state-label pill that uses
   --re-fg as background — works in light mode because --re-fg is dark, breaks in dark mode
   because --re-fg is light gray). */
[data-bs-theme="dark"] .cr-rules-editor .cr-group-header__depth {
    background: rgba(255, 255, 255, 0.05);
}

[data-bs-theme="dark"] .cr-rules-editor .cr-state-label__num {
    background: var(--re-bd-strong);
    color: var(--re-fg);
}

/* Warning glyph color now flows through the --re-warning token; this override block is
   intentionally empty after task 4 (kept as anchor for any future glyph-only dark tweak). */

.cr-rules-editor .group-tint-1 { background: var(--re-group-1); }
.cr-rules-editor .group-tint-2 { background: var(--re-group-2); }
.cr-rules-editor .group-tint-3 { background: var(--re-group-3); }
.cr-rules-editor .group-tint-4 { background: var(--re-group-4); }

/* Page-level layout. Aligned with .cr-orgs-page from orgs-admin.css — single content column
   below the page header, with breathing room on the sides. */
.cr-rules-editor-page {
    padding: 1rem 1.5rem;
}

.cr-rules-editor-page .cr-rules-editor-pane {
    max-width: 980px;
}

/* READS AS banner — top of the tree. */
.cr-reads-as-banner {
    display: flex;
    align-items: flex-start;
    gap: 0.625rem;
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.5rem;
    border: 1px solid var(--re-bd);
    background: var(--re-surface-2);
    border-radius: 6px;
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--re-fg);
}

.cr-reads-as-banner__caps {
    flex: none;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--re-muted-2);
    padding-top: 1px;
}

/* Summary text — flex grows so the right-edge toggle sits flush. min-width: 0 lets the
   element shrink past content size (otherwise ellipsis never engages on a flex child). */
.cr-reads-as-banner__summary {
    flex: 1;
    min-width: 0;
}

.cr-reads-as-banner__summary--clipped {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cr-reads-as-banner__summary--full {
    /* Default wrap; long unbroken tokens (rare in English summaries but possible
       in compact field labels) break to the next line rather than overflowing. */
    word-break: break-word;
}

/* show full / show less toggle — small caps text, muted color, white background, thin
   border. Matches the artifact's RootSummary right-edge button (lowercase wording). */
.cr-reads-as-banner__toggle {
    flex: none;
    align-self: flex-start;
    padding: 1px 6px;
    border: 1px solid var(--re-bd);
    border-radius: 4px;
    background: var(--re-chip-bg);
    color: var(--re-muted);
    font-size: 10.5px;
    line-height: 1.4;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
}

.cr-reads-as-banner__toggle:hover {
    color: var(--re-fg);
    border-color: var(--re-bd-strong);
}

.cr-reads-as-banner__toggle:focus-visible {
    outline: 2px solid var(--re-accent-strong);
    outline-offset: 1px;
}

/* LogicalGroupView — outer card with depth tint + accent left guide. The depth-tint
   background is natively clipped by border-radius across all evergreen browsers, and the
   accent-guide strip is bounded by its own absolute positioning (top: 12px, bottom: 12px)
   so it never reaches the rounded corners. We deliberately do NOT set overflow: hidden
   here — that clips popovers (FieldPickerMenu, OperatorPickerMenu, OperandModeMenu,
   ChoiceSinglePill / MultiChipInput option lists) anchored to descendant chips when the
   popover would extend past the group's bottom edge. The popover's z-index handles
   sibling-section stacking; group-level overflow handles the rest. */
.cr-group {
    position: relative;
    border: 1px solid var(--re-bd);
    border-radius: 8px;
}

.cr-group__guide {
    position: absolute;
    left: 0;
    top: 12px;
    bottom: 12px;
    width: 2px;
    border-radius: 999px;
    opacity: 0.6;
}

.cr-group__guide--and { background: var(--re-accent-strong); }
.cr-group__guide--re-or  { background: var(--re-or); }

.cr-group__body {
    padding: 0 0.5rem 0.5rem 0.5rem;
}

/* LogicalGroupHeader — operator pill + explainer + L-depth chip on the left, collapse
   chevron (and later NOT-button + more-menu) on the right. Two inner wrappers so
   space-between pushes the right cluster flush to the edge regardless of left content. */
.cr-group-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
}

.cr-group-header__left {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.cr-group-header__right {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex: none;
    color: var(--re-muted);
}

.cr-group-header__toggle {
    display: inline-flex;
    align-items: stretch;
    box-sizing: border-box;
    height: 24px;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    border-radius: 6px;
    padding: 0;
    /* overflow: hidden so any sub-pixel oversize on a child segment can't escape the
       container's rounded corners. */
    overflow: hidden;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    gap: 0;
}

/* Buttons fill the toggle container's content area exactly. Direct-child selector for
   higher specificity, plus an aggressive reset on every UA / Bootstrap surface that can
   leak vertical space: appearance, min-height, max-height, line-height, font shorthand
   pieces, margins, borders. The green pill should render edge-to-edge inside the 2px
   container padding, with the round corners coming from border-radius: 4px. */
.cr-group-header__toggle > button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    box-sizing: border-box;
    height: 100%;
    min-height: 0;
    max-height: none;
    margin: 0;
    padding: 0 0.5rem;
    border: 0;
    border-radius: 4px;
    background: transparent;
    font-family: inherit;
    font-size: inherit;
    font-weight: inherit;
    line-height: 1;
    letter-spacing: inherit;
    text-transform: inherit;
    color: var(--re-muted);
    cursor: default;
    appearance: none;
    -webkit-appearance: none;
}

.cr-group-header__toggle button.active.cr-and {
    background: var(--re-accent-soft);
    color: var(--re-accent-strong);
}

.cr-group-header__toggle button.active.cr-or {
    background: var(--re-or-soft);
    color: var(--re-or);
}

.cr-group-header__explainer {
    font-size: 11.5px;
    color: var(--re-muted);
}

.cr-group-header__depth {
    border: 1px solid var(--re-bd);
    background: rgba(255, 255, 255, 0.7);
    border-radius: 999px;
    padding: 1px 6px;
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10px;
    color: var(--re-muted-2);
}

/* Collapse chevron in the expanded header. Plain <button> with inline SVG so the editor
   doesn't introduce a DevExpress component primitive for a control this small. Hover lifts
   to the same row-hover background other interactive surfaces use. */
.cr-group-header__chevron {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 22px;
    width: 22px;
    padding: 0;
    border: 0;
    border-radius: 4px;
    background: transparent;
    color: inherit;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
}

.cr-group-header__chevron:hover {
    background: var(--re-row-hover);
    color: var(--re-fg);
}

.cr-group-header__chevron:focus-visible {
    outline: 2px solid var(--re-accent-strong);
    outline-offset: 1px;
}

/* CollapsedGroupSummary — single inner row that replaces header+body+footer when
   Ctx.IsCollapsed(Path). Lives inside the existing cr-group outer so the depth tint and
   accent guide strip stay visible — the group reads as *folded*, not removed. Layout
   mirrors the artifact's CollapsedGroup but without the redundant outer border. */
.cr-collapsed-group {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    /* Leave room for the cr-group__guide strip at left:0 (2px wide) so the chevron
       doesn't overlap it visually. */
    padding-left: 0.875rem;
}

.cr-collapsed-group__chevron {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    height: 22px;
    width: 22px;
    padding: 0;
    border: 1px solid var(--re-bd);
    border-radius: 4px;
    background: var(--re-chip-bg);
    color: var(--re-muted);
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
}

.cr-collapsed-group__chevron:hover {
    color: var(--re-fg);
    border-color: var(--re-bd-strong);
}

.cr-collapsed-group__chevron:focus-visible {
    outline: 2px solid var(--re-accent-strong);
    outline-offset: 1px;
}

/* AND/OR pill — small caps register marker. Reuses the And/Or toggle's active palette
   so the collapsed and expanded states read with the same color identity. */
.cr-collapsed-group__op-pill {
    flex: none;
    border-radius: 4px;
    padding: 1px 6px;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.cr-collapsed-group__op-pill--and {
    background: var(--re-accent-soft);
    color: var(--re-accent-strong);
}

.cr-collapsed-group__op-pill--re-or {
    background: var(--re-or-soft);
    color: var(--re-or);
}

.cr-collapsed-group__depth {
    flex: none;
    border: 1px solid var(--re-bd);
    background: rgba(255, 255, 255, 0.7);
    border-radius: 999px;
    padding: 1px 6px;
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10px;
    color: var(--re-muted-2);
}

/* Truncated summary — flex grows, min-width 0 lets it shrink past content size,
   text-overflow ellipsis clips the overflow. The "expand the group to see it in full"
   pattern handles long summaries; title="..." gives a hover tooltip as a bonus, not
   the contract. */
.cr-collapsed-group__summary {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12px;
    color: var(--re-muted);
}

.cr-collapsed-group__count {
    flex: none;
    background: var(--re-chip-bg);
    border: 1px solid var(--re-bd);
    border-radius: 4px;
    padding: 1px 6px;
    font-size: 10px;
    color: var(--re-muted-2);
}

/* Dark-mode counterparts. Tokens that have light-only hardcoded color (depth chip
   white-alpha bg) get an explicit override so the chip doesn't read as a white island
   on the dark canvas. The chevron + summary + count + op-pill all already cascade
   through tokens that have dark counterparts above. */
[data-bs-theme="dark"] .cr-rules-editor .cr-collapsed-group__depth {
    background: rgba(255, 255, 255, 0.05);
}

/* Between-row joiner — small uppercase pill on a thin rule between siblings. */
.cr-row-joiner {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0 0.75rem 0 3rem;
}

.cr-row-joiner__rule {
    height: 1px;
    background: var(--re-bd);
}

.cr-row-joiner__rule--short { flex: none; width: 12px; }
.cr-row-joiner__rule--rest { flex: 1; }

/* Between-group joiner needs explicit breathing room because cards (cr-group,
   cr-not-card) have their border at their edge with no external padding to
   contribute the implicit daylight that Comparison rows do via their internal
   vertical padding. Match the row-pad contribution (~6px) on whichever side of
   the joiner is adjacent to a card. The between-row case is unaffected — those
   joiners flow between two rows whose own padding already provides the gap. */
.cr-rules-editor .cr-group + .cr-row-joiner,
.cr-rules-editor .cr-not-card + .cr-row-joiner {
    margin-top: 0.375rem;
}

.cr-rules-editor .cr-row-joiner + .cr-group,
.cr-rules-editor .cr-row-joiner + .cr-not-card {
    margin-top: 0.375rem;
}

.cr-row-joiner__pill {
    background: var(--re-chip-bg);
    border: 1px solid var(--re-bd);
    border-radius: 4px;
    padding: 1px 6px;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted);
}

/* LogicalGroupFooter — child-count line. */
.cr-group-footer {
    display: flex;
    align-items: center;
    border-top: 1px solid var(--re-bd);
    padding: 0.5rem 0.75rem;
    color: var(--re-muted);
    font-size: 11px;
}

.cr-group-footer__count {
    margin-left: auto;
}

/* ComparisonRowView — single comparison row. Column layout so the row's __operands
   cluster and the optional __diag inline message stack vertically rather than wrapping
   side by side. */
.cr-comparison-row {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.25rem;
    padding: 0.375rem 0.5rem 0.375rem 0.25rem;
    border-radius: 6px;
}

.cr-comparison-row__operands {
    flex: 1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
}

.cr-fx-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 28px;
    width: 28px;
    margin-left: 2px;
    border: 1px dashed var(--re-bd);
    background: transparent;
    border-radius: 6px;
    color: var(--re-muted);
    cursor: default;
}

.cr-fx-button > span {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 11px;
    font-style: italic;
}

/* FieldChip — section caps + kind icon + title. Read-only in task 1, no caret. */
.cr-field-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    height: 28px;
    padding: 0 0.5rem;
    border: 1px solid var(--re-field-bd);
    background: var(--re-field-bg);
    border-radius: 6px;
    color: var(--re-field-fg);
    font-size: 13px;
}

.cr-field-chip__icon {
    opacity: 0.7;
}

.cr-field-chip__section {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    opacity: 0.6;
}

.cr-field-chip__title {
    font-weight: 600;
}

/* OperatorPill — italicized operator phrase. */
.cr-operator-pill {
    display: inline-flex;
    align-items: center;
    height: 28px;
    padding: 0 0.5rem;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    border-radius: 6px;
    color: var(--re-muted);
    font-size: 13px;
    font-style: italic;
}

/* ChoiceSinglePill / BoolPill — value display. */
.cr-value-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    height: 28px;
    padding: 0 0.5rem;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    border-radius: 6px;
    color: var(--re-fg);
    font-size: 13px;
}

.cr-value-pill__dot {
    height: 6px;
    width: 6px;
    border-radius: 999px;
    background: var(--re-accent);
}

.cr-value-pill__dot--re-muted {
    background: var(--re-muted);
}

/* ---- Task 2 additions ---------------------------------------------------- */

/* State preview separator — caps strip between stacked rules on the preview page.
   Pass-3 chrome around the per-rule body. The actual editor surface (header, footer,
   etc.) replaces this when the editor shell lands. */
.cr-rule-preview {
    margin-bottom: 2.5rem;
}

.cr-rule-preview:last-child {
    margin-bottom: 0;
}

.cr-state-label {
    display: flex;
    align-items: baseline;
    gap: 0.625rem;
    margin-bottom: 0.5rem;
    padding-bottom: 0.375rem;
    border-bottom: 1px dashed var(--re-bd);
}

.cr-state-label__num {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #fff;
    background: var(--re-fg);
    padding: 1px 6px;
    border-radius: 3px;
}

.cr-state-label__name {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--re-fg);
    letter-spacing: -0.01em;
}

/* NOT wrapper — soft pink card with a NOT tab on the top edge. The register is
   "negation," not "error" — pink, not red. The tab uses --re-not-fg as the fill so
   it reads as a deeper register marker against the soft --re-not-bg card. */
.cr-not-card {
    position: relative;
    border: 1px solid var(--re-not-bd);
    background: var(--re-not-bg);
    border-radius: 8px;
    padding: 0.75rem 0.5rem 0.5rem;
    margin-top: 0.625rem;
}

.cr-not-tab {
    position: absolute;
    top: -0.5rem;
    left: 0.75rem;
    display: inline-flex;
    align-items: center;
    height: 16px;
    padding: 0 6px;
    background: var(--re-not-fg);
    color: #fff;
    border-radius: 3px;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.cr-not-card__body {
    /* Inner padding lets the tab visually overhang the card edge without the
       child component (a Comparison row or LogicalGroupView) jamming against the
       tab itself. */
    padding-top: 0.125rem;
}

/* MissingFieldChip — red-ring chip rendered when IFieldResolver returns null.
   Three-channel severity per component-separation §4.2: square Error glyph
   (shape) + MISSING caps prefix (word) + danger color (hue). The path text
   is preserved with strikethrough so admins can see what the rule was pointing
   at before the field disappeared from the form. */
.cr-missing-field-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    height: 28px;
    padding: 0 0.5rem 0 0.375rem;
    border: 2px solid var(--re-danger);
    background: var(--re-danger-soft);
    border-radius: 6px;
    color: var(--re-fg);
    font-size: 13px;
}

.cr-missing-field-chip__caps {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-danger);
}

.cr-missing-field-chip__path {
    text-decoration: line-through;
    text-decoration-color: var(--re-danger);
    text-decoration-thickness: 2px;
    text-underline-offset: 1px;
}

/* SeverityGlyph — square (Error) and triangle (Warning) shapes. Task 2 only
   inhabits Error inside MissingFieldChip; Warning is defined so task 4 doesn't
   need to extend it. The shape carries the severity in print and at small
   sizes; color is not the only signal. */
.cr-severity-glyph {
    display: inline-block;
    flex: none;
    width: 12px;
    height: 12px;
}

.cr-severity-glyph--error {
    color: var(--re-danger);
}

.cr-severity-glyph--re-warning {
    /* Drives by the --re-warning token introduced in task 4 — light mode #C7951C, dark mode
       brighter amber. Both modes are owned by the token block at the top of the file. */
    color: var(--re-warning);
}

/* TextLiteral — plain text pill for number / decimal / fallback string values.
   Reuses the shared .cr-value-pill structural styles; adds the currency-prefix
   slot and the placeholder treatment for empty values. */
.cr-text-literal__currency {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    color: var(--re-muted);
}

.cr-text-literal__placeholder {
    color: var(--re-placeholder);
}

/* OffsetEditor — composite "Today − N unit(s)" chip in the system-pill palette
   (warm-yellow), distinguishing it from regular value chips so the system-value
   register reads at a glance. */
.cr-offset-editor {
    border-color: var(--re-system-bd);
    background: var(--re-system-bg);
    color: var(--re-system-fg);
}

.cr-offset-editor__icon {
    font-size: 12px;
    opacity: 0.85;
}

.cr-offset-editor__today {
    font-weight: 600;
}

.cr-offset-editor__sign {
    color: var(--re-system-fg);
}

.cr-offset-editor__magnitude {
    font-family: "JetBrains Mono", ui-monospace, monospace;
}

.cr-offset-editor__unit {
    font-size: 12px;
}

/* MultiChipInput — read-only chip list for Choice + Contains/DoesNotContain over
   a JsonArray. White surface with internal accent-soft chips so the membership
   set reads as multiple values, distinct from a single ChoiceSinglePill. */
.cr-multi-chip-input {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
    min-height: 28px;
    padding: 0.25rem 0.375rem;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    border-radius: 6px;
    max-width: 420px;
}

.cr-multi-chip {
    display: inline-flex;
    align-items: center;
    padding: 1px 0.375rem;
    background: var(--re-accent-soft);
    color: var(--re-accent-strong);
    border-radius: 4px;
    font-size: 12px;
}

/* === Pass 2 — diagnostic surfaces (task 4) ============================== */

/* DiagInline — left-border-accented row that appears under a comparison row when there's
   a diagnostic at the row's left or right path. SHAPE (glyph) + WORD (uppercase prefix) +
   COLOR (tone class) — three channels per component-separation §4.2.

   Asymmetric border-radius (squared left, rounded right) reinforces the directional
   "this came from the row above" reading. Indent is owned by the row container. */
.cr-diag-inline {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.375rem 0.625rem;
    border-left: 3px solid;
    border-radius: 2px 6px 6px 2px;
    font-size: 12px;
    line-height: 1.45;
}

.cr-diag-inline--re-danger {
    border-left-color: var(--re-danger);
    background: var(--re-danger-soft);
    color: var(--re-danger);
}

.cr-diag-inline--re-warning {
    border-left-color: var(--re-warning);
    background: var(--re-warning-soft);
    color: var(--re-warning-strong);
}

.cr-diag-inline__word {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-top: 1px;
    flex: none;
}

.cr-diag-inline__message {
    color: var(--re-fg);
}

/* Comparison row — diagnostic slot below the operands. Indent under the row's content;
   the operator pills don't carry diagnostic chrome, so the inline row sits aligned with
   the field-chip column. */
.cr-comparison-row__diag {
    margin-top: 0.25rem;
    margin-left: 1.75rem;
}

/* EmptyGroupCard — draft chrome (dashed amber, italic placeholder copy, primary-ish
   "+ Add condition" button). Sits inside the cr-group container so the depth tint and
   accent guide stay visible — a draft group, not a destroyed one. */
.cr-empty-group-card {
    border: 2px dashed var(--re-warning-dashed);
    background: var(--re-warning-card);
    border-radius: 8px;
    margin: 0.25rem;
}

.cr-empty-group-card__header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
}

.cr-empty-group-card__op-toggle {
    display: inline-flex;
    align-items: center;
    height: 24px;
    padding: 2px;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    border-radius: 6px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.cr-empty-group-card__op-btn {
    display: inline-flex;
    align-items: center;
    height: 18px;
    padding: 0 0.5rem;
    border: 0;
    background: transparent;
    border-radius: 4px;
    color: var(--re-muted);
    cursor: pointer;
    font: inherit;
}

.cr-empty-group-card__op-btn:hover {
    background: var(--re-row-hover);
}

.cr-empty-group-card__op-btn--active {
    background: var(--re-accent-soft);
    color: var(--re-accent-strong);
}

.cr-empty-group-card__placeholder {
    flex: 1;
    font-size: 11.5px;
    font-style: italic;
    color: var(--re-warning-strong);
}

.cr-empty-group-card__body {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.625rem 0.75rem;
    border-top: 1px dashed var(--re-warning-dashed);
}

.cr-empty-group-card__add {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.25rem 0.5rem;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    border-radius: 6px;
    font-size: 12px;
    color: var(--re-fg);
    cursor: pointer;
}

.cr-empty-group-card__add:hover {
    border-color: var(--re-accent);
    color: var(--re-accent-strong);
}

.cr-empty-group-card__remove {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.25rem 0.5rem;
    border: 1px solid var(--re-bd);
    background: transparent;
    border-radius: 6px;
    font-size: 12px;
    color: var(--re-muted);
    cursor: pointer;
    margin-left: 0.375rem;
}

.cr-empty-group-card__remove:hover {
    border-color: var(--re-danger);
    color: var(--re-danger);
}

.cr-empty-group-card__hint {
    font-size: 11px;
    color: var(--re-muted);
}

/* ArithmeticNodeView — bordered composite that reads as a single operand at the
   comparison level. Always-on header strip (so the node is visually "an arithmetic group"
   even without an error); danger-toned border + strip when a diagnostic is anchored. */
.cr-arith-node {
    display: inline-flex;
    flex-direction: column;
    border: 2px solid var(--re-bd-strong);
    background: var(--re-chip-bg);
    border-radius: 6px;
    overflow: hidden;
    vertical-align: middle;
}

/* When an open popover lives inside the arithmetic node (operand field-picker or
   operator picker), drop the overflow:hidden so the dropdown isn't clipped by the
   node's rounded edges. The strip's bottom-border still reads correctly because
   the strip sits at the top of the node — only the popover would have been clipped
   by the bottom-right rounded corner. RulesEditorPopover only renders its DOM when
   Open=true, so this rule matches exactly when there's something to reveal. */
.cr-arith-node:has(.cr-rules-popover) {
    overflow: visible;
}

.cr-arith-node--error {
    border-color: var(--re-danger);
}

.cr-arith-node__strip {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    padding: 2px 0.5rem;
    border-bottom: 1px solid var(--re-bd);
    background: var(--re-surface-2);
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted);
}

.cr-arith-node__strip-tag {
    color: var(--re-danger);
    font-weight: 600;
}

.cr-arith-node__body {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
    padding: 0.375rem;
}

.cr-arith-node__paren {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 14px;
    color: var(--re-muted);
}

.cr-arith-node__op {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 15px;
    font-weight: 600;
    color: var(--re-fg);
}

.cr-arith-node__lit {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 12px;
}

.cr-arith-node__error-footer {
    display: flex;
    align-items: flex-start;
    gap: 0.375rem;
    padding: 0.375rem 0.5rem;
    border-top: 1px solid var(--re-bd);
    font-size: 11.5px;
    color: var(--re-danger);
}

.cr-arith-node__error-word {
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 10px;
    margin-top: 1px;
    flex: none;
}

.cr-arith-node__error-msg {
    color: var(--re-fg);
    line-height: 1.4;
}

/* === Task 7b — arithmetic editing affordances ===

   Operand wrapper carries the × button at its right edge. The button is in the DOM at
   all times (so keyboard tab order finds it consistently) but visually hidden until the
   operand is hovered or has focus-within. Same hover/focus reveal pattern as the + add
   button below.

   Operator glyph becomes a button styled to match the read-only span — same monospace
   font, same color. Cursor switches to pointer on hover; subtle background tint signals
   it's interactive. Native <button> reset (border/padding/background) keeps the visual
   identical to a non-interactive <span> in the resting state. */

.cr-arith-node__operand {
    display: inline-flex;
    align-items: center;
    gap: 0.125rem;
}

.cr-arith-node__op--editable {
    border: 0;
    background: transparent;
    padding: 0 0.125rem;
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 15px;
    font-weight: 600;
    color: var(--re-fg);
    cursor: pointer;
    border-radius: 4px;
    line-height: 1;
}

.cr-arith-node__op--editable:hover,
.cr-arith-node__op--editable:focus-visible {
    background: var(--re-arith-operator-hover-bg);
}

.cr-arith-node__op--editable:focus-visible {
    outline: 2px solid var(--re-accent-strong);
    outline-offset: 2px;
}

.cr-arith-node__remove {
    border: 0;
    background: transparent;
    padding: 0;
    width: 18px;
    height: 18px;
    line-height: 1;
    border-radius: 4px;
    color: var(--re-arith-term-remove-fg);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    /* Hidden by default; revealed on the operand's hover/focus-within. The button stays
       in the DOM so screen readers can find it via aria-label. */
    opacity: 0;
    transform: scale(0.85);
    transition: opacity 80ms ease, transform 80ms ease;
}

.cr-arith-node__operand:hover .cr-arith-node__remove,
.cr-arith-node__operand:focus-within .cr-arith-node__remove,
.cr-arith-node__remove:focus-visible {
    opacity: 1;
    transform: scale(1);
}

.cr-arith-node__remove:hover,
.cr-arith-node__remove:focus-visible {
    background: var(--re-arith-operand-hover-bg);
}

.cr-arith-node__remove:focus-visible {
    outline: 2px solid var(--re-danger);
    outline-offset: 2px;
}

/* Per-operand fx button (Task 7d). Sits alongside the × per-operand remove button
   inside an arithmetic operand. Hover/focus-within reveal matches the × pattern —
   inline-flex layout so it doesn't displace the operand chip (the wrap-button's
   0-width-slot-with-absolute-positioned-button pattern would visually straddle
   the chip and × here; see lint trail at feedback_zero_width_slot_overlap.md). */
.cr-arith-node__fx {
    border: 1px dashed var(--re-arith-operand-fx-border);
    background: transparent;
    padding: 0 0.35rem;
    height: 18px;
    line-height: 1;
    border-radius: 4px;
    color: var(--re-arith-operand-fx-fg);
    font-family: var(--font-sans);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    cursor: pointer;
    /* Hidden by default; revealed on the operand's hover/focus-within. */
    opacity: 0;
    transform: scale(0.85);
    transition: opacity 80ms ease, transform 80ms ease;
}

.cr-arith-node__operand:hover .cr-arith-node__fx,
.cr-arith-node__operand:focus-within .cr-arith-node__fx,
.cr-arith-node__fx:focus-visible {
    opacity: 1;
    transform: scale(1);
}

.cr-arith-node__fx:hover,
.cr-arith-node__fx:focus-visible {
    background: var(--re-arith-operand-hover-bg);
}

.cr-arith-node__fx:focus-visible {
    outline: 2px solid var(--re-accent-strong);
    outline-offset: 2px;
}

.cr-arith-node__add {
    border: 1px dashed var(--re-arith-term-add-fg);
    background: transparent;
    padding: 0 0.5rem;
    height: 22px;
    line-height: 1;
    border-radius: 4px;
    color: var(--re-arith-term-add-fg);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    /* Visible only on hover/focus-within of the arithmetic node body. */
    opacity: 0;
    transition: opacity 80ms ease;
}

.cr-arith-node:hover .cr-arith-node__add,
.cr-arith-node:focus-within .cr-arith-node__add,
.cr-arith-node__add:focus-visible {
    opacity: 1;
}

.cr-arith-node__add:hover,
.cr-arith-node__add:focus-visible {
    background: var(--re-arith-operand-hover-bg);
}

.cr-arith-node__add:focus-visible {
    outline: 2px solid var(--re-accent-strong);
    outline-offset: 2px;
}

/* Wrap (…) button — sits in a slot rendered between an operator and its rightward
   operand. The slot reserves its width permanently so revealing the button doesn't
   displace surrounding tokens (the brief's "no layout jumps" requirement). The
   previous attempt absolute-positioned the button on a 0-width slot, which made the
   button visually straddle the operator glyph and obscure it — fixed by giving the
   slot natural width and inline-flexing the button inside it. Hover/focus reveal is
   purely an opacity transition. */
.cr-arith-node__wrap-slot {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.cr-arith-node__wrap {
    border: 0;
    background: transparent;
    padding: 0 0.25rem;
    height: 18px;
    line-height: 1;
    border-radius: 4px;
    color: var(--re-arith-wrap-fg);
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 11px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 80ms ease;
}

.cr-arith-node:hover > .cr-arith-node__body .cr-arith-node__wrap,
.cr-arith-node:focus-within > .cr-arith-node__body .cr-arith-node__wrap,
.cr-arith-node__wrap:focus-visible {
    opacity: 1;
}

.cr-arith-node__wrap:hover,
.cr-arith-node__wrap:focus-visible {
    background: var(--re-arith-wrap-hover-bg);
    color: var(--re-fg);
}

.cr-arith-node__wrap:focus-visible {
    outline: 2px solid var(--re-accent-strong);
    outline-offset: 1px;
}

/* Group wrapper + × ungroup button. The wrapper is a relative-positioned container
   around the nested ArithmeticNodeView so the ungroup button can sit at the group's
   top-right corner absolutely-positioned. Reveal pattern mirrors the operand × button
   — fades in on hover of the group wrapper or focus-within. */
.cr-arith-node__group {
    position: relative;
    display: inline-block;
}

.cr-arith-node__ungroup {
    position: absolute;
    top: -8px;
    right: -8px;
    z-index: 5;
    border: 1px solid var(--re-bd);
    background: var(--re-popover-bg);
    padding: 0;
    width: 18px;
    height: 18px;
    line-height: 1;
    border-radius: 50%;
    color: var(--re-arith-ungroup-fg);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    opacity: 0;
    transform: scale(0.85);
    transition: opacity 80ms ease, transform 80ms ease;
}

.cr-arith-node__group:hover .cr-arith-node__ungroup,
.cr-arith-node__group:focus-within .cr-arith-node__ungroup,
.cr-arith-node__ungroup:focus-visible {
    opacity: 1;
    transform: scale(1);
}

.cr-arith-node__ungroup:hover,
.cr-arith-node__ungroup:focus-visible {
    background: var(--re-arith-ungroup-hover-bg);
    color: var(--re-fg);
}

.cr-arith-node__ungroup:focus-visible {
    outline: 2px solid var(--re-accent-strong);
    outline-offset: 1px;
}

/* DependencyChip — asymmetric pill for rule-to-rule references. Squared left, rounded
   right (border-radius 2px / 10px) preserves the "rule reference" lineage. CycleEdge
   variant swaps to danger palette + "cycle:" prefix; same shape on purpose. */
.cr-dep-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    height: 28px;
    padding: 0 0.5rem 0 0.375rem;
    border: 2px double;
    border-radius: 2px 10px 10px 2px;
    background: var(--re-chip-bg);
    font-size: 13px;
    cursor: pointer;
}

.cr-dep-chip--rule {
    border-color: var(--re-dep-bd);
    background: var(--re-dep-bg);
    color: var(--re-dep-fg);
}

.cr-dep-chip--cycle {
    border-color: var(--re-danger);
    background: var(--re-danger-soft);
    color: var(--re-danger);
}

.cr-dep-chip__icon {
    font-size: 11px;
    opacity: 0.7;
}

.cr-dep-chip__prefix {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    opacity: 0.85;
}

.cr-dep-chip__name {
    font-weight: 500;
    color: var(--re-fg);
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cr-dep-chip--cycle .cr-dep-chip__name {
    color: var(--re-fg);
}

.cr-dep-chip__caret {
    opacity: 0.5;
    font-size: 12px;
}

/* RulePreviewPopover — hover/focus preview for a DependencyChip (Task 5).
   Portal-rendered, so the consumer's root <div> carries both `cr-rule-ref-preview`
   AND `cr-rules-editor`. The `cr-rules-editor` class re-establishes the scoped
   token chain (--re-fg / --re-muted / --re-bd / --re-accent / --re-accent-strong / --re-surface-2 /
   --re-danger / --re-danger-soft) at the portal layer — see PopoverShell + the
   `[data-bs-theme="dark"] .cr-popover-shell` block below for the same pattern.

   Light + dark modes share the same selectors because every value reads through a
   scoped token whose value is already mode-aware (the `.cr-rules-editor` and
   `[data-bs-theme="dark"] .cr-rules-editor` blocks at the top of this file own
   the per-mode token values). No `[data-bs-theme="dark"]` overrides needed below. */
.cr-rule-ref-preview {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-width: 240px;
    max-width: 360px;
    padding: 0.625rem 0.75rem;
    color: var(--re-fg);
    font-size: 13px;
    line-height: 1.4;
}

.cr-rule-ref-preview__loading {
    color: var(--re-muted);
    font-style: italic;
    padding: 0.25rem 0;
}

.cr-rule-ref-preview__missing {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--re-danger);
    background: var(--re-danger-soft);
    padding: 0.375rem 0.5rem;
    border-radius: 4px;
}

.cr-rule-ref-preview__header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    justify-content: space-between;
}

.cr-rule-ref-preview__name {
    font-weight: 600;
    color: var(--re-fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
}

.cr-rule-ref-preview__target-pill {
    flex-shrink: 0;
    background: var(--re-surface-2);
    color: var(--re-muted);
    border: 1px solid var(--re-bd);
    border-radius: 999px;
    padding: 0.0625rem 0.5rem;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.cr-rule-ref-preview__cycle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--re-danger);
    background: var(--re-danger-soft);
    padding: 0.375rem 0.5rem;
    border-radius: 4px;
    font-size: 12px;
}

.cr-rule-ref-preview__summary {
    color: var(--re-fg);
    font-size: 12.5px;
    word-break: break-word;
}

.cr-rule-ref-preview__footer {
    display: flex;
    justify-content: flex-end;
    padding-top: 0.25rem;
    border-top: 1px solid var(--re-bd);
}

.cr-rule-ref-preview__jump {
    appearance: none;
    border: none;
    background: var(--re-accent);
    color: #ffffff;
    font-size: 12px;
    font-weight: 600;
    padding: 0.3125rem 0.625rem;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 80ms ease;
}

.cr-rule-ref-preview__jump:hover,
.cr-rule-ref-preview__jump:focus-visible {
    background: var(--re-accent-strong);
    outline: none;
}

/* === Pass 3 — rule-scope + page-scope surfaces (task 4) ================= */

/* CycleBanner — rule-scope error banner above the READS AS. Names both participants in
   the cycle with "↔" between them. Sits inside the cr-rule-preview section, between the
   state label and the READS AS banner. */
.cr-cycle-banner {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.625rem 0.75rem;
    border-left: 3px solid var(--re-danger);
    background: var(--re-danger-soft);
    border-radius: 2px 6px 6px 2px;
    margin: 0.5rem 0;
}

.cr-cycle-banner__body {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.cr-cycle-banner__caps {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.cr-cycle-banner__caps-strong {
    color: var(--re-danger);
    font-weight: 600;
}

.cr-cycle-banner__caps-detail {
    text-transform: none;
    letter-spacing: normal;
    color: var(--re-muted);
    font-family: inherit;
    font-size: 11px;
}

.cr-cycle-banner__pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
}

.cr-cycle-banner__pill {
    border: 1px solid var(--re-danger);
    background: var(--re-chip-bg);
    color: var(--re-fg);
    padding: 2px 0.5rem;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
}

.cr-cycle-banner__sep {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    color: var(--re-danger);
}

.cr-cycle-banner__message {
    font-size: 12px;
    line-height: 1.4;
    color: var(--re-fg);
}

.cr-cycle-banner__action {
    font-size: 11.5px;
    color: var(--re-muted);
}

.cr-cycle-banner__open {
    border: 0;
    background: transparent;
    color: var(--re-accent-strong);
    font-size: inherit;
    padding: 0;
    cursor: pointer;
}

.cr-cycle-banner__open:hover {
    text-decoration: underline;
}

/* SaveBlockedPanel — sits below a rule with Errors. Collapsed bar + expanded details list. */
.cr-save-blocked {
    border-top: 1px solid var(--re-bd);
    background: var(--re-surface);
    border-radius: 0 0 8px 8px;
    margin-top: 0.5rem;
}

.cr-save-blocked__details {
    border-bottom: 1px solid var(--re-bd);
    padding: 0.625rem 1.25rem;
    background: var(--re-danger-soft);
}

.cr-save-blocked__details-caps {
    margin-bottom: 0.375rem;
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted);
}

.cr-save-blocked__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.cr-save-blocked__row {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    width: 100%;
    padding: 0.5rem 0.625rem;
    border: 1px solid transparent;
    background: var(--re-chip-bg);
    border-radius: 6px;
    text-align: left;
    cursor: pointer;
}

.cr-save-blocked__row:hover {
    border-color: var(--re-bd);
}

.cr-save-blocked__row-msg {
    flex: 1;
    font-size: 12px;
    color: var(--re-fg);
    line-height: 1.4;
}

.cr-save-blocked__row-path {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10.5px;
    color: var(--re-muted-2);
    margin-left: 0.5rem;
}

.cr-save-blocked__row-jump {
    font-size: 11px;
    color: var(--re-accent-strong);
    flex: none;
    margin-top: 1px;
}

.cr-save-blocked__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.625rem 1.25rem;
}

.cr-save-blocked__bar-left {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cr-save-blocked__bar-strong {
    font-size: 12px;
    font-weight: 600;
    color: var(--re-danger);
}

.cr-save-blocked__bar-sep {
    color: var(--re-muted);
    font-size: 12px;
}

.cr-save-blocked__bar-msg {
    font-size: 12px;
    color: var(--re-fg);
}

.cr-save-blocked__why {
    margin-left: 0.25rem;
    padding: 2px 0.5rem;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    color: var(--re-fg);
    border-radius: 6px;
    font-size: 11px;
    cursor: pointer;
}

.cr-save-blocked__why:hover {
    border-color: var(--re-bd-strong);
}

.cr-save-blocked__bar-right {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cr-save-blocked__discard {
    padding: 0.375rem 0.75rem;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    color: var(--re-fg);
    border-radius: 6px;
    font-size: 12px;
    cursor: pointer;
}

.cr-save-blocked__discard:hover:not(:disabled) {
    background: var(--re-row-hover);
}

.cr-save-blocked__discard:disabled {
    cursor: not-allowed;
    opacity: 0.5;
    color: var(--re-muted);
}

.cr-save-blocked__save {
    padding: 0.375rem 0.75rem;
    border: 0;
    background: var(--re-bd);
    color: white;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 600;
    opacity: 0.7;
    cursor: not-allowed;
}

/* SaveWithWarningsFooter — sits below a rule with Warnings (no Errors). Save stays
   affirmative; copy degrades to "Save anyway". */
.cr-save-warnings {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.625rem 1.25rem;
    border-top: 1px solid var(--re-bd);
    background: var(--re-surface);
    border-radius: 0 0 8px 8px;
    margin-top: 0.5rem;
}

.cr-save-warnings__left {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cr-save-warnings__strong {
    font-size: 12px;
    font-weight: 600;
    color: var(--re-warning-strong);
}

.cr-save-warnings__sep {
    color: var(--re-muted);
    font-size: 12px;
}

.cr-save-warnings__msg {
    font-size: 12px;
    color: var(--re-fg);
}

.cr-save-warnings__right {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cr-save-warnings__discard {
    padding: 0.375rem 0.75rem;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    color: var(--re-fg);
    border-radius: 6px;
    font-size: 12px;
    cursor: pointer;
}

.cr-save-warnings__discard:hover:not(:disabled) {
    background: var(--re-row-hover);
}

.cr-save-warnings__discard:disabled,
.cr-save-warnings__save:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

.cr-save-warnings__save {
    padding: 0.375rem 0.75rem;
    border: 0;
    background: var(--re-accent);
    color: white;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
}

/* ============================================================================
   Task 9f — portal-rendered popover primitive (replaces the Task 5 inline anchor).
   Popovers register with PopoverRegistry on open and PopoverLayer (mounted once
   in MainLayout) renders the shell at top-level z-order. Position is computed
   from the trigger's viewport rect in C# and set inline as `position: fixed;
   top/left`. No flip-on-overflow logic — top-level z-order already solves the
   clipping bug that motivated the refactor.
   ============================================================================ */

/* Portal host: fixed viewport-sized container, click-through. Children
   (.cr-popover-shell) flip pointer-events back on to receive clicks. The layer
   itself stays transparent so the page below remains interactive. */
.cr-popover-layer {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 1000;
}

/* The popover shell — visual chrome (bg/border/shadow), takes the inline
   position: fixed; top/left/min-width assigned by PopoverShell.razor from the
   PopoverPosition the registering RulesEditorPopover computed. Style otherwise
   matches the pre-refactor .cr-rules-popover (now retired) so visual
   continuity holds across the rename.

   Token re-declaration: PopoverLayer mounts the shell at app root, OUTSIDE the
   .cr-rules-editor scope that hosts the canonical --re-popover-* and
   --re-picker-* token definitions. Without redeclaring them here the
   shell's background/border/shadow silently fall back to transparent and the
   picker row colors fail to resolve. Values mirror the .cr-rules-editor block
   above; the underlying globals (--re-bd, --re-surface-2, --re-row-hover, etc.) read
   through threshold-tokens.css and are theme-aware at app root. */
.cr-popover-shell {
    /* Base tokens — re-declared because the canonical definitions live inside
       .cr-rules-editor (lines 17-32) and don't cascade to a portal-mounted shell
       at app root. Any chained var() in the descendant rules below silently
       resolves to nothing without these. Values mirror line 17-32 exactly. */
    --re-fg:            #3B3F4C;
    --re-muted:         #7B8AA1;
    --re-placeholder:   #B7C0CC;
    --re-bd:            #E4E8EE;
    --re-bd-strong:     #C9D1DC;
    --re-surface-2:     #F4F6F9;
    --re-row-hover:     #EEF2F7;
    --re-accent-soft:   #DDF2EC;
    --re-accent-strong: #0F8E76;
    /* Danger tokens — redeclared so the rule-actions menu's danger item (Task 9d
       "Delete rule") resolves --re-danger and --re-danger-soft inside the portal-mounted
       popover. Without this the danger item silently falls back to --re-fg + transparent
       background — same scope-leak class as the popover-shell base tokens above. */
    --re-danger:        #FB5858;
    --re-danger-soft:   #FCE3E6;

    /* Popover-specific tokens — also re-declared (same reason). These chain into
       the base tokens above, which now resolve at this scope. */
    --re-popover-bg:        #ffffff;
    --re-popover-border:    var(--re-bd);
    --re-popover-shadow:    0 6px 24px rgba(28, 39, 56, 0.12), 0 2px 6px rgba(28, 39, 56, 0.06);
    --re-popover-search-bg: var(--re-surface-2);

    --re-picker-row-bg-hover:   var(--re-row-hover);
    --re-picker-row-fg:         var(--re-fg);
    --re-picker-row-active-bg:  var(--re-accent-soft);
    --re-picker-row-active-fg:  var(--re-accent-strong);
    --re-picker-section-fg:     var(--re-muted);

    pointer-events: auto;

    min-width: 240px;
    max-width: 360px;
    max-height: 320px;

    display: flex;
    flex-direction: column;

    background: var(--re-popover-bg);
    border: 1px solid var(--re-popover-border);
    border-radius: 6px;
    box-shadow: var(--re-popover-shadow);
    overflow: hidden;
}

/* Dark counterpart — same shape as [data-bs-theme="dark"] .cr-rules-editor above
   but applied at app root because the shell lives in PopoverLayer, not in the
   editor pane. The theme attribute sits on <html>, so this selector matches
   regardless of where the shell renders in the DOM. */
[data-bs-theme="dark"] .cr-popover-shell {
    /* Dark-mode base tokens — values mirror [data-bs-theme="dark"] .cr-rules-editor
       at line 237-251. Same re-declaration story as the light block above. */
    --re-fg:            #adb5bf;
    --re-muted:         rgba(173, 181, 191, 0.75);
    --re-placeholder:   rgba(173, 181, 191, 0.4);
    --re-bd:            #2c2d38;
    --re-bd-strong:     #3a3b46;
    --re-surface-2:     #1b1c22;
    --re-row-hover:     rgba(255, 255, 255, 0.04);
    --re-accent-soft:   rgba(26, 179, 148, 0.18);
    --re-accent-strong: #2dd4ad;
    /* Danger tokens — same redeclaration story as the light block above; values
       mirror [data-bs-theme="dark"] .cr-rules-editor's --re-danger / --re-danger-soft so
       the rule-actions menu's danger item reads consistently in dark mode. */
    --re-danger:        #FB5858;
    --re-danger-soft:   rgba(251, 88, 88, 0.16);

    --re-popover-bg:        #23252e;
    --re-popover-border:    var(--re-bd-strong);
    --re-popover-shadow:    0 8px 28px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.30);
    --re-popover-search-bg: rgba(255, 255, 255, 0.04);

    --re-picker-row-bg-hover:   var(--re-row-hover);
    --re-picker-row-fg:         var(--re-fg);
    --re-picker-row-active-bg:  var(--re-accent-soft);
    --re-picker-row-active-fg:  var(--re-accent-strong);
    --re-picker-section-fg:     var(--re-muted);
}

/* Anchor wrapper retained for self-contained consumers (ChoiceSinglePill,
   MultiChipInput, the row's fx + operator wrappers) — its positioning role is
   gone but it's used for inline-flex layout grouping of trigger + (now empty)
   popover slot. Harmless if removed later; keeping for now to minimize diff. */
.cr-rules-editor .cr-popover-anchor {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.cr-popover-shell .cr-rules-popover__search {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.625rem;
    border-bottom: 1px solid var(--re-popover-border);
    background: var(--re-popover-search-bg);
    color: var(--re-muted);
}

.cr-popover-shell .cr-rules-popover__search input {
    flex: 1;
    border: 0;
    outline: 0;
    background: transparent;
    color: var(--re-fg);
    font: inherit;
}

.cr-popover-shell .cr-rules-popover__search input::placeholder {
    color: var(--re-placeholder);
}

.cr-popover-shell .cr-rules-popover__body {
    flex: 1;
    overflow-y: auto;
    padding: 0.25rem 0;
}

.cr-popover-shell .cr-rules-popover__section {
    padding: 0.375rem 0.625rem 0.25rem;
    color: var(--re-picker-section-fg);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
}

.cr-popover-shell .cr-rules-popover__row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.375rem 0.625rem;
    border: 0;
    background: transparent;
    color: var(--re-picker-row-fg);
    text-align: left;
    cursor: pointer;
    font: inherit;
}

.cr-popover-shell .cr-rules-popover__row:hover,
.cr-popover-shell .cr-rules-popover__row:focus-visible {
    background: var(--re-picker-row-bg-hover);
    outline: 0;
}

.cr-popover-shell .cr-rules-popover__row--active {
    background: var(--re-picker-row-active-bg);
    color: var(--re-picker-row-active-fg);
    font-weight: 600;
}

.cr-popover-shell .cr-rules-popover__row-icon {
    color: var(--re-muted);
    width: 14px;
    text-align: center;
}

.cr-popover-shell .cr-rules-popover__row-title {
    flex: 1;
}

.cr-popover-shell .cr-rules-popover__row-path {
    color: var(--re-muted);
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 11px;
}

.cr-popover-shell .cr-rules-popover__empty {
    padding: 0.75rem 0.625rem;
    color: var(--re-muted);
    font-style: italic;
    text-align: center;
}

/* Clickable variants of the existing read-only chip + pill primitives. The shared
   look-and-feel comes from the base classes; these variants only add interactivity
   (cursor + hover register + caret affordance for FieldChip). */

.cr-rules-editor .cr-field-chip--clickable {
    cursor: pointer;
    border: 1px solid var(--re-field-bd);
    background: var(--re-field-bg);
    color: var(--re-field-fg);
    padding: 2px 8px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font: inherit;
    line-height: 1.4;
}

.cr-rules-editor .cr-field-chip--clickable:hover {
    border-color: var(--re-field-bd-h);
}

.cr-rules-editor .cr-field-chip__caret {
    margin-left: 2px;
    color: var(--re-muted);
    font-size: 10px;
}

.cr-rules-editor .cr-field-chip--re-placeholder {
    cursor: pointer;
    border: 1px dashed var(--re-placeholder-border);
    background: transparent;
    color: var(--re-placeholder-fg);
    padding: 2px 10px;
    border-radius: 4px;
    font: inherit;
    font-style: italic;
    line-height: 1.4;
}

.cr-rules-editor .cr-field-chip--re-placeholder:hover {
    border-color: var(--re-field-bd-h);
    color: var(--re-fg);
}

.cr-rules-editor .cr-field-chip--re-placeholder:disabled {
    cursor: default;
}

.cr-rules-editor .cr-operator-pill--clickable {
    cursor: pointer;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    font-style: italic;
    padding: 2px 4px;
    border-radius: 3px;
}

.cr-rules-editor .cr-operator-pill--clickable:hover {
    background: var(--re-row-hover);
}

/* ============================================================================
   Task 5 — dirty dot + saved-pill + clean-footer (Pass 2 commit / discard UX).
   ============================================================================ */

/* Section-label dirty dot. Sits inline after the rule name. Quiet by design: the
   diagnostic system already owns the loud "something's wrong" register. Editing
   isn't an error; the dot is just a marker. */
.cr-rules-editor .cr-state-label__dirty {
    display: inline-block;
    width: 8px;
    height: 8px;
    margin-left: 0.5rem;
    background: var(--re-dirty-dot-bg);
    border-radius: 50%;
    vertical-align: middle;
}

/* Saved pill — small affirmative status badge that auto-fades. The page bumps an
   @key on each save so the animation re-fires even on repeated saves of the same
   rule. The pill positions itself between the rule body and the footer (block
   flow) so it doesn't disturb either layout. */
.cr-rules-editor .cr-rules-saved-pill {
    display: inline-flex;
    align-items: center;
    margin: 0.5rem 0;
    padding: 2px 10px;
    background: var(--re-saved-pill-bg);
    color: var(--re-saved-pill-fg);
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    animation: cr-rules-saved-pill-fade 2s ease-in-out forwards;
}

@keyframes cr-rules-saved-pill-fade {
    0%   { opacity: 0;    transform: translateY(-2px); }
    15%  { opacity: 1;    transform: translateY(0); }
    80%  { opacity: 1;    transform: translateY(0); }
    100% { opacity: 0;    transform: translateY(-2px); }
}

/* CleanFooter — "edited, ready to save" footer. Same structural shape as
   SaveBlockedPanel and SaveWithWarningsFooter for visual consistency; copy
   register is neutral (no danger / warning color) because there's nothing
   wrong, just unsaved. */
.cr-rules-editor .cr-clean-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: 0.5rem 0.75rem;
    background: var(--re-surface-2);
    border: 1px solid var(--re-bd);
    border-radius: 4px;
    color: var(--re-fg);
    margin-top: 0.5rem;
}

.cr-rules-editor .cr-clean-footer__left {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    color: var(--re-muted);
}

.cr-rules-editor .cr-clean-footer__strong {
    color: var(--re-fg);
    font-weight: 600;
}

.cr-rules-editor .cr-clean-footer__sep {
    color: var(--re-muted-2);
}

.cr-rules-editor .cr-clean-footer__right {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cr-rules-editor .cr-clean-footer__discard {
    border: 1px solid var(--re-bd-strong);
    background: transparent;
    color: var(--re-fg);
    padding: 4px 12px;
    border-radius: 4px;
    cursor: pointer;
    font: inherit;
}

.cr-rules-editor .cr-clean-footer__discard:hover {
    background: var(--re-row-hover);
}

.cr-rules-editor .cr-clean-footer__save {
    border: 1px solid var(--re-accent);
    background: var(--re-accent);
    color: #fff;
    padding: 4px 12px;
    border-radius: 4px;
    cursor: pointer;
    font: inherit;
    font-weight: 600;
}

.cr-rules-editor .cr-clean-footer__save:hover {
    background: var(--re-accent-strong);
    border-color: var(--re-accent-strong);
}

/* ============================================================================
   Task 5 — Pass 3 — value editors (TextLiteral / BoolPill / ChoiceSinglePill /
   MultiChipInput / OffsetEditor / DateLiteral / TodayChip), operand-mode menu,
   system-value badges. All chip-level / pill-level affordances; the chrome stays
   consistent with the existing read-only primitives so the read↔edit transition
   is "same shape, gained an interaction" rather than "different component".
   ============================================================================ */

/* Generic clickable variant — used by ChoiceSinglePill (and any future pill that
   wants the same affordance). Matches the read-only pill chrome with a pointer
   cursor + hover register. */
.cr-rules-editor .cr-value-pill--clickable {
    cursor: pointer;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    color: inherit;
    font: inherit;
    padding: 2px 8px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    line-height: 1.4;
}

.cr-rules-editor .cr-value-pill--clickable:hover {
    border-color: var(--re-bd-strong);
}

.cr-rules-editor .cr-value-pill__caret {
    color: var(--re-muted);
    font-size: 10px;
    margin-left: 2px;
}

/* "value…" placeholder — dashed neutral chrome, used when right operand is JsonNull
   (AddCondition state, OperandModeChanged → Literal). Same dashed-register as the
   FieldChip placeholder; distinct from MissingFieldChip's danger palette. */
.cr-rules-editor .cr-value-pill--re-placeholder {
    border: 1px dashed var(--re-placeholder-border);
    background: transparent;
    color: var(--re-placeholder-fg);
    padding: 2px 10px;
    border-radius: 4px;
    font-style: italic;
    line-height: 1.4;
}

/* TextLiteral — clickable + editing variants. Editing replaces the inner span with
   an <input> that inherits the pill's chrome so the layout doesn't shift on entry. */
.cr-rules-editor .cr-text-literal--clickable {
    cursor: pointer;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    color: inherit;
    font: inherit;
    padding: 2px 8px;
    border-radius: 4px;
    line-height: 1.4;
}

.cr-rules-editor .cr-text-literal--clickable:hover {
    border-color: var(--re-bd-strong);
}

.cr-rules-editor .cr-text-literal--editing {
    padding: 2px 4px 2px 6px;
}

/* BoolPill toggle — two-segment Yes/No clicker. Active segment uses --re-accent-soft so
   the toggle stays inside the value-pill register rather than competing with the
   And/Or operator-toggle palette. The bool-toggle is rendered as `<span class="cr-value-pill
   cr-bool-toggle">`; the cr-value-pill base provides padding that would leave a white
   gutter around the segment buttons, so the toggle variant zeroes that out and lets
   each segment fill the chip edge-to-edge. */
.cr-rules-editor .cr-bool-toggle {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    border-radius: 4px;
    overflow: hidden;
    line-height: 1.4;
    padding: 0;
    gap: 0;
}

.cr-rules-editor .cr-bool-toggle__seg {
    align-items: center;
    display: inline-flex;
}

.cr-rules-editor .cr-bool-toggle__seg {
    border: 0;
    background: transparent;
    color: var(--re-muted);
    padding: 2px 10px;
    cursor: pointer;
    font: inherit;
}

.cr-rules-editor .cr-bool-toggle__seg:hover {
    background: var(--re-row-hover);
}

.cr-rules-editor .cr-bool-toggle__seg--active {
    background: var(--re-accent-soft);
    color: var(--re-accent-strong);
    font-weight: 600;
}

/* MultiChipInput editing affordances — × on each chip + trailing "+ Add" button. */
.cr-rules-editor .cr-multi-chip__remove {
    border: 0;
    background: transparent;
    color: var(--re-muted);
    cursor: pointer;
    font: inherit;
    padding: 0 0 0 4px;
    line-height: 1;
}

.cr-rules-editor .cr-multi-chip__remove:hover {
    color: var(--re-danger);
}

.cr-rules-editor .cr-multi-chip-input__add {
    border: 1px dashed var(--re-placeholder-border);
    background: transparent;
    color: var(--re-placeholder-fg);
    padding: 2px 8px;
    border-radius: 4px;
    cursor: pointer;
    font: inherit;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    line-height: 1.4;
}

.cr-rules-editor .cr-multi-chip-input__add:hover {
    color: var(--re-fg);
    border-color: var(--re-bd-strong);
}

/* TodayChip — system-pill palette (matches OffsetEditor). The × switch-back affords
   "go back to a literal" via OperandModeChanged → Literal. */
.cr-rules-editor .cr-today-chip {
    border: 1px solid var(--re-system-bd);
    background: var(--re-system-bg);
    color: var(--re-system-fg);
    padding: 2px 8px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    line-height: 1.4;
}

.cr-rules-editor .cr-today-chip__icon { color: var(--re-system-fg); }
.cr-rules-editor .cr-today-chip__label { font-weight: 600; }

.cr-rules-editor .cr-today-chip__remove {
    border: 0;
    background: transparent;
    color: var(--re-system-fg);
    cursor: pointer;
    font: inherit;
    padding: 0 0 0 4px;
    line-height: 1;
    opacity: 0.7;
}

.cr-rules-editor .cr-today-chip__remove:hover { opacity: 1; }

/* DateLiteral — same chip register as TextLiteral but with a calendar icon prefix. */
.cr-rules-editor .cr-date-literal {
    border: 1px solid var(--re-bd);
    background: var(--re-chip-bg);
    color: inherit;
    padding: 2px 8px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font: inherit;
    line-height: 1.4;
}

.cr-rules-editor .cr-date-literal--clickable {
    cursor: pointer;
}

.cr-rules-editor .cr-date-literal--clickable:hover {
    border-color: var(--re-bd-strong);
}

.cr-rules-editor .cr-date-literal__icon { color: var(--re-muted); }

/* OffsetEditor — clickable + editing variants. Editing inlines a number input + unit
   select inside the system-pill chrome. */
.cr-rules-editor .cr-offset-editor--clickable {
    cursor: pointer;
    border: 1px solid var(--re-system-bd);
    background: var(--re-system-bg);
    color: var(--re-system-fg);
    font: inherit;
    padding: 2px 8px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    line-height: 1.4;
}

.cr-rules-editor .cr-offset-editor--clickable:hover {
    /* darken slightly so the chip reads as interactive without breaking the warm-amber palette */
    filter: brightness(0.96);
}

.cr-rules-editor .cr-offset-editor--editing {
    gap: 6px;
}

.cr-rules-editor .cr-offset-editor__commit {
    border: 0;
    background: var(--re-accent);
    color: #fff;
    padding: 0 6px;
    border-radius: 3px;
    cursor: pointer;
    font: inherit;
    font-weight: 600;
    line-height: 1.6;
}

/* Generic editor-input chrome — TextLiteral input, DateLiteral date input,
   OffsetEditor amount input + unit select. One token family so all four read
   consistently when focused. */
.cr-rules-editor .cr-rules-input {
    border: 1px solid var(--re-editor-input-border);
    background: var(--re-editor-input-bg);
    color: var(--re-fg);
    padding: 1px 6px;
    border-radius: 3px;
    font: inherit;
    line-height: 1.4;
    outline: 0;
    width: auto;
    min-width: 6em;
}

.cr-rules-editor .cr-rules-input:focus {
    border-color: var(--re-accent);
    box-shadow: 0 0 0 2px var(--re-editor-input-focus-ring);
}

.cr-rules-editor .cr-rules-input--narrow { min-width: 0; width: 4em; }
.cr-rules-editor .cr-rules-input--unit   { min-width: 0; width: auto; }
.cr-rules-editor .cr-rules-input--date   { min-width: 0; width: 9em; }

/* System-value badge — wraps a FieldReference-as-right-operand FieldChip with a "field"
   badge prefix and × switch-back. Visual lineage: same warm-amber palette as TodayChip /
   OffsetEditor (the "system value" register). The × dispatches OperandModeChanged →
   Literal so the right operand reverts to a fresh JsonNull placeholder. */
.cr-rules-editor .cr-system-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 1px solid var(--re-system-bd);
    background: var(--re-system-bg);
    color: var(--re-system-fg);
    padding: 1px 6px 1px 8px;
    border-radius: 4px;
    line-height: 1.4;
}

.cr-rules-editor .cr-system-badge__label {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--re-system-fg);
}

.cr-rules-editor .cr-system-badge__remove {
    border: 0;
    background: transparent;
    color: var(--re-system-fg);
    cursor: pointer;
    font: inherit;
    padding: 0 0 0 4px;
    line-height: 1;
    opacity: 0.7;
}

.cr-rules-editor .cr-system-badge__remove:hover { opacity: 1; }

/* The fx button — extracted in Pass 3 from inline ComparisonRowView markup into the
   OperandModeButton component. Was previously disabled; now clickable. */
.cr-rules-editor .cr-fx-button {
    border: 1px dashed var(--re-bd-strong);
    background: transparent;
    color: var(--re-muted);
    padding: 1px 6px;
    border-radius: 3px;
    cursor: pointer;
    font: inherit;
    font-size: 11px;
    line-height: 1.4;
}

.cr-rules-editor .cr-fx-button:hover {
    color: var(--re-fg);
    border-color: var(--re-accent);
}

/* ============================================================================
   Task 5 — Pass 4 — row + group hover actions, And/Or interactive toggle,
   footer add-buttons.

   Hover-revealed action clusters use opacity+pointer-events transitions so the
   row's height stays the same whether actions are visible or not (the brief's
   layout-stability rule). They reveal on :hover or :focus-within (keyboard
   reachability via tab into the cluster).
   ============================================================================ */

.cr-rules-editor .cr-row-actions,
.cr-rules-editor .cr-group-actions {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin-left: auto;
    opacity: 0;
    pointer-events: none;
    transition: opacity 120ms ease;
}

.cr-rules-editor .cr-comparison-row:hover .cr-row-actions,
.cr-rules-editor .cr-comparison-row:focus-within .cr-row-actions,
.cr-rules-editor .cr-group-header:hover .cr-group-actions,
.cr-rules-editor .cr-group-header:focus-within .cr-group-actions {
    opacity: 1;
    pointer-events: auto;
}

.cr-rules-editor .cr-row-actions__btn,
.cr-rules-editor .cr-group-actions__btn {
    border: 0;
    background: transparent;
    color: var(--re-muted);
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 3px;
    font: inherit;
    line-height: 1;
}

.cr-rules-editor .cr-row-actions__btn:hover,
.cr-rules-editor .cr-group-actions__btn:hover {
    color: var(--re-fg);
    background: var(--re-row-hover);
}

.cr-rules-editor .cr-row-actions__btn--re-danger:hover,
.cr-rules-editor .cr-group-actions__btn--re-danger:hover {
    color: var(--re-danger);
    background: var(--re-danger-soft);
}

.cr-rules-editor .cr-row-actions__not,
.cr-rules-editor .cr-group-actions__not {
    font-weight: 700;
    font-style: italic;
}

/* And/Or interactive toggle — drop the disabled cursor styles inherited from the
   read-only Pass 1 markup. The active segment keeps the existing palette. */
.cr-rules-editor .cr-group-header__toggle button {
    cursor: pointer;
}

.cr-rules-editor .cr-group-header__toggle button:disabled {
    cursor: default;
}

/* Footer "+ Add condition" / "+ Add group" buttons. Same dashed-neutral register as
   the FieldChip placeholder so the "I'm a creator affordance" reads consistently. */
.cr-rules-editor .cr-group-footer {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cr-rules-editor .cr-group-footer__add-cluster {
    display: flex;
    gap: 0.25rem;
    margin-left: auto;
}

.cr-rules-editor .cr-group-footer__add {
    border: 1px dashed var(--re-placeholder-border);
    background: transparent;
    color: var(--re-placeholder-fg);
    padding: 2px 8px;
    border-radius: 4px;
    cursor: pointer;
    font: inherit;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    line-height: 1.4;
}

.cr-rules-editor .cr-group-footer__add:hover {
    color: var(--re-accent-strong);
    border-color: var(--re-accent);
}

/* NOT tab — clickable variant. Same shape as the read-only span; gain a pointer
   cursor + hover register. */
/* The NOT tab is a <button> in Pass 5 (was a <span> in earlier passes) so clicking it
   can dispatch UnwrapNot. Reset the user-agent button defaults that would otherwise
   show through the .cr-not-tab base styling — border, font-family, font-size — and add
   a hover hint so the affordance reads as interactive. */
.cr-rules-editor .cr-not-tab {
    border: 0;
    font: inherit;
    font-size: 10px;
    font-weight: 600;
    cursor: pointer;
}

.cr-rules-editor .cr-not-tab:hover {
    filter: brightness(0.92);
    text-decoration: line-through;
}

.cr-rules-editor .cr-not-tab:hover::after {
    content: " ×";
    margin-left: 2px;
}

/* MissingFieldChip clickable variant — Pass 5: rendered as a <button> so clicking opens
   the field picker to fix the dangling reference. The base .cr-missing-field-chip styles
   provide the danger-palette chrome; this variant adds button defaults reset + cursor
   pointer + hover register. */
.cr-rules-editor .cr-missing-field-chip--clickable {
    cursor: pointer;
    font: inherit;
    font-size: 13px;
    line-height: 1.4;
}

.cr-rules-editor .cr-missing-field-chip--clickable:hover {
    filter: brightness(0.96);
}

/* ============================================================================
   Jump-to-AstPath flash highlight.

   When the user clicks "Jump →" on a SaveBlockedPanel diagnostic, the page sets
   _pendingFlashId; OnAfterRenderAsync calls flashElement which adds .cr-flash-highlight
   to the targeted element for ~1.5s. The animation runs once and removes itself; the
   JS helper also removes the class on timeout as a belt-and-suspenders measure if the
   animationend event doesn't fire (e.g., the element re-renders mid-pulse).

   Box-shadow rather than background so the chrome of the targeted chip / pill / button
   isn't fighting the diagnostic's own color register.
   ============================================================================ */

@keyframes cr-flash-highlight-pulse {
    0%   { box-shadow: 0 0 0 0 var(--re-flash-color); }
    25%  { box-shadow: 0 0 0 6px var(--re-flash-color); }
    100% { box-shadow: 0 0 0 0 transparent; }
}

.cr-rules-editor .cr-flash-highlight {
    animation: cr-flash-highlight-pulse 1.5s ease-out 1;
    border-radius: 6px;
    /* Lift above sibling chrome so the glow doesn't clip behind a popover-anchor. */
    position: relative;
    z-index: 2;
}

/* ============================================================================
   Task 6 — EditorEmptyState.

   Centered card with a message + "+ New rule" button. Used by the bare
   /rules-editor route and by /rules-editor/{miss} when the slug doesn't resolve.
   Visual weight is subdued — empty states are content-of-the-moment, not chrome.
   The button is visual scaffolding only this task; real new-rule creation
   depends on the rail (Task 7) + slice handler.
   ============================================================================ */

.cr-rules-editor .cr-rules-empty-state {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 60vh;
    padding: 24px;
}

.cr-rules-editor .cr-rules-empty-state__card {
    background: var(--re-empty-bg);
    border: 1px solid var(--re-bd);
    border-radius: 10px;
    padding: 40px 56px;
    text-align: center;
    max-width: 480px;
}

.cr-rules-editor .cr-rules-empty-state__heading {
    margin: 0 0 20px 0;
    font-size: 18px;
    font-weight: 600;
    color: var(--re-empty-fg);
    line-height: 1.4;
}

.cr-rules-editor .cr-rules-empty-state__new {
    border: none;
    border-radius: 6px;
    padding: 8px 18px;
    font-size: 13px;
    font-weight: 600;
    background: var(--re-accent);
    color: #ffffff;
    cursor: pointer;
}

.cr-rules-editor .cr-rules-empty-state__new:hover,
.cr-rules-editor .cr-rules-empty-state__new:focus-visible {
    background: var(--re-accent-strong);
    outline: none;
}

/* ============================================================================
   Task 6 — EditorHeader.

   Renders, top-to-bottom: breadcrumb (slug) + last-edited line + target pill on
   the top row, then the rule name (large), then the description (paragraph).
   Edit-on-click flips name + description into inputs simultaneously; static
   variant uses an unstyled <button> so the click target is keyboard-accessible
   and the hover/focus affordance sits on a real interactive element.
   ============================================================================ */

.cr-rules-editor .cr-rules-header {
    background: var(--re-header-bg);
    padding: 20px 24px 16px;
    border-bottom: 1px solid var(--re-bd);
    display: flex;
    flex-direction: column;
    gap: 0;
}

/* Edit mode reintroduces the row gap so the inputs / Save+Cancel cluster aren't
   crammed against the breadcrumb and description. Read mode collapses the gap so the
   breadcrumb + name + description read as a tight title block. */
.cr-rules-editor .cr-rules-header--editing {
    gap: 8px;
}

.cr-rules-editor .cr-rules-header__top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    min-height: 24px;
}

.cr-rules-editor .cr-rules-header__meta {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-width: 0;
    flex-wrap: wrap;
}

.cr-rules-editor .cr-rules-header__meta-sep {
    color: var(--re-muted-2);
    font-size: 11px;
}

.cr-rules-editor .cr-rules-header__breadcrumb {
    font-family: 'JetBrains Mono', Menlo, Consolas, monospace;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--re-header-breadcrumb-fg);
}

.cr-rules-editor .cr-rules-header__last-edited {
    font-size: 11px;
    color: var(--re-header-last-edited-fg);
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.cr-rules-editor .cr-rules-header__last-edited-icon {
    font-size: 10px;
    color: var(--re-header-last-edited-fg);
}

.cr-rules-editor .cr-rules-header__target {
    /* Position relative so the popover anchors below the pill. The popover uses
       position: absolute; top: 100% from this container per RulesEditorPopover's
       positioning convention. */
    position: relative;
    flex-shrink: 0;
}

/* TargetTypePill — both static and clickable variants. Both render the chip;
   clickable variant adds hover + focus affordances. */
.cr-rules-editor .cr-rules-target-pill {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border: 1px solid var(--re-target-pill-border);
    border-radius: 999px;
    background: var(--re-target-pill-bg);
    color: var(--re-target-pill-fg);
    font-size: 11px;
    font-weight: 500;
    line-height: 1.6;
    white-space: nowrap;
}

.cr-rules-editor .cr-rules-target-pill--clickable {
    cursor: pointer;
    /* Reset native button chrome so the pill renders identically across variants. */
    font-family: inherit;
    text-align: left;
}

.cr-rules-editor .cr-rules-target-pill--clickable:hover,
.cr-rules-editor .cr-rules-target-pill--clickable:focus-visible {
    background: var(--re-target-pill-hover-bg);
    outline: none;
}

.cr-rules-editor .cr-rules-target-pill--clickable[aria-expanded="true"] {
    background: var(--re-target-pill-hover-bg);
}

/* Name row — large semibold text with optional dirty dot + edit-mode actions. */
.cr-rules-editor .cr-rules-header__name-row {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 32px;
}

.cr-rules-editor .cr-rules-header__name {
    /* Native button reset — header name reads as a static heading until clicked. */
    background: transparent;
    border: none;
    padding: 2px 4px 2px 0;
    font-family: inherit;
    font-size: 22px;
    font-weight: 600;
    color: var(--re-header-name-fg);
    text-align: left;
    cursor: text;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 auto;
    min-width: 0;
}

.cr-rules-editor .cr-rules-header__name:hover .cr-rules-header__name-text,
.cr-rules-editor .cr-rules-header__name:focus-visible .cr-rules-header__name-text {
    /* Hairline underline as the edit-on-click cue. */
    text-decoration: underline;
    text-decoration-color: var(--re-edit-hint-fg);
    text-decoration-thickness: 1px;
    text-underline-offset: 4px;
}

.cr-rules-editor .cr-rules-header__name:focus-visible {
    outline: none;
}

.cr-rules-editor .cr-rules-header__dirty {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--re-dirty-dot-bg);
    flex-shrink: 0;
}

.cr-rules-editor .cr-rules-header__name-input {
    flex: 1 1 auto;
    font-family: inherit;
    font-size: 22px;
    font-weight: 600;
    color: var(--re-header-name-fg);
    background: var(--re-header-input-bg);
    border: 1px solid var(--re-header-input-border);
    border-radius: 4px;
    padding: 4px 8px;
    line-height: 1.4;
}

.cr-rules-editor .cr-rules-header__name-input:focus-visible {
    outline: none;
    border-color: var(--re-accent);
    box-shadow: 0 0 0 3px var(--re-header-input-focus-ring);
}

.cr-rules-editor .cr-rules-header__actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}

.cr-rules-editor .cr-rules-header__action {
    border: 1px solid var(--re-bd-strong);
    background: transparent;
    border-radius: 4px;
    padding: 4px 12px;
    font-family: inherit;
    font-size: 12px;
    cursor: pointer;
    color: var(--re-fg);
}

.cr-rules-editor .cr-rules-header__action:hover,
.cr-rules-editor .cr-rules-header__action:focus-visible {
    background: var(--re-row-hover);
    outline: none;
}

.cr-rules-editor .cr-rules-header__action--save {
    background: var(--re-accent);
    color: #ffffff;
    border-color: var(--re-accent);
    font-weight: 600;
}

.cr-rules-editor .cr-rules-header__action--save:hover,
.cr-rules-editor .cr-rules-header__action--save:focus-visible {
    background: var(--re-accent-strong);
    border-color: var(--re-accent-strong);
    color: #ffffff;
}

/* Description row — paragraph text or textarea. */
.cr-rules-editor .cr-rules-header__description-row {
    display: flex;
    align-items: stretch;
    gap: 12px;
}

.cr-rules-editor .cr-rules-header__description {
    background: transparent;
    border: none;
    padding: 2px 4px 2px 0;
    font-family: inherit;
    font-size: 13px;
    color: var(--re-header-description-fg);
    text-align: left;
    cursor: text;
    line-height: 1.5;
    flex: 1 1 auto;
    min-width: 0;
}

.cr-rules-editor .cr-rules-header__description:hover,
.cr-rules-editor .cr-rules-header__description:focus-visible {
    text-decoration: underline;
    text-decoration-color: var(--re-edit-hint-fg);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    outline: none;
}

.cr-rules-editor .cr-rules-header__description--empty {
    color: var(--re-edit-hint-fg);
    font-style: italic;
}

.cr-rules-editor .cr-rules-header__description-input {
    flex: 1 1 auto;
    font-family: inherit;
    font-size: 13px;
    color: var(--re-header-name-fg);
    background: var(--re-header-input-bg);
    border: 1px solid var(--re-header-input-border);
    border-radius: 4px;
    padding: 6px 8px;
    line-height: 1.5;
    resize: vertical;
    min-height: 56px;
}

.cr-rules-editor .cr-rules-header__description-input:focus-visible {
    outline: none;
    border-color: var(--re-accent);
    box-shadow: 0 0 0 3px var(--re-header-input-focus-ring);
}

/* ============================================================================
   Task 6 — EditorFooter height alignment.

   The four footer states (CleanFooter passive, CleanFooter active,
   SaveWithWarningsFooter, SaveBlockedPanel collapsed) must render at the same
   height so the rule body's bottom edge doesn't jump as the user transitions
   between states (clean → dirty → warnings/errors).

   Pad to the tallest natural state — empirically the SaveBlockedPanel and
   SaveWithWarningsFooter rows at ~44px (0.625rem vert padding + 12px button
   chrome + 12px line-height). CleanFooter's natural height is ~36px (smaller
   vert padding + smaller button chrome). min-height with box-sizing:border-box
   on the shared .cr-rules-footer class snaps the shorter footer up without
   forcing all three to share padding.
   ============================================================================ */

.cr-rules-editor .cr-rules-footer {
    min-height: 2.75rem;
    box-sizing: border-box;
    /* Pin to the bottom of the editor section (which is flex column with flex:1 inside
     a flex-column pane). Auto-margin pushes this past whatever the body's natural
     height resolves to so the save bar always sits at the bottom of the pane, not
     directly under the rule body. */
    margin-top: auto;
    border-top: 1px solid var(--re-bd);
    padding: 12px 24px;
    background: var(--re-surface);
}

/* ============================================================================
   Task 6 — CleanFooter active/passive + Used-by line.
   ============================================================================ */

.cr-rules-editor .cr-clean-footer--passive .cr-clean-footer__discard,
.cr-rules-editor .cr-clean-footer--passive .cr-clean-footer__save {
    opacity: 0.5;
    cursor: not-allowed;
}

.cr-rules-editor .cr-clean-footer__discard[disabled],
.cr-rules-editor .cr-clean-footer__save[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Used-by line — sits in the footer's left cluster. Renders only when the
   rule has consumers; absent slugs → no markup → no line. */
.cr-rules-editor .cr-clean-footer__used-by {
    color: var(--re-used-by-fg);
    font-size: 12px;
}

.cr-rules-editor .cr-clean-footer__used-by-sep {
    color: var(--re-used-by-fg);
    margin: 0 1px 0 -1px;
}

.cr-rules-editor .cr-clean-footer__used-by-slug {
    font-family: 'JetBrains Mono', Menlo, Consolas, monospace;
    font-size: 11px;
    padding: 1px 6px;
    border: 1px solid var(--re-used-by-slug-border);
    background: var(--re-used-by-slug-bg);
    color: var(--re-used-by-slug-fg);
    border-radius: 4px;
}


/* =====================================================================================
   Task 8b — Rules editor rail
   =====================================================================================
   Layout: flush rail (280px, right-bordered, no card) on the left + editor pane on the
   right. Translated from shell.jsx's <Rail> function (lines 37–105 of the design
   handoff). All sizing/color values picked to mirror the source's Tailwind tokens
   against the existing --re-fg / --re-muted / --re-bd / --re-surface / --re-surface-2 / --re-accent /
   --re-accent-soft / --re-accent-strong / --re-danger / --re-danger-soft semantic vars. */

/* Page-level flex row: rail on the left, pane on the right. flex:1 + min-height:0
   would let the page fill whatever vertical space its parent (.cc-page) gives it
   — but .cc-page only has flex:1 (no max-height), so flex:1 alone resolves to the
   children's natural height and the layout grows past the viewport.
   Cap with an explicit max-height that subtracts the chrome stacked above and below
   the rules editor inside the main column:
     • topbar:                var(--th-chrome-topbar-h)  (default 52px)
     • AppDef switcher bar:   var(--th-chrome-subbar-h)  (44px — rules editor is AppDef-scoped)
     • global Footer:         ~50px            (14 padding ×2 + ~14 text + 1 border)
   The internal scroll is on .cr-rule-preview__body so the header stays pinned at top
   and the footer stays pinned at bottom. The same calc is reused by .cr-rule-rail and
   .cr-test-panel via --re-editor-page-height so future chrome adjustments only
   touch one place. Phase 5 removed the legacy PageHeader strip (~84px) from the
   calc — the topbar breadcrumb absorbed its role and no per-page chrome stacks
   above the editor anymore. */
:root {
    --re-editor-chrome:     94px;
    --re-editor-page-height: calc(100vh - var(--th-chrome-topbar-h, 52px) - var(--re-editor-chrome));
}
.cr-rules-editor-page {
    display: flex;
    align-items: stretch;
    gap: 0;
    padding: 0;
    flex: 1 1 auto;
    min-height: 0;
    max-height: var(--re-editor-page-height);
    overflow: hidden;
}

.cr-rules-editor-page .cr-rules-editor-pane {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    background: var(--re-surface);
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* The section inside the pane fills it vertically so the footer pins to the bottom
   regardless of how much rule body content there is. */
.cr-rules-editor .cr-rule-single {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

/* Body wrapper between header and footer — scrollable on overflow. The header above
   and footer below stay pinned because they're flex 0 0 auto siblings; this is the
   only scrollable region. */
.cr-rules-editor .cr-rule-preview__body {
    flex: 1 1 auto;
    padding: 16px 24px 24px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
}

/* The rail itself — flush against the page edge, no border-radius, no card. Right
   border is the only visual separator from the editor pane (per shell.jsx:47:
   `border-r border-[var(--re-bd)] bg-[var(--re-surface-2)]`). */
.cr-rule-rail {
    flex: 0 0 280px;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--re-bd);
    background: var(--re-surface-2);
    /* Reuse the page-wrapper's bound. The previous calc(100vh - topbar) ignored
       footer + AppDef switcher bar chrome, which let the rail set the row height
       to almost 100vh and pushed the whole page past the viewport. */
    max-height: var(--re-editor-page-height);
}

/* Header section holds title + new-button + search + chips + count line, all
   bottom-bordered as a unit (shell.jsx:48: border-b border-[var(--re-bd)] px-3 pb-2 pt-3). */
.cr-rule-rail__header {
    display: flex;
    flex-direction: column;
    padding: 0.75rem 0.75rem 0.5rem;
    border-bottom: 1px solid var(--re-bd);
    background: transparent;
}

.cr-rule-rail__title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.cr-rule-rail__title {
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-muted);
    margin: 0;
}

/* + New button: tight, white pill (shell.jsx:51-53). */
.cr-rule-rail__new-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    background: var(--re-surface);
    border: 1px solid var(--re-bd);
    color: var(--re-fg);
    padding: 3px 8px;
    font-size: 11px;
    border-radius: 4px;
    cursor: pointer;
}

.cr-rule-rail__new-btn:hover {
    border-color: var(--re-bd-strong);
}

/* Search box: 28px-tall white pill with inline magnifying-glass icon
   (shell.jsx:55-58). Container is bordered, the input is transparent. */
.cr-rule-rail__search {
    margin-top: 0.5rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    height: 28px;
    padding: 0 0.5rem;
    background: var(--re-surface);
    border: 1px solid var(--re-bd);
    border-radius: 4px;
}

.cr-rule-rail__search-icon {
    width: 14px;
    height: 14px;
    color: var(--re-muted);
    flex: 0 0 14px;
}

.cr-rule-rail__search-input {
    flex: 1 1 auto;
    background: transparent;
    color: var(--re-fg);
    border: 0;
    outline: none;
    font-size: 12.5px;
    padding: 0;
    min-width: 0;
}

.cr-rule-rail__search-input::placeholder {
    color: var(--re-muted-2);
}

.cr-rule-rail__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-top: 0.5rem;
}

/* Chip: white-bg pill, accent-soft when active (shell.jsx:30). */
.cr-chip {
    background: var(--re-surface);
    border: 1px solid var(--re-bd);
    color: var(--re-muted);
    padding: 2px 8px;
    font-size: 11px;
    border-radius: 999px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

.cr-chip:hover {
    color: var(--re-fg);
}

.cr-chip--selected {
    background: var(--re-accent-soft);
    color: var(--re-accent-strong);
    border-color: var(--re-accent);
}

/* Issues chip: same shape as other chips when inactive; danger-toned when active
   (shell.jsx:64-70). */
.cr-chip--issues {
    /* Inherits .cr-chip defaults. */
}

.cr-chip--issues.cr-chip--selected {
    background: var(--re-danger-soft);
    color: var(--re-danger);
    border-color: var(--re-danger);
}

.cr-chip--issues .cr-chip__glyph {
    display: inline-flex;
    width: 12px;
    height: 12px;
    color: var(--re-danger);
}

.cr-chip--issues .cr-chip__glyph .cr-severity-glyph {
    width: 12px;
    height: 12px;
}

/* Count pill inside the issues chip — inactive uses surface-2/muted; active flips to
   white/danger (shell.jsx:69). */
.cr-chip--issues .cr-chip__count {
    margin-left: 0.125rem;
    background: var(--re-surface-2);
    color: var(--re-muted);
    padding: 0 6px;
    border-radius: 999px;
    font-size: 10px;
    font-family: 'JetBrains Mono', Menlo, Consolas, monospace;
    line-height: 1;
}

.cr-chip--issues.cr-chip--selected .cr-chip__count {
    background: var(--re-surface);
    color: var(--re-danger);
}

/* "Showing N of N" line — plain inline text, no chrome (shell.jsx:73-75). */
.cr-rule-rail__count {
    margin: 0.375rem 0 0;
    font-size: 10.5px;
    color: var(--re-muted-2);
}

.cr-rule-rail__count-active {
    font-weight: 600;
    color: var(--re-muted);
}

/* Scrollable list body. Padding y-1 (shell.jsx:78). */
.cr-rule-rail__list {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
    padding: 0.25rem 0;
}

/* Each rule row: 2px transparent left border slot, padding px-3 py-2. Selected state
   swaps left border to accent and background to surface (white-on-surface-2 pop).
   Fixed height kept (84px) so <Virtualize> can window correctly. */
.cr-rule-rail__entry {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    width: 100%;
    padding: 0.75rem 1rem;
    height: 100px;
    background: transparent;
    border: 0;
    border-left: 2px solid transparent;
    text-align: left;
    cursor: pointer;
    color: var(--re-fg);
    box-sizing: border-box;
}

.cr-rule-rail__entry + .cr-rule-rail__entry {
    border-top: 1px solid var(--re-bd);
}

.cr-rule-rail__entry:hover {
    background: var(--re-surface);
}

.cr-rule-rail__entry--selected {
    background: var(--re-accent-soft);
    border-left-color: var(--re-accent);
}

.cr-rule-rail__entry--selected:hover {
    background: var(--re-accent-soft);
}

.cr-rule-rail__entry--re-placeholder {
    cursor: default;
    color: var(--re-muted);
}

.cr-rule-rail__entry-body {
    flex: 1 1 auto;
    min-width: 0;
}

/* Top line: severity dot (inline, not a column) + name. */
.cr-rule-rail__entry-headline {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    min-width: 0;
}

.cr-rule-rail__entry-glyph {
    display: inline-flex;
    flex: 0 0 12px;
    width: 12px;
    height: 12px;
}

.cr-rule-rail__entry-glyph .cr-severity-glyph {
    width: 12px;
    height: 12px;
}

.cr-rule-rail__entry-glyph .cr-severity-glyph--error {
    color: var(--re-danger);
}

.cr-rule-rail__entry-glyph .cr-severity-glyph--re-warning {
    color: #C7951C;
}

.cr-rule-rail__entry-name {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--re-fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1 1 auto;
}

.cr-rule-rail__entry-desc {
    margin: 1px 0 0;
    font-size: 11.5px;
    line-height: 1.35;
    color: var(--re-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
    overflow: hidden;
}

.cr-rule-rail__entry-meta {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    margin-top: 0.375rem;
    font-size: 10.5px;
    color: var(--re-muted-2);
}

.cr-rule-rail__entry-meta-sep {
    color: var(--re-muted-2);
}

.cr-rule-rail__entry-usage {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1 1 auto;
    min-width: 0;
}

/* Skeleton placeholders shown while <Virtualize> is fetching a window. */
.cr-rule-rail__entry .cr-skeleton-name,
.cr-rule-rail__entry .cr-skeleton-desc {
    display: block;
    background: var(--re-row-hover);
    border-radius: 3px;
    animation: cr-rail-skeleton-pulse 1.4s ease-in-out infinite;
}

.cr-rule-rail__entry .cr-skeleton-name {
    width: 60%;
    height: 14px;
    margin-bottom: 6px;
}

.cr-rule-rail__entry .cr-skeleton-desc {
    width: 90%;
    height: 11px;
}

@keyframes cr-rail-skeleton-pulse {
    0% { opacity: 0.6; }
    50% { opacity: 1.0; }
    100% { opacity: 0.6; }
}

/* Compact mobile layout — stack rail above pane below ~960px. */
@media (max-width: 960px) {
    .cr-rules-editor-page {
        flex-direction: column;
    }
    .cr-rule-rail {
        flex: 0 0 auto;
        width: 100%;
        max-height: 50vh;
    }
}

/* ===========================================================================
   Test panel (Task 9) — right column of the three-column rules-editor shell.
   Composes: TestPanelHeader -> InputsPanel -> MirrorTree -> TraceList. The
   refused branch swaps MirrorTree + TraceList for RefusedReasonPanel + a
   disabled InputsPanel. Three-channel severity encoding (shape + word + color)
   per design handoff section 4.2.
   =========================================================================== */

.cr-test-panel {
    flex: 0 0 400px;
    display: flex;
    flex-direction: column;
    border-left: 1px solid var(--re-bd);
    background: var(--re-surface-2);
    /* Reuse the page-wrapper's bound — same chrome story as .cr-rule-rail above. */
    max-height: var(--re-editor-page-height);
    overflow: hidden;
    min-width: 0;
}

.cr-test-panel__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--re-bd);
    background: var(--re-surface);
}

.cr-test-panel__title {
    margin: 0;
    font-size: 12.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted);
}

.cr-test-panel__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.cr-test-panel__empty {
    padding: 1.5rem 1rem;
    color: var(--re-muted);
    font-style: italic;
    font-size: 12.5px;
}

.cr-test-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    height: 1.75rem;
    padding: 0 0.625rem;
    border-radius: 0.375rem;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    border: 1px solid transparent;
}

.cr-test-pill__glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 0.875rem;
    height: 0.875rem;
}

.cr-test-pill--none {
    background: transparent;
    border-color: var(--re-bd);
    color: var(--re-muted-2, var(--re-muted));
}

.cr-test-pill--pass {
    background: var(--re-accent);
    color: #fff;
}

.cr-test-pill--fail {
    background: transparent;
    border-color: var(--re-bd-strong, var(--re-bd));
    color: var(--re-muted);
}

.cr-test-pill--error {
    background: var(--re-danger);
    color: #fff;
}

.cr-test-pill--refused {
    background: var(--re-danger-soft);
    border: 2px dashed var(--re-danger);
    color: var(--re-danger);
}

.cr-test-inputs {
    background: var(--re-surface);
    border: 1px solid var(--re-bd);
    border-radius: 0.375rem;
}

.cr-test-inputs__header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--re-bd);
}

.cr-test-inputs__title {
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted);
}

.cr-test-inputs__count {
    display: inline-flex;
    align-items: center;
    padding: 0 0.375rem;
    height: 1rem;
    border-radius: 999px;
    background: var(--re-surface-2);
    font: 10px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--re-muted-2, var(--re-muted));
}

.cr-test-inputs__disabled-note {
    margin-left: auto;
    font-size: 10.5px;
    font-style: italic;
    color: var(--re-muted-2, var(--re-muted));
}

.cr-test-inputs__rows {
    display: flex;
    flex-direction: column;
}

.cr-test-inputs__rows--disabled {
    opacity: 0.5;
}

.cr-test-inputs__rows > .cr-test-input-row + .cr-test-input-row {
    border-top: 1px solid var(--re-bd);
}

.cr-test-inputs__empty {
    padding: 0.75rem;
    color: var(--re-muted);
    font-size: 12px;
    font-style: italic;
}

.cr-test-input-row {
    padding: 0.5rem 0.75rem;
}

.cr-test-input-row__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}

.cr-test-input-row__title {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--re-fg);
}

.cr-test-input-row__section {
    font: 10px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted-2, var(--re-muted));
}

.cr-test-input-row__editor {
    margin-top: 0.375rem;
}

.cr-test-input {
    font-size: 12.5px;
}

.cr-test-input--text,
.cr-test-input--number,
.cr-test-input--date {
    height: 1.75rem;
    padding: 0 0.5rem;
    border: 1px solid var(--re-bd);
    border-radius: 0.375rem;
    background: var(--re-surface);
    color: var(--re-fg);
    outline: none;
}

.cr-test-input--text:focus,
.cr-test-input--number:focus,
.cr-test-input--date:focus {
    border-color: var(--re-accent);
}

.cr-test-input--text { width: 100%; max-width: 16.25rem; }
.cr-test-input--number { width: 8rem; font: 12.5px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

.cr-test-input--bool {
    display: inline-flex;
    align-items: center;
    height: 1.75rem;
    padding: 0.125rem;
    border: 1px solid var(--re-bd);
    border-radius: 0.375rem;
    background: var(--re-surface-2);
    gap: 0;
}

.cr-test-input__seg {
    border: none;
    background: transparent;
    color: var(--re-muted);
    padding: 0 0.625rem;
    height: 1.375rem;
    border-radius: 0.25rem;
    font-size: 12px;
    cursor: pointer;
}

.cr-test-input__seg--active {
    background: var(--re-surface);
    color: var(--re-fg);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

.cr-test-input--choice {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.25rem;
}

.cr-test-input__chip {
    border: 1px solid var(--re-bd);
    background: var(--re-surface);
    color: var(--re-fg);
    padding: 0.125rem 0.5rem;
    border-radius: 0.375rem;
    font-size: 11.5px;
    cursor: pointer;
}

.cr-test-input__chip:hover:not(:disabled) {
    border-color: var(--re-bd-strong, var(--re-bd));
}

.cr-test-input__chip--active {
    border-color: var(--re-accent);
    background: var(--re-accent-soft);
    color: var(--re-accent-strong);
    font-weight: 600;
}

.cr-mirror-tree {
    background: var(--re-surface-2);
    border: 1px solid var(--re-bd);
    border-radius: 0.375rem;
    padding: 0.5rem;
}

.cr-mirror-tree__header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.375rem;
    padding: 0 0.25rem;
}

.cr-mirror-tree__title {
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted);
}

.cr-mirror-tree__subtitle {
    font-size: 10.5px;
    font-style: italic;
    color: var(--re-muted-2, var(--re-muted));
}

.cr-mirror-tree__body {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.cr-mirror-group {
    position: relative;
    border: 1px solid var(--re-bd);
    border-radius: 0.375rem;
    background: var(--re-surface);
}

.cr-mirror-group--skipped { opacity: 0.5; }

.cr-mirror-group--hovered {
    outline: 2px solid var(--re-accent);
    outline-offset: 1px;
}

.cr-mirror-group__head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.5rem;
    font-size: 11px;
}

.cr-mirror-group__op {
    padding: 0.0625rem 0.375rem;
    border-radius: 0.25rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.cr-mirror-group--and .cr-mirror-group__op {
    background: var(--re-accent-soft);
    color: var(--re-accent-strong);
}

.cr-mirror-group--re-or .cr-mirror-group__op {
    background: var(--re-or-soft, var(--re-accent-soft));
    color: var(--re-or, var(--re-accent-strong));
}

.cr-mirror-group__skip-note {
    font: 10px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted-2, var(--re-muted));
}

.cr-mirror-group__children {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0 0.5rem 0.375rem 0.75rem;
}

.cr-mirror-not {
    padding: 0.375rem;
    border: 1px solid var(--re-not-bd, var(--re-danger-soft));
    background: var(--re-not-bg, var(--re-surface-2));
    border-radius: 0.375rem;
}

.cr-mirror-not--skipped { opacity: 0.5; }
.cr-mirror-not--hovered {
    outline: 2px solid var(--re-accent);
    outline-offset: 1px;
}

.cr-mirror-not__head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-not-fg, var(--re-danger));
}

.cr-mirror-comparison {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem;
    background: var(--re-surface);
    border: 1px solid var(--re-bd);
    border-radius: 0.375rem;
    font-size: 12px;
}

.cr-mirror-comparison--skipped {
    border-style: dashed;
    opacity: 0.5;
}

.cr-mirror-comparison--errored {
    border-color: var(--re-danger);
    background: var(--re-danger-soft);
}

.cr-mirror-comparison--hovered {
    outline: 2px solid var(--re-accent);
    outline-offset: 1px;
}

.cr-mirror-comparison__left { font-weight: 500; color: var(--re-fg); }
.cr-mirror-comparison__op { font-style: italic; color: var(--re-muted); }
.cr-mirror-comparison__right { color: var(--re-fg); }
.cr-mirror-comparison__resolved {
    margin-left: auto;
    font: 10.5px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--re-muted-2, var(--re-muted));
}

.cr-mirror-dot {
    display: inline-block;
    width: 0.625rem;
    height: 0.625rem;
    flex: none;
    border-radius: 999px;
}

.cr-mirror-dot--true { background: var(--re-accent); }
.cr-mirror-dot--false { border: 1px solid var(--re-bd-strong, var(--re-bd)); background: var(--re-surface); }
.cr-mirror-dot--pending { border: 1px dashed var(--re-bd-strong, var(--re-bd)); }
.cr-mirror-dot--unknown { background: var(--re-muted-2, var(--re-muted)); }
.cr-mirror-dot--errored {
    width: auto;
    height: auto;
    background: transparent;
}

.cr-mirror-dot--errored .cr-mirror-dot__glyph {
    width: 0.75rem;
    height: 0.75rem;
    color: var(--re-danger);
}

.cr-trace-list {
    background: var(--re-surface);
    border: 1px solid var(--re-bd);
    border-radius: 0.375rem;
}

.cr-trace-list__header {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: transparent;
    border: none;
    cursor: pointer;
    text-align: left;
}

.cr-trace-list__title {
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-muted);
}

.cr-trace-list__count {
    display: inline-flex;
    padding: 0 0.375rem;
    height: 1rem;
    border-radius: 999px;
    background: var(--re-surface-2);
    font: 10px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--re-muted-2, var(--re-muted));
}

.cr-trace-list__chevron {
    margin-left: auto;
    color: var(--re-muted);
    transition: transform 0.15s ease;
    transform: rotate(-90deg);
}

.cr-trace-list__chevron--open { transform: rotate(0); }

.cr-trace-list__steps {
    list-style: none;
    margin: 0;
    padding: 0.25rem 0.5rem 0.5rem;
    border-top: 1px solid var(--re-bd);
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.cr-trace-step {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem;
    border-radius: 0.375rem;
    font-size: 12px;
}

.cr-trace-step:hover { background: var(--re-row-hover, var(--re-surface-2)); }
.cr-trace-step--hovered { background: var(--re-accent-soft); }
.cr-trace-step--skipped { color: var(--re-muted-2, var(--re-muted)); }

.cr-trace-step__index {
    flex: none;
    margin-top: 0.125rem;
    font: 10px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--re-muted-2, var(--re-muted));
}

.cr-trace-step__body { flex: 1 1 auto; min-width: 0; }

.cr-trace-step__line {
    display: flex;
    align-items: center;
    gap: 0.375rem;
}

.cr-trace-step__label { font-weight: 500; color: var(--re-fg); }
.cr-trace-step__arrow { color: var(--re-muted-2, var(--re-muted)); }
.cr-trace-step__value {
    font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--re-fg);
}

.cr-trace-step--errored .cr-trace-step__value {
    color: var(--re-danger);
    font-weight: 600;
}

.cr-trace-step__error {
    margin-top: 0.25rem;
    padding: 0.25rem 0.5rem;
    border-left: 3px solid var(--re-danger);
    background: var(--re-danger-soft);
    font-size: 11.5px;
    color: var(--re-fg);
    border-radius: 0 0.25rem 0.25rem 0;
}

.cr-trace-step__error-prefix {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-danger);
}

.cr-trace-step__jump {
    flex: none;
    border: none;
    background: transparent;
    font-size: 10px;
    color: var(--re-accent-strong);
    cursor: pointer;
}

.cr-trace-step__jump:hover { text-decoration: underline; }

.cr-refused-panel {
    padding: 0.625rem 0.75rem;
    border-left: 3px solid var(--re-danger);
    background: var(--re-danger-soft);
    border-radius: 0 0.375rem 0.375rem 0;
}

.cr-refused-panel__head {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
}

.cr-refused-panel__glyph {
    flex: none;
    margin-top: 0.0625rem;
    width: 0.875rem;
    height: 0.875rem;
    color: var(--re-danger);
}

.cr-refused-panel__title {
    font-size: 12px;
    font-weight: 600;
    color: var(--re-danger);
}

.cr-refused-panel__subtitle {
    margin-top: 0.0625rem;
    font-size: 11.5px;
    color: var(--re-muted);
}

.cr-refused-panel__list {
    list-style: none;
    margin: 0.375rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.cr-refused-panel__item {
    padding: 0.375rem 0.5rem;
    background: var(--re-surface);
    border-radius: 0.375rem;
    font-size: 12px;
    color: var(--re-fg);
}

.cr-refused-panel__prefix {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--re-danger);
}

.cr-refused-panel__jump {
    margin-left: 0.5rem;
    border: none;
    background: transparent;
    font-size: 11px;
    color: var(--re-accent-strong);
    cursor: pointer;
}

.cr-refused-panel__jump:hover { text-decoration: underline; }

/* Dark-mode overrides for test-panel-only tokens. Shared semantic vars
   (--re-surface / --re-surface-2 / --re-bd / --re-accent / --re-danger / etc.) already pivot
   under [data-bs-theme="dark"] via threshold-tokens.css; the NOT-card colors
   need explicit dark values per feedback_rules_editor_dual_mode_tokens. */
[data-bs-theme="dark"] .cr-rules-editor .cr-mirror-not {
    border-color: rgba(244, 114, 182, 0.4);
    background: rgba(244, 114, 182, 0.08);
}

[data-bs-theme="dark"] .cr-rules-editor .cr-mirror-not__head {
    color: rgb(244, 114, 182);
}

/* Three-column collapse behaviour — under ~1280px the test panel stacks below
   the rail+pane row; under 960px the rail also stacks per the existing media
   rule above. */
@media (max-width: 1280px) {
    .cr-rules-editor-page {
        flex-wrap: wrap;
    }
    .cr-test-panel {
        flex: 0 0 100%;
        max-height: 50vh;
        border-left: none;
        border-top: 1px solid var(--re-bd);
    }
}

/* ---- Task 9c: destructive-choice modal + inline error banner ----------
   The modal markup renders inside the .cr-rules-editor wrapper so the scoped
   tokens below resolve (same scope-leak concern that bit 9f portal popovers
   — see feedback_portal_css_scope_leak.md). The banner sits inside
   .cr-rule-preview__body in the single-rule branch and adjacent to the empty
   state in the no-rule branch; both are inside the wrapper. */
.cr-modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1500;
}

.cr-modal {
    background: var(--re-surface);
    color: var(--re-fg);
    border: 1px solid var(--re-bd);
    border-radius: 8px;
    padding: 24px 28px;
    min-width: 360px;
    max-width: 520px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
}

.cr-modal__title {
    margin: 0 0 12px 0;
    font-size: 16px;
    font-weight: 600;
    line-height: 1.3;
}

.cr-modal__body {
    margin: 0 0 20px 0;
    font-size: 13px;
    color: var(--re-muted);
    line-height: 1.5;
}

.cr-modal__actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

.cr-modal__btn {
    padding: 6px 14px;
    font-size: 13px;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
}

.cr-modal__btn--primary {
    background: var(--re-accent);
    color: #fff;
    border: 1px solid var(--re-accent);
}

.cr-modal__btn--primary:hover {
    background: var(--re-accent-strong);
    border-color: var(--re-accent-strong);
}

.cr-modal__btn--destructive {
    background: var(--re-danger);
    border-color: var(--re-danger);
}

.cr-modal__btn--destructive:hover {
    background: var(--re-danger);
    border-color: var(--re-danger);
    filter: brightness(0.92);
}

.cr-modal__btn--secondary {
    background: transparent;
    color: var(--re-fg);
    border: 1px solid var(--re-bd);
}

.cr-modal__btn--secondary:hover {
    background: var(--re-surface-2);
}

.cr-error-banner {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    margin: 8px 0;
    background: var(--re-danger-soft);
    border: 1px solid var(--re-danger);
    border-radius: 4px;
    color: var(--re-fg);
    font-size: 13px;
}

.cr-error-banner__icon {
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--re-danger);
    color: #fff;
    border-radius: 50%;
    font-weight: 700;
    font-size: 11px;
    margin-top: 1px;
}

.cr-error-banner__content {
    flex: 1 1 auto;
    min-width: 0;
}

.cr-error-banner__title {
    font-weight: 600;
    margin-bottom: 2px;
}

.cr-error-banner__message {
    color: var(--re-muted);
}

.cr-error-banner__close {
    flex: 0 0 auto;
    background: transparent;
    border: none;
    color: var(--re-muted);
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 0 4px;
}

.cr-error-banner__close:hover {
    color: var(--re-fg);
}

/* ─────────────────────────────────────────────────────────────────────────
   Task 9d — Rule-level actions menu (overflow "..." in editor header)
   ───────────────────────────────────────────────────────────────────────── */

/* Right-aligned actions slot in the editor header's top row. Sits beside the
   target pill; margin-left:auto pushes it to the far right of the flex row. */
.cr-rules-editor .cr-rules-header__actions-menu {
    display: flex;
    align-items: center;
    margin-left: auto;
    flex-shrink: 0;
}

/* The "..." trigger button. Lives in the header (inside .cr-rules-editor), so
   --re-muted / --re-fg / --re-surface-2 resolve from the editor scope. */
.cr-rules-editor .cr-rule-actions__trigger {
    background: transparent;
    border: none;
    border-radius: 4px;
    padding: 4px 8px;
    font-size: 14px;
    line-height: 1;
    color: var(--re-muted);
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
}

.cr-rules-editor .cr-rule-actions__trigger:hover,
.cr-rules-editor .cr-rule-actions__trigger:focus-visible {
    background: var(--re-surface-2);
    color: var(--re-fg);
    outline: none;
}

/* Menu rendered inside the portal-mounted .cr-popover-shell. Override the shell's
   default min-width / max-height — an actions menu with one or two items
   shouldn't claim a 240px-wide slot. The shell's bg / border / shadow stay. */
.cr-popover-shell .cr-rule-actions__menu {
    list-style: none;
    margin: 0;
    padding: 4px 0;
    min-width: 160px;
}
/* Promote the actions-menu shell to the smaller min-width without affecting other
   pickers' shells. The shell's own min-width: 240px wins by default; this overrides
   when the shell hosts an actions menu. */
.cr-popover-shell:has(.cr-rule-actions__menu) {
    min-width: 0;
}

.cr-popover-shell .cr-rule-actions__item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 6px 12px;
    background: transparent;
    border: none;
    text-align: left;
    font-size: 13px;
    color: var(--re-fg);
    cursor: pointer;
}

.cr-popover-shell .cr-rule-actions__item:hover,
.cr-popover-shell .cr-rule-actions__item:focus-visible {
    background: var(--re-row-hover);
    outline: none;
}

.cr-popover-shell .cr-rule-actions__item--re-danger {
    color: var(--re-danger);
}

.cr-popover-shell .cr-rule-actions__item--re-danger:hover,
.cr-popover-shell .cr-rule-actions__item--re-danger:focus-visible {
    background: var(--re-danger-soft);
}

.cr-popover-shell .cr-rule-actions__item .fa-light {
    width: 14px;
    text-align: center;
    color: inherit;
}

/* ─────────────────────────────────────────────────────────────────────────
   Task 9d — Can't-delete modal references list
   ───────────────────────────────────────────────────────────────────────── */

/* Lives inside the .cr-modal panel, which lives inside .cr-rules-editor — so
   --re-fg / --re-muted resolve directly. max-height + overflow-y so a rule pinned by
   50 references doesn't push the modal beyond the viewport. */
.cr-rules-editor .cr-cant-delete__references {
    list-style: disc;
    margin: 0;
    padding-left: 24px;
    max-height: 200px;
    overflow-y: auto;
    font-size: 13px;
}

.cr-rules-editor .cr-cant-delete__references li {
    margin: 4px 0;
    color: var(--re-fg);
}

.cr-rules-editor .cr-cant-delete__ref-type {
    color: var(--re-muted);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-right: 6px;
}

.cr-rules-editor .cr-cant-delete__ref-name {
    font-weight: 500;
}

.cr-rules-editor .cr-cant-delete__ref-location {
    color: var(--re-muted);
    margin-left: 4px;
}
