/* rustango-cms admin chrome — Material 3 design system.
 *
 * Single source of truth for every /cms-admin/* screen + the
 * stand-alone auth flow (login / password-reset).
 *
 * Token tier (`--md-sys-*`): direct port of the Tailwind config in
 * the project-root `./design` reference. Component tier reads through
 * the alias layer (`--accent`, `--fg`, `--bg`, ...) so the existing
 * 77 class names in this file keep working without per-template
 * rewrites. Tenant-theme overrides emitted by `theme::emit_css()`
 * cascade ABOVE this file (loaded inline in `_base.html`), so the
 * resolution order is: built-in MD3 defaults → tenant theme overrides
 * → @media (prefers-color-scheme: dark).
 *
 * Host apps override by dropping `static/cms_admin/cms.css` in their
 * project root — the admin's static handler returns it instead of
 * this bundled file.
 *
 * Namespacing convention (#314):
 *   - The admin shell carries `.rcms-admin` on <body> (both _base.html
 *     and _auth_layout.html). It is the single scope anchor for the
 *     framework's chrome.
 *   - New component classes use the `rcms-` prefix. The legacy `cms-`
 *     classes are fully folded into `rcms-`; folding the remaining
 *     unprefixed/generic names (`.rcms-btn`, `.rcms-card`, `.rcms-field`, …) into the
 *     prefix is the #293-coordinated rewrite, in progress. Avoid bare
 *     generic names in new markup.
 *   - Bare element rules (typography, form controls) that would restyle
 *     user-authored HTML rendered INLINE in the admin (rich-text /
 *     stream / block previews) must be `@scope`d to `.rcms-admin` with a
 *     lower boundary at the user-content wrappers (`.rcms-block-*`,
 *     `.rcms-stream-block`, `.rcms-block-picker-tile-preview`, `.rcms-display-chip-value`)
 *     so admin chrome never leaks into stored content. The page-preview
 *     pane is already iframe-isolated and needs no scoping.
 */

/* ============================================================
 * 1. MD3 design tokens
 * ============================================================ */

:root {
    /* Color roles — light mode. Calmer indigo than the design's raw
       #1b32e7 — the original read too saturated against the light
       surface under hands-on review. #6366f1 (Tailwind indigo-500)
       is the modern "mild SaaS indigo" — Stripe / Linear / Notion
       sit in the same band. Container / fixed tiers shift up in
       step so the full M3 role family stays internally consistent. */
    --md-sys-color-primary: #6366f1;
    --md-sys-color-on-primary: #ffffff;
    --md-sys-color-primary-container: #818cf8;
    --md-sys-color-on-primary-container: #1e1b4b;
    --md-sys-color-primary-fixed: #eef2ff;
    --md-sys-color-primary-fixed-dim: #c7d2fe;
    --md-sys-color-on-primary-fixed: #312e81;
    --md-sys-color-on-primary-fixed-variant: #4338ca;

    --md-sys-color-secondary: #5f5e5e;
    --md-sys-color-on-secondary: #ffffff;
    --md-sys-color-secondary-container: #e5e2e1;
    --md-sys-color-on-secondary-container: #656464;

    --md-sys-color-tertiary: #942f00;
    --md-sys-color-on-tertiary: #ffffff;
    --md-sys-color-tertiary-container: #bd3e00;
    --md-sys-color-on-tertiary-container: #ffe5dd;
    --md-sys-color-tertiary-fixed: #ffdbcf;
    --md-sys-color-tertiary-fixed-dim: #ffb59c;

    --md-sys-color-error: #ba1a1a;
    --md-sys-color-on-error: #ffffff;
    --md-sys-color-error-container: #ffdad6;
    --md-sys-color-on-error-container: #93000a;

    --md-sys-color-success: #22a06b;
    --md-sys-color-on-success: #ffffff;
    --md-sys-color-success-container: #d1fae5;
    --md-sys-color-on-success-container: #065f46;

    --md-sys-color-warning: #d97706;
    --md-sys-color-on-warning: #ffffff;
    --md-sys-color-warning-container: #fef3c7;
    --md-sys-color-on-warning-container: #92400e;

    --md-sys-color-surface: #f8f9fa;
    --md-sys-color-surface-dim: #d9dadb;
    --md-sys-color-surface-bright: #f8f9fa;
    --md-sys-color-surface-container-lowest: #ffffff;
    --md-sys-color-surface-container-low: #f3f4f5;
    --md-sys-color-surface-container: #edeeef;
    --md-sys-color-surface-container-high: #e7e8e9;
    --md-sys-color-surface-container-highest: #e1e3e4;
    --md-sys-color-surface-variant: #e1e3e4;
    --md-sys-color-on-surface: #191c1d;
    --md-sys-color-on-surface-variant: #444656;
    --md-sys-color-surface-tint: #2f44f4;

    --md-sys-color-outline: #757688;
    --md-sys-color-outline-variant: #c5c5d9;

    --md-sys-color-inverse-surface: #2e3132;
    --md-sys-color-inverse-on-surface: #f0f1f2;
    --md-sys-color-inverse-primary: #bdc2ff;

    /* Shape — corner radii. */
    --md-sys-radius-sm: 2px;
    --md-sys-radius-md: 4px;
    --md-sys-radius-lg: 8px;
    --md-sys-radius-xl: 12px;
    --md-sys-radius-full: 9999px;

    /* Elevation — three tiers using paired shadows (one tight + one
       diffuse) so depth reads on light surfaces without becoming
       muddy. Tuned warmer + slightly stronger than the design's raw
       0.03-alpha — under hands-on review the chrome wanted more
       definition between surface tiers. */
    --md-sys-elevation-1: 0px 1px 3px rgba(15, 23, 42, 0.08),
                          0px 1px 2px rgba(15, 23, 42, 0.04);
    --md-sys-elevation-2: 0px 4px 12px rgba(15, 23, 42, 0.08),
                          0px 2px 4px rgba(15, 23, 42, 0.04);
    --md-sys-elevation-3: 0px 12px 28px rgba(15, 23, 42, 0.10),
                          0px 4px 8px rgba(15, 23, 42, 0.06);
    /* Tinted shadow for the primary filled button — a soft halo of
       the primary hue lifts it off the surface without using a
       darker grey shadow (which always reads heavy on filled M3). */
    --md-sys-shadow-primary: 0px 4px 14px color-mix(in srgb, var(--md-sys-color-primary) 22%, transparent);

    /* Type — Hanken Grotesk for UI, Literata for editorial body. */
    --md-sys-font-ui: 'Hanken Grotesk', 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
    --md-sys-font-editor: 'Literata', Georgia, 'Times New Roman', serif;

    --md-sys-typescale-display-lg: 700 48px/60px var(--md-sys-font-ui);
    --md-sys-typescale-ui-header: 600 18px/24px var(--md-sys-font-ui);
    --md-sys-typescale-ui-body: 400 14px/20px var(--md-sys-font-ui);
    --md-sys-typescale-ui-button: 500 14px/20px var(--md-sys-font-ui);
    --md-sys-typescale-ui-label: 600 12px/16px var(--md-sys-font-ui);
    --md-sys-typescale-editor-text: 400 16px/1.7 var(--md-sys-font-editor);

    /* ----------------------------------------------------------
     * 1a. Alias layer — existing chrome variables map onto MD3
     *     tokens. The 77 existing class names below this block
     *     keep working without per-template rewrites.
     * ---------------------------------------------------------- */
    --fg: var(--md-sys-color-on-surface);
    --muted: var(--md-sys-color-on-surface-variant);
    --muted-2: var(--md-sys-color-outline);
    --accent: var(--md-sys-color-primary);
    --accent-hover: var(--md-sys-color-primary-container);
    --accent-soft: var(--md-sys-color-primary-fixed);
    --accent-soft-2: var(--md-sys-color-primary-fixed-dim);
    /* #293 phase 1 — the `--violet*` alias trio was an early
       shorthand that no template or stylesheet ever read; removed.
       Templates that need the tertiary swatch reference
       `--md-sys-color-tertiary*` directly (the styleguide + the
       brand-image card both already do). */
    --border: var(--md-sys-color-outline-variant);
    --border-strong: var(--md-sys-color-outline);
    --bg: var(--md-sys-color-surface);
    --bg-elevated: var(--md-sys-color-surface-container-lowest);
    --danger: var(--md-sys-color-error);
    --danger-soft: var(--md-sys-color-error-container);
    --danger-hover: var(--md-sys-color-on-error-container);
    --success: var(--md-sys-color-success);
    --success-soft: var(--md-sys-color-success-container);
    --success-hover: var(--md-sys-color-on-success-container);
    --warning: var(--md-sys-color-warning);
    --warning-soft: var(--md-sys-color-warning-container);
    --warning-hover: var(--md-sys-color-on-warning-container);

    /* Sidebar palette — M3 nav rail. Light surface with on-surface
       text; primary becomes the *active* color, not the background.
       Matches the design file's `bg-surface-container-low` aside. */
    --sidebar-bg: var(--md-sys-color-surface-container-low);
    --sidebar-fg: var(--md-sys-color-on-surface);
    --sidebar-fg-muted: var(--md-sys-color-on-surface-variant);
    --sidebar-active: var(--md-sys-color-primary);
    --sidebar-active-bg: var(--md-sys-color-primary-fixed);
    --sidebar-hover-bg: var(--md-sys-color-surface-container);
    --sidebar-border: var(--md-sys-color-outline-variant);

    --font-body: var(--md-sys-font-ui);
    --font-heading: var(--md-sys-font-ui);
    --radius-md: var(--md-sys-radius-md);
}

/* Dark token block applies in TWO contexts:
 *   1. OS dark mode (`@media (prefers-color-scheme: dark)`), unless
 *      the user explicitly chose Light via the toggle (`[data-theme="light"]`).
 *   2. Explicit Dark choice via the toggle (`[data-theme="dark"]`),
 *      regardless of OS pref.
 * CSS can't combine an `@media` query with another selector via comma,
 * so the body is duplicated below. Keep them in sync. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        /* Dark mode owns its own identity — violet primary + emerald
           accent on charcoal-with-violet-tint surfaces. Distinct
           from a mechanical inversion of the light palette. */

        /* Primary — violet-400 family. */
        --md-sys-color-primary: #a78bfa;
        --md-sys-color-on-primary: #1e1b4b;
        --md-sys-color-primary-container: #7c3aed;
        --md-sys-color-on-primary-container: #ede9fe;
        --md-sys-color-primary-fixed: #312e81;
        --md-sys-color-primary-fixed-dim: #5b21b6;
        --md-sys-color-on-primary-fixed: #ede9fe;
        --md-sys-color-on-primary-fixed-variant: #c4b5fd;

        /* Tertiary becomes the emerald accent — used for success
           states + focus highlights + the "right rail" cues. */
        --md-sys-color-tertiary: #34d399;
        --md-sys-color-on-tertiary: #064e3b;
        --md-sys-color-tertiary-container: #047857;
        --md-sys-color-on-tertiary-container: #d1fae5;
        --md-sys-color-tertiary-fixed: #064e3b;
        --md-sys-color-tertiary-fixed-dim: #065f46;

        /* Success — emerald, matching the tertiary accent so the two
           reinforce each other in dark mode. */
        --md-sys-color-success: #34d399;
        --md-sys-color-on-success: #064e3b;
        --md-sys-color-success-container: #047857;
        --md-sys-color-on-success-container: #d1fae5;

        /* Surface containers — charcoal with a faint violet tint
           so the chrome reads warm against the violet primary. */
        --md-sys-color-surface: #141220;
        --md-sys-color-surface-dim: #0e0c18;
        --md-sys-color-surface-bright: #322f4a;
        --md-sys-color-surface-container-lowest: #0a0814;
        --md-sys-color-surface-container-low: #1e1b2e;
        --md-sys-color-surface-container: #232036;
        --md-sys-color-surface-container-high: #2a2740;
        --md-sys-color-surface-container-highest: #322f4a;
        --md-sys-color-surface-variant: #2a2740;
        --md-sys-color-on-surface: #e7e5ef;
        --md-sys-color-on-surface-variant: #c4b5d9;

        --md-sys-color-outline: #8b85a8;
        --md-sys-color-outline-variant: #322f4a;

        --md-sys-color-error: #ffb4ab;
        --md-sys-color-on-error: #690005;
        --md-sys-color-error-container: #93000a;
        --md-sys-color-on-error-container: #ffdad6;

        /* Re-tune the sidebar palette for dark mode so the active
           pill picks up violet against the violet-tinted charcoal. */
        --sidebar-bg: var(--md-sys-color-surface-container-low);
        --sidebar-fg: var(--md-sys-color-on-surface);
        --sidebar-fg-muted: var(--md-sys-color-on-surface-variant);
        --sidebar-active: var(--md-sys-color-primary);
        --sidebar-active-bg: var(--md-sys-color-primary-fixed-dim);
        --sidebar-hover-bg: var(--md-sys-color-surface-container);
        --sidebar-border: var(--md-sys-color-outline-variant);
    }
}

/* Explicit dark — mirrors the @media block above. Both bodies must
 * stay in sync (CSS can't deduplicate across an @media boundary). */
:root[data-theme="dark"] {
    color-scheme: dark;

    /* Primary — violet-400 family. */
    --md-sys-color-primary: #a78bfa;
    --md-sys-color-on-primary: #1e1b4b;
    --md-sys-color-primary-container: #7c3aed;
    --md-sys-color-on-primary-container: #ede9fe;
    --md-sys-color-primary-fixed: #312e81;
    --md-sys-color-primary-fixed-dim: #5b21b6;
    --md-sys-color-on-primary-fixed: #ede9fe;
    --md-sys-color-on-primary-fixed-variant: #c4b5fd;

    /* Tertiary becomes the emerald accent. */
    --md-sys-color-tertiary: #34d399;
    --md-sys-color-on-tertiary: #064e3b;
    --md-sys-color-tertiary-container: #047857;
    --md-sys-color-on-tertiary-container: #d1fae5;
    --md-sys-color-tertiary-fixed: #064e3b;
    --md-sys-color-tertiary-fixed-dim: #065f46;

    /* Success — emerald, matching the tertiary accent. */
    --md-sys-color-success: #34d399;
    --md-sys-color-on-success: #064e3b;
    --md-sys-color-success-container: #047857;
    --md-sys-color-on-success-container: #d1fae5;

    /* Surface containers — charcoal with a faint violet tint. */
    --md-sys-color-surface: #141220;
    --md-sys-color-surface-dim: #0e0c18;
    --md-sys-color-surface-bright: #322f4a;
    --md-sys-color-surface-container-lowest: #0a0814;
    --md-sys-color-surface-container-low: #1e1b2e;
    --md-sys-color-surface-container: #232036;
    --md-sys-color-surface-container-high: #2a2740;
    --md-sys-color-surface-container-highest: #322f4a;
    --md-sys-color-surface-variant: #2a2740;
    --md-sys-color-on-surface: #e7e5ef;
    --md-sys-color-on-surface-variant: #c4b5d9;

    --md-sys-color-outline: #8b85a8;
    --md-sys-color-outline-variant: #322f4a;

    --md-sys-color-error: #ffb4ab;
    --md-sys-color-on-error: #690005;
    --md-sys-color-error-container: #93000a;
    --md-sys-color-on-error-container: #ffdad6;

    --sidebar-bg: var(--md-sys-color-surface-container-low);
    --sidebar-fg: var(--md-sys-color-on-surface);
    --sidebar-fg-muted: var(--md-sys-color-on-surface-variant);
    --sidebar-active: var(--md-sys-color-primary);
    --sidebar-active-bg: var(--md-sys-color-primary-fixed-dim);
    --sidebar-hover-bg: var(--md-sys-color-surface-container);
    --sidebar-border: var(--md-sys-color-outline-variant);
}

/* ============================================================
 * 1b. Accessibility prefs (#14) — driven by `[data-text-scale]`
 *     and `[data-high-contrast]` on <html>. Toggle UI lives in the
 *     sidebar footer; preferences persist in localStorage.
 * ============================================================ */

/* Text scale — applied as root font-size %, which `em`-based units
   in cms.css and `rem`-based units in editor templates both pick up.
   Default (M) is omitted: it's 100%. */
:root[data-text-scale="s"]  { font-size: 87.5%; }
:root[data-text-scale="l"]  { font-size: 112.5%; }
:root[data-text-scale="xl"] { font-size: 125%; }

/* High-contrast — bumps border weight + opacity, swaps the focus
   ring color to maximum contrast, darkens muted text. */
:root[data-high-contrast] {
    --md-sys-color-outline: #000000;
    --md-sys-color-outline-variant: #4a4a4a;
    --md-sys-color-on-surface-variant: #1a1a1a;
}
:root[data-high-contrast] .rcms-card,
:root[data-high-contrast] table.rcms-data,
:root[data-high-contrast] .rcms-field input,
:root[data-high-contrast] .rcms-field select,
:root[data-high-contrast] .rcms-field textarea,
:root[data-high-contrast] .rcms-btn-outlined,
:root[data-high-contrast] .rcms-sidebar-logout,
:root[data-high-contrast] .rcms-mode-toggle,
:root[data-high-contrast] .rcms-search-input,
:root[data-high-contrast] select:not(.rcms-field select) {
    border-width: 2px;
}
:root[data-high-contrast] :focus,
:root[data-high-contrast] :focus-visible {
    /* #336 — dual-ring focus that reads on ANY background. Outer ring is
       the forced max-contrast neutral (--md-sys-color-outline = #000 in
       light / #fff in dark) instead of `primary`, which blended into
       filled / active primary surfaces. The inner --md-sys-color-surface
       band (≈white in light / dark in dark — the inverse tone) fills the
       outline-offset gap, so on the dark sidebar (where the black outline
       has little contrast) the light band still reads, and vice-versa.
       `!important` keeps a control's decorative elevation shadow from
       suppressing the ring. */
    outline: 3px solid var(--md-sys-color-outline);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--md-sys-color-surface) !important;
}
:root[data-high-contrast][data-theme="dark"] {
    --md-sys-color-outline: #ffffff;
    --md-sys-color-outline-variant: #c4b5d9;
    --md-sys-color-on-surface-variant: #f5f3ff;
}
/* Sidebar links are stacked ~2px apart, so an *outset* focus ring (offset +
   the high-contrast dual ring) spills onto the items above/below and reads as
   overlapping menu rows. Inset the ring so it stays inside each link. */
.rcms-sidebar-link:focus-visible {
    outline-offset: -2px;
}
:root[data-high-contrast] .rcms-sidebar-link:focus,
:root[data-high-contrast] .rcms-sidebar-link:focus-visible {
    outline-offset: -3px;
    box-shadow: none !important;
}

/* ============================================================
 * 2. Reset + global type
 * ============================================================ */

/* #314 — the universal box-sizing reset and the body chrome are
   qualified on the admin root class so cms.css can never restyle a
   host page that happens to load it. */
body.rcms-admin, .rcms-admin * { box-sizing: border-box; }
html:has(> body.rcms-admin), body.rcms-admin { height: 100%; }
body.rcms-admin {
    font: var(--md-sys-typescale-ui-body);
    /* Override the shorthand's literal 14px with rem so the
       per-user text-scale toggle (#14) can scale body content via
       :root[data-text-scale]'s font-size %. Components that use the
       typescale shorthand directly (buttons, chips) intentionally
       stay at fixed px so the chrome density doesn't drift. */
    font-size: 0.875rem;
    line-height: 1.43;
    color: var(--fg);
    margin: 0;
    background: var(--bg);
    display: flex;
    height: 100vh;
    /* Lock body height so the sidebar can be a fixed-height flex
       column. Scrolling happens inside `.rcms-app-main` (the right-hand
       column), which keeps the sidebar perfectly stationary even on
       long edit pages. */
    overflow: hidden;
}
/* #314 — scope the heading font to the admin shell, stopping at inline
   user-content wrappers so a user's stored <h2> in a rich-text / stream /
   block preview keeps its own (public/UA) font instead of admin chrome.
   The richtext preview deliberately styles its own content and is left
   in-scope. */
@scope (.rcms-admin) to (.rcms-block-paragraph, .rcms-block-rich-text, .rcms-block-raw-html, .rcms-block-snippet-chooser, .rcms-block-picker-tile-preview, .rcms-stream-block, .rcms-display-chip-value) {
    h1, h2, h3, h4, h5, h6 { font-family: var(--font-heading); }
}

/* ============================================================
 * 3. Material Symbols icons
 * ============================================================ */

.material-symbols-rounded {
    font-family: 'Material Symbols Rounded';
    font-weight: normal;
    font-style: normal;
    font-size: 20px;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    display: inline-block;
    white-space: nowrap;
    word-wrap: normal;
    direction: ltr;
    -webkit-font-feature-settings: 'liga';
    -webkit-font-smoothing: antialiased;
    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
    vertical-align: -4px;
}
.material-symbols-rounded.filled {
    font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 24;
}
.material-symbols-rounded.sm { font-size: 18px; vertical-align: -3px; }
.material-symbols-rounded.lg { font-size: 24px; vertical-align: -6px; }

/* ============================================================
 * 4. Sidebar
 * ============================================================ */

.rcms-sidebar {
    width: 260px;
    background: var(--sidebar-bg);
    color: var(--sidebar-fg);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
    flex-shrink: 0;
    /* Soft edge shadow instead of a hard right border — reads as
       elevation against the main content. */
    box-shadow: 4px 0 16px rgba(15, 23, 42, 0.04),
                1px 0 0 var(--md-sys-color-outline-variant);
    z-index: 1;
    /* #271 — smooth width transition for the collapse toggle. */
    transition: width 0.18s ease;
}

/* #272 — Collapsible sidebar group (e.g. Reports). Native
   `<details>` powers the open/closed toggle so the chevron click,
   keyboard activation, and accessibility tree all work without
   custom JS. `.rcms-sidebar-group-summary` is the parent row;
   `.rcms-sidebar-group-items` wraps the nested links and animates
   expand/collapse via a grid-template-rows trick (rows track
   interpolates from 0fr to 1fr, so the wrapper's height tweens
   with the content). */
.rcms-sidebar-group {
    display: block;
}
.rcms-sidebar-group-summary {
    /* Hide the native disclosure triangle — we draw our own chevron. */
    list-style: none;
    cursor: pointer;
    user-select: none;
}
.rcms-sidebar-group-summary::-webkit-details-marker { display: none; }
.rcms-sidebar-group-label {
    /* Push the chevron to the far right of the summary row. */
    flex: 1;
}
.rcms-sidebar-group-chevron {
    font-size: 18px !important;
    transition: transform 0.18s ease;
    opacity: 0.7;
}
.rcms-sidebar-group[open] > .rcms-sidebar-group-summary .rcms-sidebar-group-chevron {
    transform: rotate(180deg);
}
.rcms-sidebar-group.active > .rcms-sidebar-group-summary {
    /* Subtle tint on the parent row when any child is the current
       page — reinforces "you are here, inside this group". Lighter
       than the full `.active` treatment so the actual current item
       still reads as the primary highlight. */
    color: var(--md-sys-color-on-surface);
}
/* Nested links sit a touch deeper than top-level entries so the
   group reads as nested in scan. */
.rcms-sidebar-group-items .rcms-sidebar-link {
    padding-left: 36px;
    font-size: 13px;
}
.rcms-sidebar-group-items .rcms-sidebar-link .material-symbols-rounded {
    font-size: 18px;
}
/* Smooth expand/collapse. Override the user-agent stylesheet that
   slot-hides <details>'s non-summary children so they stay in
   layout for the JS-driven height animation in cms-ux.js. CSS
   alone can't transition between `0` and `auto`, so the JS
   intercepts the <summary> click, sets inline `height: <px>` to
   the measured scrollHeight, lets the transition run, then
   clears the inline style at transitionend so the [open] rule's
   `height: auto` takes back over (and the group grows naturally
   if items are added/removed dynamically). */
.rcms-sidebar-group > *:not(summary) {
    display: block;
}
.rcms-sidebar-group-items {
    overflow: hidden;
    height: 0;
    opacity: 0;
    transition: height 0.22s cubic-bezier(0.2, 0, 0, 1),
                opacity 0.16s ease;
}
.rcms-sidebar-group[open] > .rcms-sidebar-group-items {
    height: auto;
    opacity: 1;
}

/* Collapsed-rail tweaks: drop the chevron + label so the group's
   summary looks like a regular icon entry; force the items rail
   open so nested icons stay visible without requiring a click. */
html[data-sidebar-collapsed] .rcms-sidebar-group > .rcms-sidebar-group-items {
    height: auto;
    opacity: 1;
}
html[data-sidebar-collapsed] .rcms-sidebar-group-chevron,
html[data-sidebar-collapsed] .rcms-sidebar-group-label {
    display: none;
}
html[data-sidebar-collapsed] .rcms-sidebar-group-items .rcms-sidebar-link {
    padding-left: 0;
}

.rcms-sidebar-brand {
    padding: 24px 24px 20px;
    display: flex;
    align-items: center;
    gap: 12px;
}
.rcms-sidebar-brand::before {
    content: "";
    width: 36px;
    height: 36px;
    flex: 0 0 36px;
    border-radius: var(--md-sys-radius-lg);
    background:
        linear-gradient(135deg, var(--md-sys-color-primary), var(--md-sys-color-primary-container));
    box-shadow: 0 4px 12px color-mix(in srgb, var(--md-sys-color-primary) 30%, transparent);
}
/* An uploaded logo IS the brand mark — drop the decorative gradient square. */
.rcms-sidebar-brand.has-logo::before {
    display: none;
}
.rcms-sidebar-brand h1 {
    font: var(--md-sys-typescale-ui-header);
    font-size: 15px;
    margin: 0 0 2px;
    color: var(--md-sys-color-on-surface);
}
.rcms-sidebar-brand .rcms-tenant {
    font: var(--md-sys-typescale-ui-label);
    text-transform: none;
    letter-spacing: 0;
    color: var(--sidebar-fg-muted);
}
.rcms-sidebar-nav {
    flex: 1;
    padding: 8px 12px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.rcms-sidebar-section {
    font: var(--md-sys-typescale-ui-label);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--sidebar-fg-muted);
    padding: 18px 12px 6px;
}
.rcms-sidebar-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    color: var(--sidebar-fg-muted);
    text-decoration: none;
    font: var(--md-sys-typescale-ui-button);
    border-radius: var(--md-sys-radius-full);
    transition: background 0.12s, color 0.12s;
}
.rcms-sidebar-link .material-symbols-rounded {
    font-size: 20px;
    color: var(--sidebar-fg-muted);
    transition: color 0.12s;
}
.rcms-sidebar-link:hover {
    background: var(--sidebar-hover-bg);
    color: var(--md-sys-color-on-surface);
}
.rcms-sidebar-link:hover .material-symbols-rounded {
    color: var(--md-sys-color-on-surface);
}
.rcms-sidebar-link.active {
    background: var(--sidebar-active-bg);
    color: var(--md-sys-color-on-primary-fixed);
    font-weight: 600;
}
.rcms-sidebar-link.active .material-symbols-rounded {
    color: var(--md-sys-color-on-primary-fixed);
}
.rcms-sidebar-link .rcms-icon {
    display: inline-block;
    width: 16px;
    font-size: 14px;
    text-align: center;
}
.rcms-sidebar-footer {
    padding: 16px 16px 20px;
    border-top: 1px solid var(--sidebar-border);
    font: var(--md-sys-typescale-ui-label);
    text-transform: none;
    letter-spacing: 0;
    color: var(--sidebar-fg-muted);
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.rcms-sidebar-footer a {
    color: var(--sidebar-fg-muted);
    text-decoration: none;
}
.rcms-sidebar-footer a:hover { color: var(--md-sys-color-primary); }
/* Light / Dark / System segmented toggle — three Material-symbol
   icons in a single pill, with the active mode highlighted.
   `.rcms-text-scale-toggle` (#14) shares the same shape; the only
   difference is the button glyph (A at progressively larger sizes). */
.rcms-mode-toggle,
.rcms-text-scale-toggle {
    display: inline-flex;
    background: var(--md-sys-color-surface-container);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-full);
    padding: 2px;
    gap: 2px;
    /* Let the bar size to its contents so the pills inside get
       breathing room. `align-self: stretch` previously pinned each
       segment to equal-share which cramped the longer labels. */
    flex-wrap: wrap;
}
.rcms-mode-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 32px;
    padding: 0 14px;
    background: transparent;
    color: var(--md-sys-color-on-surface-variant);
    border: 0;
    border-radius: var(--md-sys-radius-full);
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
    white-space: nowrap;
    font: var(--md-sys-typescale-ui-button);
}
.rcms-mode-toggle-btn:hover {
    color: var(--md-sys-color-on-surface);
}
.rcms-mode-toggle-btn.active,
.rcms-mode-toggle-btn[aria-checked="true"] {
    background: var(--md-sys-color-primary);
    color: var(--md-sys-color-on-primary);
}
.rcms-mode-toggle-btn .material-symbols-rounded {
    font-size: 16px;
    vertical-align: 0;
}

.rcms-sidebar-logout {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 14px;
    background: transparent;
    color: var(--md-sys-color-on-surface-variant);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-full);
    font: var(--md-sys-typescale-ui-button);
    text-transform: none;
    letter-spacing: 0;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.rcms-sidebar-logout:hover {
    background: var(--md-sys-color-error-container);
    color: var(--md-sys-color-on-error-container);
    border-color: var(--md-sys-color-error);
}
.rcms-sidebar-logout .material-symbols-rounded { font-size: 16px; }
/* #337 — equalize the stacked footer buttons. "Sign out" is a <button>
   inside a <form>, so it shrinks to content width while "Preferences"
   (a bare <a>) stretches to the footer — they read as misaligned. In the
   expanded sidebar, stretch both to full width and left-align icon+label
   so they line up as a stack. Scoped with :not([data-sidebar-collapsed])
   so the collapsed icon-rail (centered, icon-only) is left untouched. */
html:not([data-sidebar-collapsed]) .rcms-sidebar-footer .rcms-sidebar-logout {
    width: 100%;
    justify-content: flex-start;
}

/* #271 — sidebar collapse / icons-only rail. Driven by the
   `data-sidebar-collapsed` attribute on <html> (set BEFORE first
   paint by the no-flash <script> in _base.html, then toggled by
   the top-of-sidebar button via cms-ux.js). Native `title`
   tooltips on each link surface the label on hover in collapsed
   mode. The button sits inside `.rcms-sidebar-brand` and is pushed to
   the right edge via `margin-left: auto` so the brand name +
   tenant pill stay anchored to the left. */
.rcms-sidebar-collapse-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    margin-left: auto;
    background: transparent;
    color: var(--sidebar-fg-muted);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-full);
    cursor: pointer;
    transition: background 0.12s, color 0.12s, transform 0.18s ease;
}
.rcms-sidebar-collapse-toggle:hover {
    background: var(--sidebar-hover-bg);
    color: var(--md-sys-color-on-surface);
}
.rcms-sidebar-collapse-toggle .material-symbols-rounded { font-size: 20px; }
html[data-sidebar-collapsed] .rcms-sidebar-collapse-toggle .material-symbols-rounded {
    transform: rotate(180deg);
}

html[data-sidebar-collapsed] .rcms-sidebar {
    width: 64px;
}
/* Hide text content on the icon rail. `font-size: 0` collapses the
   label text-node next to each icon; the icon itself restores its
   own font-size so it stays visible. */
html[data-sidebar-collapsed] .rcms-sidebar-link,
html[data-sidebar-collapsed] .rcms-sidebar-logout {
    font-size: 0;
    gap: 0;
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
}
html[data-sidebar-collapsed] .rcms-sidebar-link .material-symbols-rounded {
    font-size: 20px;
}
html[data-sidebar-collapsed] .rcms-sidebar-link .material-symbols-rounded.sm,
html[data-sidebar-collapsed] .rcms-sidebar-logout .material-symbols-rounded {
    font-size: 18px;
}
/* Section headings + brand text + footer text-only items all hide
   in collapsed mode — the rail is icons-only. */
html[data-sidebar-collapsed] .rcms-sidebar-section,
html[data-sidebar-collapsed] .rcms-sidebar-brand h1,
html[data-sidebar-collapsed] .rcms-sidebar-brand .rcms-tenant,
html[data-sidebar-collapsed] .rcms-sidebar-footer > a[href^="/admin/"],
html[data-sidebar-collapsed] .rcms-sidebar-footer [data-user-tz] {
    display: none;
}
html[data-sidebar-collapsed] .rcms-sidebar-brand {
    justify-content: center;
    padding: 16px 0;
}
html[data-sidebar-collapsed] .rcms-sidebar-brand-logo img {
    max-height: 32px;
}
/* Hide the brand gradient square (`::before`) and any logo
   image in collapsed mode — the toggle button is the only thing
   that fits in the 64 px rail, and its rotated chevron doubles
   as the "expand me" affordance. */
html[data-sidebar-collapsed] .rcms-sidebar-brand::before,
html[data-sidebar-collapsed] .rcms-sidebar-brand-logo {
    display: none;
}
html[data-sidebar-collapsed] .rcms-sidebar-collapse-toggle {
    margin-left: 0;
}
html[data-sidebar-collapsed] .rcms-sidebar-footer {
    padding: 12px 8px 16px;
    align-items: center;
}
html[data-sidebar-collapsed] .rcms-sidebar-profile {
    /* Drop the username text — keep the avatar only. */
    gap: 0 !important;
}
html[data-sidebar-collapsed] .rcms-sidebar-profile > span:not([style*="border-radius"]) {
    display: none;
}
/* Drop the "logout" form wrapper's margin so its button stays
   centered in the rail. */
html[data-sidebar-collapsed] .rcms-sidebar-footer form {
    width: 100%;
    display: flex;
    justify-content: center;
}

/* ============================================================
 * 5. Top chrome — topbar, action bar, breadcrumbs, chips
 * ============================================================ */

.rcms-app-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    /* The only scroll container — body is height-locked above,
       sidebar is its sibling at 100vh, so the user scrolls *here*.
       Sticky `.rcms-app-topbar` / `.rcms-action-bar` stay pinned at the top. */
    overflow-y: auto;
    height: 100vh;
}
.rcms-app-topbar {
    background: var(--bg-elevated);
    border-bottom: 1px solid var(--border);
    padding: 6px 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    min-height: 48px;
    position: sticky;
    top: 0;
    z-index: 12;
}
.rcms-app-topbar-left {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 0;
}
.rcms-app-topbar-heading {
    font: var(--md-sys-typescale-ui-header);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    flex: 1 1 auto;
    /* Stay on one line — a long page title must truncate, not wrap and spill
       down over the tab bar (the top bar is pinned at a fixed 48px height). */
    white-space: nowrap;
    overflow: hidden;
}
.rcms-app-topbar-heading > .rcms-crumb {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rcms-app-topbar h1 {
    font: var(--md-sys-typescale-ui-header);
    margin: 0;
}
.rcms-app-topbar-actions {
    display: flex;
    gap: 8px;
    align-items: center;
    flex: 0 0 auto;
}

/* Mobile — the top bar is a single nowrap row (breadcrumbs · search ·
   actions). In the page editor the action cluster (locale, preview,
   three saves, kebab) is `flex: 0 0 auto` so it overflows off-screen
   while the breadcrumb collapses to width 0. The second toolbar
   (`.rcms-action-bar`) is a sibling sticky bar pinned at `top: 48px` — i.e.
   coupled to a 48px-tall top bar — so simply letting the top bar grow
   makes the two collide.

   Below 768px: un-stick both bars (a multi-row sticky toolbar would eat
   the small viewport), let the top bar wrap into stacked rows, drop the
   global search (list views carry their own), let the actions wrap, and
   let the overflowing tab strip scroll horizontally. Everything stays
   on-screen and tappable, and the two bars stack instead of overlap. */
@media (max-width: 768px) {
    .rcms-app-topbar {
        position: static;
        /* `.rcms-app-main` is a 100vh flex column; without this the column
           shrinks the bar back to its 48px min-height and the wrapped
           rows spill out and overlap the next bar. */
        flex: none;
        flex-wrap: wrap;
        height: auto;
        row-gap: 8px;
        padding: 8px 12px;
    }
    .rcms-app-topbar-left { flex: 1 1 100%; }
    .rcms-app-topbar-search { display: none; }
    .rcms-app-topbar-actions {
        flex: 1 1 100%;
        flex-wrap: wrap;
        justify-content: flex-start;
    }
    .rcms-action-bar {
        position: static;
        flex: none;
        flex-wrap: wrap;
        height: auto;
        row-gap: 6px;
    }
    /* 8 section tabs can't fit a phone row — let them swipe instead of
       overflowing under the preview controls. */
    .rcms-tab-strip {
        flex-wrap: nowrap;
        max-width: 100%;
        overflow-x: auto;
    }
    .rcms-preview-controls { flex-wrap: wrap; }
}

/* Secondary "action bar" sticky row — page-editor mounts the tab
   strip + preview controls here. List pages leave the `action_bar`
   block empty so the row never renders. */
.rcms-action-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 16px;
    min-height: 40px;
    /* Don't shrink inside the scrollable main column — grow to fit the
       wrapped tab strip instead of clipping it. */
    flex-shrink: 0;
    background: var(--bg-elevated);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 48px;
    z-index: 11;
}

.rcms-topbar-crumbs {
    display: flex; align-items: center; gap: 6px;
    min-width: 0; flex: 0 1 auto;
}
/* Crumb items are shared by the topbar and the page-editor trail
   (#532) — one rule set; each context keeps only its own deltas. */
.rcms-topbar-crumbs .rcms-crumb,
.rcms-page-editor-crumbs .rcms-crumb {
    display: inline-flex; align-items: center; gap: 4px;
    color: var(--muted); text-decoration: none;
    font-size: 13px; font-weight: 500;
    white-space: nowrap;
}
.rcms-topbar-crumbs .rcms-crumb:hover,
.rcms-page-editor-crumbs .rcms-crumb:hover { color: var(--accent); }
.rcms-topbar-crumbs .rcms-crumb.here,
.rcms-page-editor-crumbs .rcms-crumb.here { color: var(--fg); font-weight: 600; }
/* Only the topbar shrinks its current-page crumb — the editor trail
   scrolls sideways instead, so no min-width/ellipsis there. */
.rcms-topbar-crumbs .rcms-crumb.here {
    min-width: 0;
    overflow: hidden; text-overflow: ellipsis;
}
.rcms-topbar-crumbs .rcms-crumb-sep,
.rcms-page-editor-crumbs .rcms-crumb-sep { color: var(--muted-2); }
.rcms-topbar-crumbs .rcms-crumb-sep { font-size: 14px; }
.rcms-topbar-crumbs .rcms-crumb .material-symbols-rounded,
.rcms-page-editor-crumbs .rcms-crumb .material-symbols-rounded { font-size: 16px; }

/* #525 — admin UI language switcher in the sidebar footer. */
.rcms-lang-switcher { width: 100%; }
/* Summary reset comes from the shared <details> menu rule below. */
.rcms-lang-switcher > summary { display: flex; align-items: center; gap: 8px; }
.rcms-lang-switcher .rcms-lang-menu { display: flex; flex-direction: column; padding: 4px 0 4px 28px; gap: 2px; }
.rcms-lang-switcher .rcms-lang-opt { text-decoration: none; color: inherit; font-size: 0.85rem; padding: 3px 6px; border-radius: 6px; opacity: 0.8; }
.rcms-lang-switcher .rcms-lang-opt:hover { background: var(--md-sys-color-surface-container-high, #ececf2); opacity: 1; }
.rcms-lang-switcher .rcms-lang-opt.active { font-weight: 700; opacity: 1; }

/* #532 — page-editor breadcrumb row: dedicated slim row below the topbar
   with a back-to-parent control + the full ancestor trail (scrolls when long
   instead of re-cramming). */
.rcms-page-editor-crumbs {
    display: flex; align-items: center; gap: 12px;
    padding: 8px 16px; min-width: 0;
    border-bottom: 1px solid var(--border);
    background: var(--md-sys-color-surface);
    font-size: 13px;
}
.rcms-page-editor-crumbs .rcms-crumb-back {
    display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto;
    color: var(--accent); text-decoration: none; font-weight: 600;
    padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px;
}
.rcms-page-editor-crumbs .rcms-crumb-back:hover { background: var(--md-sys-color-surface-container-high); }
.rcms-page-editor-crumbs .rcms-crumb-back .material-symbols-rounded { font-size: 16px; }
.rcms-page-editor-crumbs .rcms-crumb-trail {
    display: flex; align-items: center; gap: 6px; min-width: 0;
    overflow-x: auto; white-space: nowrap; scrollbar-width: thin;
}
/* Crumb items themselves are styled with the topbar crumbs above. */
@media (max-width: 640px) {
    .rcms-page-editor-crumbs .rcms-crumb-back-label { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
.rcms-topbar-crumbs .rcms-locale-badge {
    background: var(--md-sys-color-tertiary-container);
    color: var(--md-sys-color-on-tertiary-container);
    padding: 1px 8px; border-radius: var(--md-sys-radius-full);
    font: var(--md-sys-typescale-ui-label);
    margin-left: 4px; text-transform: uppercase;
}

.rcms-chip-cluster {
    display: flex; align-items: center; gap: 6px;
    flex: 1 1 auto; min-width: 0;
    overflow-x: auto; overflow-y: hidden;
    scrollbar-width: thin;
    padding: 2px 0;
}
.rcms-chip-cluster::-webkit-scrollbar { height: 4px; }
.rcms-chip-cluster::-webkit-scrollbar-thumb {
    background: var(--md-sys-color-outline-variant); border-radius: 2px;
}
/* #i18n — the locale list's "Core support" cell: wrap its status pills and
   stay visible at every width (unlike the scrolling, mobile-hidden
   .rcms-chip-cluster). */
.rcms-locale-support { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
/* #324 — `.status-chip` (the icon-bearing status pill) duplicated `.rcms-tag`
   with identical status colors; it folded into the canonical `.rcms-tag` API
   (see the `.rcms-tag` section). The page-form topbar now uses `.rcms-tag.<status>`
   with a leading icon; the scoped `.rcms-chip-cluster .rcms-tag` rule there restores
   the no-shrink / nowrap layout this variant needed. */
.rcms-display-chip {
    display: inline-flex; align-items: baseline; gap: 6px;
    padding: 4px 10px; border-radius: var(--md-sys-radius-md);
    background: var(--md-sys-color-surface-container-low);
    border: 1px solid var(--md-sys-color-outline-variant);
    font-size: 12px; white-space: nowrap; flex: 0 0 auto;
}
.rcms-display-chip .material-symbols-rounded {
    font-size: 14px; color: var(--accent); align-self: center;
}
.rcms-display-chip-label {
    color: var(--muted); text-transform: uppercase;
    font: var(--md-sys-typescale-ui-label);
    letter-spacing: 0.04em;
}
.rcms-display-chip-value { color: var(--fg); font-weight: 500; }
.rcms-display-chip-value strong { font-weight: 600; }

/* Kebab + locale menus — native <details>/<summary> disclosures.
   The summary reset (no marker, pointer cursor) is shared by every
   <details>-based menu in the admin — one rule set here. */
.rcms-kebab-menu, .rcms-locale-menu { position: relative; }
.rcms-kebab-menu > summary,
.rcms-locale-menu > summary,
.rcms-lang-switcher > summary,
.rcms-col-picker > summary,
.rcms-fb-translate-menu > summary {
    list-style: none; cursor: pointer;
}
.rcms-kebab-menu > summary::-webkit-details-marker,
.rcms-locale-menu > summary::-webkit-details-marker,
.rcms-lang-switcher > summary::-webkit-details-marker,
.rcms-col-picker > summary::-webkit-details-marker,
.rcms-fb-translate-menu > summary::-webkit-details-marker { display: none; }
.rcms-kebab-panel, .rcms-locale-menu-panel {
    position: absolute; right: 0; top: calc(100% + 6px);
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--md-sys-radius-lg);
    box-shadow: var(--md-sys-elevation-3);
    min-width: 220px; z-index: 20;
    padding: 6px;
    display: flex; flex-direction: column;
}
.rcms-kebab-panel > a,
.rcms-kebab-panel > form > button,
.rcms-kebab-panel > button[role="menuitem"],
.rcms-locale-menu-panel > a {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 12px; border-radius: var(--md-sys-radius-md);
    color: var(--fg); text-decoration: none;
    font-size: 13px;
    background: transparent; border: 0;
    width: 100%;
    text-align: left;
    cursor: pointer;
    font: inherit;
}
.rcms-kebab-panel > form { margin: 0; display: block; }
.rcms-kebab-panel > a:hover,
.rcms-kebab-panel > form > button:hover,
.rcms-kebab-panel > button[role="menuitem"]:hover,
.rcms-locale-menu-panel > a:hover {
    background: var(--md-sys-color-surface-container-low);
    color: var(--accent);
}
.rcms-kebab-panel .rcms-kebab-divider { border-top: 1px solid var(--md-sys-color-outline-variant); margin-top: 4px; padding-top: 4px; }
.rcms-kebab-panel .rcms-kebab-danger { color: var(--md-sys-color-error); }
/* Danger/logout hover fill lives in the shared semantic fills (tag section). */

/* #156 — list-row actions: wrap gracefully on narrow viewports so
   the row layout doesn't collapse. Plus #157 — Edit + drill-down
   always visible, everything else in the row kebab. */
.rcms-row-actions { flex-wrap: wrap; justify-content: flex-end; }
.rcms-row-actions .rcms-row-actions-children { margin-right: 4px; }
.rcms-row-kebab .rcms-kebab-panel { min-width: 200px; }
/* `.rcms-kebab-logout` is a `.rcms-kebab-panel > form > button` — the shared
   menu-item rule above already styles it; its error hover fill lives
   in the shared semantic fills (tag section). */
.rcms-locale-menu-panel .rcms-canonical-tag {
    margin-left: auto;
    background: var(--md-sys-color-primary-fixed);
    color: var(--md-sys-color-on-primary-fixed);
    font: var(--md-sys-typescale-ui-label);
    padding: 1px 8px; border-radius: var(--md-sys-radius-full);
    text-transform: uppercase; letter-spacing: 0.05em;
}

/* Narrow-viewport collapses. */
@media (max-width: 760px) {
    .rcms-chip-cluster { display: none; }
    .rcms-topbar-crumbs .rcms-crumb:not(.here) .rcms-crumb-label { display: none; }
    .rcms-topbar-crumbs .rcms-crumb-sep { display: none; }
}

.rcms-breadcrumbs {
    color: var(--muted);
    font-size: 13px;
    padding: 12px 24px 0;
}
.rcms-breadcrumbs a { color: var(--accent); text-decoration: none; }
.rcms-breadcrumbs a:hover { text-decoration: underline; }
.rcms-breadcrumbs .rcms-sep { margin: 0 6px; color: var(--muted-2); }

/* ============================================================
 * 6. Flash banners
 * ============================================================ */

.rcms-app-flash {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px 24px 0;
}
.rcms-flash {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 16px;
    border-radius: var(--md-sys-radius-lg);
    border: 1px solid transparent;
    font-size: 13px;
    line-height: 1.5;
}
.rcms-flash-icon { font-size: 18px; flex-shrink: 0; margin-top: 1px; }
.rcms-flash-body { flex: 1; }
.rcms-flash--success {
    background: var(--success-soft);
    border-color: var(--success);
    color: var(--success-hover);
}
.rcms-flash--info {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--md-sys-color-on-primary-fixed-variant);
}
.rcms-flash--warning {
    background: var(--warning-soft);
    border-color: var(--warning);
    color: var(--warning-hover);
}
.rcms-flash--error,
.rcms-flash--debug {
    background: var(--danger-soft);
    border-color: var(--danger);
    color: var(--danger-hover);
}

/* #324 — `.rcms-alert`: persistent in-page status banner. Distinct from the
   transient `.rcms-flash` message region above (one-shot, hoisted to a toast
   on load) and the `.rcms-toast` corner stack — `.rcms-alert` is a banner that
   stays put (e.g. a page-lock notice). One canonical class + md-sys-token
   variants replaces hand-rolled inline-styled `role="alert"` divs. */
.rcms-alert {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    margin-bottom: 12px;
    border-radius: var(--md-sys-radius-lg);
    box-shadow: var(--md-sys-elevation-1);
}
/* `.rcms-alert--error` / `.rcms-alert--warning` fills live in the shared
   semantic fills (tag section). */
.rcms-alert-body { flex: 1; }

.rcms-app-content {
    flex: 1;
    padding: 24px 28px;
    width: 100%;
    min-width: 0;
}

/* ============================================================
 * 7. M3 button system
 *
 * Five canonical variants:
 *   .rcms-btn          — filled (primary action)
 *   .rcms-btn-tonal    — tonal (secondary emphasis)
 *   .rcms-btn-outlined — outlined (low-emphasis affirmative)
 *   .rcms-btn-text     — text (lowest emphasis, default no padding-x)
 *   .rcms-btn-danger   — filled error
 * Modifiers:
 *   .rcms-btn-small    — compact (32px tall, 16px x-pad)
 *   .rcms-btn-icon     — square icon-only (40x40)
 *
 * Each button picks up an M3 state layer via `::before`:
 * currentColor at opacity 0 → 0.08 hover → 0.12 active.
 *
 * One canonical class API (#293/#324) — the legacy compound aliases
 * (.rcms-btn.ghost/.muted/.small/.danger/.violet) are gone; see the note
 * by the .rcms-btn-small variant rules below.
 * ============================================================ */

.rcms-btn {
    position: relative;
    isolation: isolate;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 40px;
    padding: 0 24px;
    font: var(--md-sys-typescale-ui-button);
    border-radius: var(--md-sys-radius-lg);
    border: none;
    cursor: pointer;
    background: var(--md-sys-color-primary);
    color: var(--md-sys-color-on-primary);
    text-decoration: none;
    box-shadow: var(--md-sys-shadow-primary);
    transition: box-shadow 0.14s, transform 0.08s;
    user-select: none;
    white-space: nowrap;
}
.rcms-btn:hover {
    box-shadow: 0px 6px 18px color-mix(in srgb, var(--md-sys-color-primary) 28%, transparent);
}
.rcms-btn::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: currentColor;
    opacity: 0;
    border-radius: inherit;
    transition: opacity 0.12s;
    pointer-events: none;
}
.rcms-btn:hover::before { opacity: 0.08; }
.rcms-btn:focus-visible::before { opacity: 0.10; }
.rcms-btn:active::before { opacity: 0.12; }
.rcms-btn:active { transform: scale(0.98); }
.rcms-btn:disabled,
.rcms-btn[aria-disabled="true"] {
    opacity: 0.38;
    cursor: not-allowed;
    pointer-events: none;
}
.rcms-btn .material-symbols-rounded { font-size: 18px; vertical-align: -4px; }

/* Variants. */
.rcms-btn-tonal {
    background: var(--md-sys-color-primary-container);
    color: var(--md-sys-color-on-primary-container);
    box-shadow: none;
}
.rcms-btn-outlined {
    background: transparent;
    color: var(--md-sys-color-primary);
    border: 1px solid var(--md-sys-color-outline-variant);
    box-shadow: none;
}
.rcms-btn-outlined:hover { box-shadow: none; border-color: var(--md-sys-color-primary); }
.rcms-btn-text {
    background: transparent;
    color: var(--md-sys-color-primary);
    padding: 0 12px;
    box-shadow: none;
}
.rcms-btn-text:hover { box-shadow: none; }
.rcms-btn-danger {
    background: var(--md-sys-color-error);
    color: var(--md-sys-color-on-error);
    box-shadow: 0px 4px 14px color-mix(in srgb, var(--md-sys-color-error) 22%, transparent);
}
.rcms-btn-danger:hover {
    box-shadow: 0px 6px 18px color-mix(in srgb, var(--md-sys-color-error) 28%, transparent);
}
.rcms-btn-icon {
    width: 40px;
    padding: 0;
    border-radius: var(--md-sys-radius-full);
}
.rcms-btn-small {
    height: 32px;
    padding: 0 16px;
    font-size: 13px;
}
.rcms-btn-small.rcms-btn-icon { width: 32px; padding: 0; }

/* #293 phase 2 — the compound aliases `.rcms-btn.ghost` (byte-identical to
   `.rcms-btn-outlined`) and `.rcms-btn.muted` (≡ `.rcms-btn-text`) were duplicates of
   the canonical BEM variants above. Templates have been swept to
   `.rcms-btn-outlined` / `.rcms-btn-text` and the compound rules removed, so the
   dual API doesn't survive as a maintenance trap — matching the earlier
   `.rcms-btn.small` → `.rcms-btn-small` and `.rcms-btn.danger` → `.rcms-btn-danger`
   consolidations. (`.rcms-btn.violet` was dropped in phase 1: zero consumers;
   tertiary accents go through `.rcms-btn-tonal`.) The former
   `.rcms-btn-small.ghost` / `.rcms-btn-small.muted` padding override carries over
   to the canonical small-variant selectors below. */
.rcms-btn-small.rcms-btn-outlined,
.rcms-btn-small.rcms-btn-text { padding: 0 12px; }

/* ============================================================
 * 8. Cards, empty states, data tables
 * ============================================================ */

.rcms-card {
    background: var(--md-sys-color-surface-container-lowest);
    border: 1px solid color-mix(in srgb, var(--md-sys-color-outline-variant) 40%, transparent);
    border-radius: var(--md-sys-radius-xl);
    padding: 24px;
    box-shadow: var(--md-sys-elevation-2);
}
/* Adjacent-sibling card margin is opt-in via `.rcms-card-stack` so grid /
   inline-flex layouts (media library cards, snippet folder grid)
   don't pick up unwanted gaps that misalign with the parent's own
   `gap`. Vertical-stack contexts (settings page, form chrome) opt in
   by wrapping with `<div class="rcms-card-stack">`. The page edit pane
   is the most common vertical-stack container — it opts in too. */
.rcms-card-stack > .rcms-card + .rcms-card,
.rcms-edit-pane > .rcms-card + .rcms-card,
.rcms-edit-pane > section.rcms-card + section.rcms-card,
.rcms-edit-pane > form.rcms-card + .rcms-card,
.rcms-edit-pane > .rcms-card + section.rcms-card,
.rcms-edit-pane > section.rcms-card + .rcms-card,
.rcms-edit-pane > details.rcms-card + .rcms-card,
.rcms-edit-pane > .rcms-card + details.rcms-card,
.rcms-app-main > .rcms-card + .rcms-card,
.rcms-app-main > section.rcms-card + section.rcms-card { margin-top: 16px; }
.rcms-card h2 { margin-top: 0; }

.rcms-empty {
    padding: 48px;
    text-align: center;
    color: var(--muted);
    background: var(--md-sys-color-surface-container-lowest);
    border: 1px dashed var(--border);
    border-radius: var(--md-sys-radius-xl);
}
.rcms-empty p { margin: 0 0 16px; }

/* Responsive-table wrapper. Bare `table.rcms-data` is `width:100%` with no
 * min-width, so a wide table (many columns / nowrap action cells) pushes
 * the whole page into horizontal scroll on phones. Wrap wide tables in
 * `<div class="rcms-table-wrap">` so the TABLE scrolls sideways within the
 * page instead. Mirrors the overflow-x pattern used by `.rcms-chip-cluster`
 * / `.rcms-tab-strip`, and the inline precedent in form_submissions.html. */
.rcms-table-wrap {
    overflow-x: auto;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
}
/* Permission matrices have one column per action — keep them at their
 * natural width and let `.rcms-table-wrap` scroll, rather than cramming. */
.rcms-table-wrap > .rcms-perm-matrix,
.rcms-table-wrap > .rcms-perm-table,
.rcms-table-wrap > table.rcms-perm-overview {
    min-width: max-content;
}

table.rcms-data {
    width: 100%;
    border-collapse: collapse;
    background: var(--md-sys-color-surface-container-lowest);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-xl);
    overflow: hidden;
}
table.rcms-data th, table.rcms-data td {
    padding: 12px 16px;
    text-align: left;
    border-bottom: 1px solid var(--md-sys-color-outline-variant);
    vertical-align: middle;
}
table.rcms-data th {
    background: var(--md-sys-color-surface-container-low);
    font: var(--md-sys-typescale-ui-label);
    color: var(--muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
table.rcms-data tr:last-child td { border-bottom: none; }
table.rcms-data tr:hover td {
    background: var(--md-sys-color-surface-container-low);
}
/* Action cells across the admin.
 *
 * The .rcms-actions class is on the <td> ITSELF in the listing templates
 * (locked_pages, library_list, revisions_report, etc.) — buttons +
 * per-row <form>s are direct children. So we must NOT apply
 * `display: flex` to .rcms-actions, since that turns the <td> into a
 * flex container and removes it from the table-row layout — content
 * then renders as a free-floating flex box that overflows the
 * table's clipping region.
 *
 * Keep the cell native (`display: table-cell` by default). Use a
 * tiny `* + *` margin to space children. Forms render inline-block
 * so they baseline-align with <a class="rcms-btn"> siblings.
 *
 * `.rcms-row-actions` (div-based — page list) keeps the flex wrap layout
 * since it isn't constrained by a table column.
 */
table.rcms-data th.rcms-actions,
table.rcms-data td.rcms-actions {
    text-align: right;
    white-space: nowrap;
    vertical-align: middle;
}
table.rcms-data td.rcms-actions > * + * {
    margin-left: 8px;
}
table.rcms-data td.rcms-actions form {
    display: inline-block;
    margin: 0;
}
.rcms-row-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    justify-content: flex-end;
}
.rcms-row-actions form {
    display: inline-flex;
    margin: 0;
}
table.rcms-data .rcms-actions .rcms-btn,
.rcms-row-actions .rcms-btn,
table.rcms-data .rcms-actions .rcms-btn-icon,
.rcms-row-actions .rcms-btn-icon {
    /* Lock to the small-row height so label-only, label+caret, and
     * icon-only buttons share one baseline. */
    height: 32px;
    min-height: 32px;
}
table.rcms-data .rcms-actions .rcms-btn-icon,
.rcms-row-actions .rcms-btn-icon {
    width: 32px;
}
table.rcms-data .rcms-actions .rcms-btn-small,
.rcms-row-actions .rcms-btn-small {
    padding: 0 12px;
}

/* ============================================================
 * 9. Status pills (`.rcms-tag`) — pill-shape with leading dot
 *
 * Pairs an MD3 *-container background with the matching *-on-
 * foreground for AA contrast. The leading colored dot is rendered
 * via `::before` so the markup stays a plain
 * `<span class="rcms-tag published">`.
 * ============================================================ */

.rcms-tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: var(--md-sys-radius-full);
    font: var(--md-sys-typescale-ui-label);
    background: var(--md-sys-color-surface-container-high);
    color: var(--md-sys-color-on-surface-variant);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.rcms-tag::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: var(--md-sys-radius-full);
    background: currentColor;
    opacity: 0.85;
}
.rcms-tag.draft {
    background: var(--md-sys-color-surface-container-high);
    color: var(--md-sys-color-on-surface-variant);
}
.rcms-tag.published {
    background: var(--md-sys-color-success-container);
    color: var(--md-sys-color-on-success-container);
}
/* `.rcms-tag.scheduled` / `.rcms-tag.archived` fills live in the shared
   semantic fills just below. */
/* #556 — `expired` (takedown) is a neutral terminal state, distinct
   from the error-red `archived` (preserved-and-served) pill. */
.rcms-tag.expired {
    background: var(--md-sys-color-surface-container-highest);
    color: var(--md-sys-color-on-surface-variant);
    opacity: 0.75;
}
/* #324 — icon-bearing variant (absorbed the former `.status-chip`). A
   leading Material icon replaces the `::before` status dot. */
.rcms-tag .material-symbols-rounded { font-size: 14px; }
.rcms-tag:has(.material-symbols-rounded)::before { display: none; }
/* Page-form action-bar status pill — keep it from shrinking / wrapping
   in the flex `.rcms-chip-cluster` (the layout `.status-chip` used to carry). */
.rcms-chip-cluster .rcms-tag { white-space: nowrap; flex: 0 0 auto; }

/* ------------------------------------------------------------
 * Shared semantic fills (DRY). Every "this element is in an
 * error/warning state" surface uses the same container/on-container
 * token pair — one rule per tone instead of a copy per component.
 * Placed after `.rcms-card` (equal specificity with `.rcms-card--error`) and
 * before every member's own section.
 * ------------------------------------------------------------ */
.rcms-alert--error,
.rcms-tag.archived,
.rcms-card--error,
.rcms-autosave-chip[data-mode="warn"],
.rcms-kebab-panel .rcms-kebab-danger:hover,
.rcms-kebab-panel .rcms-kebab-logout:hover {
    background: var(--md-sys-color-error-container);
    color: var(--md-sys-color-on-error-container);
}
.rcms-alert--warning,
.rcms-tag.scheduled {
    background: var(--md-sys-color-warning-container);
    color: var(--md-sys-color-on-warning-container);
}

/* ============================================================
 * 10. M3 form fields
 * ============================================================ */

.rcms-field { margin-bottom: 18px; }
/* Canonical form label — one look for every labelled control in the
   admin: `.rcms-field`, stream-editor fields/legends, and the auth card.
   Contexts keep only their own margin/padding deltas at their site. */
.rcms-field label,
.rcms-stream-field > label,
.rcms-stream-nested > legend,
.rcms-auth-card label {
    display: block;
    font: var(--md-sys-typescale-ui-label);
    color: var(--md-sys-color-on-surface-variant);
    margin-bottom: 6px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.rcms-field label .rcms-req { color: var(--md-sys-color-error); }
.rcms-field input[type="text"],
.rcms-field input[type="password"],
.rcms-field input[type="email"],
.rcms-field input[type="number"],
.rcms-field input[type="url"],
.rcms-field input[type="search"],
.rcms-field input[type="tel"],
.rcms-field input[type="date"],
.rcms-field input[type="time"],
.rcms-field input[type="datetime-local"],
.rcms-field select,
.rcms-field textarea,
.rcms-auth-card input[type="text"],
.rcms-auth-card input[type="password"],
.rcms-auth-card input[type="email"] {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    font: inherit;
    background: var(--md-sys-color-surface-container);
    color: var(--md-sys-color-on-surface);
    transition: border-color 0.12s, box-shadow 0.12s;
}
.rcms-field input:hover,
.rcms-field select:hover,
.rcms-field textarea:hover,
.rcms-auth-card input:hover {
    border-color: var(--md-sys-color-outline);
}
.rcms-field input:focus,
.rcms-field select:focus,
.rcms-field textarea:focus,
.rcms-auth-card input:focus {
    outline: none;
    border-color: var(--md-sys-color-primary);
    box-shadow: 0 0 0 1px var(--md-sys-color-primary);
}
.rcms-field input[aria-invalid="true"],
.rcms-field select[aria-invalid="true"],
.rcms-field textarea[aria-invalid="true"] {
    border-color: var(--md-sys-color-error);
}
.rcms-field input[aria-invalid="true"]:focus,
.rcms-field select[aria-invalid="true"]:focus,
.rcms-field textarea[aria-invalid="true"]:focus {
    box-shadow: 0 0 0 1px var(--md-sys-color-error);
}
.rcms-field textarea { min-height: 96px; resize: vertical; }
/* Editor body surfaces get Literata for content authoring — Hanken
   Grotesk reads well in UI but a literary serif reads better for
   paragraph-length prose. */
.rcms-field textarea[data-widget-mode="markdown"],
.rcms-field textarea[data-widget-mode="richtext"] {
    font: var(--md-sys-typescale-editor-text);
    min-height: 240px;
}

/* #294 — TipTap richtext editor chrome. The `data-widget-mode="richtext"`
   textarea is hidden by richtext-editor.js and replaced by this surface. */
.rcms-richtext {
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    background: var(--md-sys-color-surface-container);
    overflow: hidden;
}
.rcms-richtext:focus-within { border-color: var(--md-sys-color-primary); }
.rcms-richtext-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    padding: 6px 8px;
    border-bottom: 1px solid var(--md-sys-color-outline-variant);
    background: var(--md-sys-color-surface-container-high);
}
.rcms-richtext-tool {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 30px;
    height: 30px;
    padding: 0 6px;
    border: 0;
    border-radius: var(--md-sys-radius-sm);
    background: transparent;
    color: var(--md-sys-color-on-surface-variant);
    font: var(--md-sys-typescale-ui-label);
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}
.rcms-richtext-tool .material-symbols-rounded { font-size: 18px; }
.rcms-richtext-tool:hover { background: var(--md-sys-color-surface-container-highest); }
/* Active fill comes from the shared active-tool rule (minimap section). */
.rcms-richtext-sep {
    width: 1px;
    align-self: stretch;
    margin: 4px 4px;
    background: var(--md-sys-color-outline-variant);
}
.rcms-richtext-surface { padding: 12px 14px; }
.rcms-richtext-surface .ProseMirror {
    min-height: 200px;
    outline: none;
    font: var(--md-sys-typescale-editor-text);
    color: var(--md-sys-color-on-surface);
}
.rcms-richtext-surface .ProseMirror > :first-child { margin-top: 0; }
.rcms-richtext-surface .ProseMirror p { margin: 0 0 10px; }
.rcms-richtext-surface .ProseMirror h2,
.rcms-richtext-surface .ProseMirror h3,
.rcms-richtext-surface .ProseMirror h4 { margin: 16px 0 8px; line-height: 1.25; }
.rcms-richtext-surface .ProseMirror ul,
.rcms-richtext-surface .ProseMirror ol { margin: 0 0 10px; padding-left: 22px; }
.rcms-richtext-surface .ProseMirror blockquote {
    margin: 0 0 10px;
    padding: 2px 12px;
    border-left: 3px solid var(--md-sys-color-outline-variant);
    color: var(--md-sys-color-on-surface-variant);
}
.rcms-richtext-surface .ProseMirror code {
    font-family: var(--md-sys-font-mono, monospace);
    background: var(--md-sys-color-surface-container-highest);
    padding: 1px 4px;
    border-radius: var(--md-sys-radius-sm);
}
.rcms-richtext-surface .ProseMirror a { color: var(--md-sys-color-primary); }
/* Unscoped — hints render inside and outside `.rcms-field` (page-form
   subscribe row, stream editor, pickers); one base look everywhere. */
.rcms-hint {
    font-size: 12px;
    color: var(--muted);
    margin-top: 6px;
}
/* #324 — hint modifiers. Compound `.rcms-hint.hint--*` (specificity 0,2,0,
   declared after `.rcms-field .rcms-hint`) so they win the cascade over the base
   hint by source order, with or without a `.rcms-field` ancestor. These
   replace per-arm inline `style=` hints in _widget.html — including the
   legacy `--danger` / `--warning` token usages — with md-sys tokens. */
.rcms-hint.rcms-hint--sm { font-size: 11px; }
.rcms-hint.rcms-hint--error { color: var(--md-sys-color-error); }
.rcms-hint.rcms-hint--warning { color: var(--md-sys-color-warning); }
/* Horizontal-flex row of fields. Columns align at the TOP
 * (`align-items: flex-start`): with single-line labels the inputs
 * sit level, and a hint below one column's input only grows that
 * column — it can't push a neighbour's input out of line (the old
 * `flex-end` bottom-alignment did exactly that, which is why hints
 * were once absolutely positioned). Hints stay IN FLOW so they
 * reserve their real height — the previous absolute + fixed-22px
 * reservation clipped any hint that wrapped past one line and let
 * the next row render on top of it (redirect form overlap). */
.rcms-field-row {
    display: flex;
    gap: 16px;
    align-items: flex-start;
    /* Wrap to stacked columns on narrow viewports — without this the
     * fixed 2-up rows (Go live / Expire, OG image / Twitter card) run
     * off the edge on small screens instead of stacking. */
    flex-wrap: wrap;
    /* Row-level rhythm replaces the per-field margin zeroed below —
     * matches the base `.rcms-field` spacing so stacked rows read the same
     * as stacked bare fields. */
    margin-bottom: 18px;
}
.rcms-field-row .rcms-field {
    /* `flex-basis` (not 0) so a column that can't keep ~220px wraps to
     * its own full-width line; `min-width:0` lets the control shrink
     * inside its column instead of forcing horizontal overflow. */
    flex: 1 1 220px;
    min-width: 0;
    margin-bottom: 0;
}
/* Keep every label in a row the same height. A label is 16px of line
 * box (`ui-label`) + 6px margin = a 22px block, but an inline
 * `.material-symbols-rounded.sm` (18px, shifted -3px) grows the line
 * box to 19px — so an icon-bearing column's control sat 3px below its
 * plain-label neighbour's. Laying the label out as a flex line pins it
 * back to 16px and gives the action rule below a fixed offset. */
.rcms-field-row > .rcms-field > label {
    display: flex;
    align-items: center;
    gap: 4px;
    min-height: 16px;
}
.rcms-field-row > .rcms-field > label .material-symbols-rounded {
    font-size: 16px;
    line-height: 1;
    vertical-align: 0;
}
/* Buttons dropped straight into a `.rcms-field-row` (the search + Apply /
 * Clear filter bars) carry no label of their own, so the row's
 * `flex-start` parked them level with the LABELS, floating above the
 * inputs. Offset them by the label block so they sit on the control
 * line. Same for a `.rcms-field` wrapper holding nothing but a button. */
.rcms-field-row > .rcms-btn,
.rcms-field-row > button,
.rcms-field-row > .rcms-field > .rcms-btn:only-child,
.rcms-field-row > .rcms-field > button:only-child {
    margin-top: 22px;
}
.rcms-checkbox-field { display: flex; align-items: center; gap: 8px; }
.rcms-checkbox-field input { width: auto; }
.rcms-checkbox-field label {
    margin-bottom: 0;
    letter-spacing: 0;
    text-transform: none;
    font-weight: 500;
    color: var(--fg);
}

/* #324 — remaining HTML input types + multi-value/option wrappers.
   Every widget renders inside `.rcms-field` (see _widget.html `render`), so
   text-shaped inputs and `<select>` (incl. `[multiple]`) already pick up
   the chrome above. These close the gaps: `color` / `range` inputs
   (emitted by widgets but absent from the base type list) and the
   option / group / pair wrappers that otherwise fell back to UA-default
   layout. */

/* Color — a fixed-size swatch chip, not a full-width text field. */
.rcms-field input[type="color"] {
    width: 56px;
    height: 38px;
    padding: 4px;
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    background: var(--md-sys-color-surface-container);
    cursor: pointer;
}
.rcms-field input[type="color"]:hover { border-color: var(--md-sys-color-outline); }
.rcms-field input[type="color"]::-webkit-color-swatch { border: none; border-radius: 4px; }
.rcms-field input[type="color"]::-moz-color-swatch { border: none; border-radius: 4px; }

/* Range — themed via accent-color (track + thumb tint, cross-browser). */
.rcms-field input[type="range"] {
    width: 100%;
    accent-color: var(--md-sys-color-primary);
    cursor: pointer;
}

/* Radio / checkbox option groups (`radio`, multi-value `checkboxes`).
   Stack options with consistent spacing; each option is an inline
   input + label, mirroring `.rcms-checkbox-field`. */
.rcms-radio-group,
.rcms-checkbox-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
/* Scoped under the group so these win over `.rcms-field label` (0,1,1),
   which otherwise forces the option `<label>` back to display:block. */
.rcms-radio-group .rcms-radio-option,
.rcms-checkbox-group .rcms-checkbox-option {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
}

/* `<select multiple>` group — the select inherits `.rcms-field select` chrome;
   the wrapper just stacks it above its hint. */
.rcms-multiselect-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.rcms-multiselect-group .rcms-hint { margin-top: 0; }

/* Datetime + timezone pair — side by side instead of stacking. */
.rcms-datetimetz-pair {
    display: flex;
    gap: 8px;
}
.rcms-datetimetz-pair input[type="datetime-local"] { flex: 1 1 60%; min-width: 0; }
.rcms-datetimetz-pair select { flex: 1 1 40%; min-width: 0; }

.rcms-form-actions {
    display: flex;
    gap: 12px;
    justify-content: flex-end;
    margin-top: 24px;
    padding-top: 18px;
    border-top: 1px solid var(--border);
}
.rcms-header-actions { display: flex; gap: 8px; }

/* Permission matrix (role form, #325) — section spacing + heading chrome
   + cell padding swept off inline styles. Scoped to the role grid only
   (`.rcms-perm-section` / `.rcms-perm-matrix` appear nowhere else). The Resource
   column (first cell) keeps text padding; the centred checkbox columns
   get vertical-only padding (horizontal handled by the grid). */
.rcms-perm-section { margin-bottom: 20px; }
.rcms-perm-section-heading {
    padding: 8px 12px;
    background: var(--md-sys-color-surface-container);
    font: var(--md-sys-typescale-ui-label);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.rcms-perm-matrix th:first-child { text-align: left; padding: 6px 12px; }
.rcms-perm-matrix td:first-child { padding: 10px 12px; }
.rcms-perm-matrix td:not(:first-child) { padding: 10px 0; }

/* Page comment threads + workflow chrome (role/page editor, #325).
   `.rcms-comment*` and `.rcms-workflow-bar` appear only in page_form. */
.rcms-comment       { padding: 10px 0; }
.rcms-comment-body  { margin-top: 6px; }
.rcms-comment-reply {
    margin-left: 16px;
    padding-left: 12px;
    border-left: 2px solid var(--md-sys-color-outline-variant);
}
.rcms-comment-reply .rcms-hint { margin-top: 2px; }
.rcms-comment-reply-form  { margin-left: 16px; }
.rcms-comment-input {
    height: 32px;
    padding: 0 8px;
    background: var(--md-sys-color-surface-container);
}
.rcms-comment-new { background: var(--md-sys-color-surface-container-low); }
.rcms-chip { padding: 4px 8px; background: var(--md-sys-color-surface-container); }
/* `.rcms-card--error` fill lives in the shared semantic fills (tag section). */
/* Page editor status banners (#325, page_form only). The presence
   banner starts hidden; its JS toggles inline `display` (none/flex),
   which overrides this base rule either way. */
.rcms-page-lock-banner {
    background: var(--md-sys-color-tertiary-container);
    color: var(--md-sys-color-on-tertiary-container);
    border-radius: var(--md-sys-radius-lg);
    box-shadow: var(--md-sys-elevation-1);
}
.rcms-page-presence-banner {
    display: none;
    padding-left: 12px;
    padding-right: 12px;
    background: var(--md-sys-color-secondary-container);
    color: var(--md-sys-color-on-secondary-container);
}
/* Upload crop tool (#325, media_upload). The preview is crosshair-
   draggable; the rect overlay is positioned by JS (top/left/size) and
   starts hidden. */
.rcms-upload-crop-preview { max-height: 480px; cursor: crosshair; }
.rcms-upload-crop-rect {
    border: 2px solid var(--md-sys-color-primary);
    background: color-mix(in srgb, var(--md-sys-color-primary) 18%, transparent);
    pointer-events: none;
    display: none;
}
/* Media-card overlays (#325, media_list) — positioned on each card. */
.rcms-media-card-select {
    top: 16px;
    left: 16px;
    z-index: 1;
    background: var(--md-sys-color-surface-container-lowest);
    padding: 2px 4px;
    box-shadow: var(--md-sys-elevation-1);
}
.rcms-media-card-kebab { top: 12px; right: 12px; z-index: 2; }

/* #159 — canonical search-input capsule. Was previously inline in
   page_list.html only; promoted here so every listing (pages,
   media, library, redirects, navigation, locales, topbar) shares
   one look.

   #288 — radius dropped from `--md-sys-radius-full` (pill) to
   `--md-sys-radius-md` so search inputs match the rest of the
   admin's text inputs (the `.rcms-field input[type="text"]` family
   above also uses `radius-md`). Pill-shaped search alongside
   square form inputs read as stitched-together; this aligns them. */
.rcms-search-input {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 0 12px;
    height: 40px;
    flex: 1 1 280px;
    background: var(--md-sys-color-surface-container);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    transition: border-color 0.12s, box-shadow 0.12s;
    cursor: text;
}
/* Shared listing filter row — a `.rcms-search-input` capsule, inline
   selects and small buttons on one line. Pages / media / redirects
   all wear this; keep new listings on it rather than a labelled
   `.rcms-field-row` card. */
.rcms-list-filter-row {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}
/* Chrome comes from the canonical `select:not(.rcms-field select)` — only
   the height is set here, so selects line up with the capsule. */
.rcms-list-filter-row select { height: 40px; }

.rcms-search-input:focus-within {
    border-color: var(--md-sys-color-primary);
    box-shadow: 0 0 0 1px var(--md-sys-color-primary);
}
.rcms-search-input > .material-symbols-rounded {
    color: var(--md-sys-color-on-surface-variant);
    flex: 0 0 auto;
}
.rcms-search-input > input[type="search"],
.rcms-search-input > input[type="text"] {
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    padding: 0;
    background: transparent;
    border: 0;
    outline: none;
    font: var(--md-sys-typescale-ui-body);
    color: var(--md-sys-color-on-surface);
}
.rcms-search-input > input[type="search"]::-webkit-search-decoration { display: none; }

/* Topbar modifier — tighter height + neutral flex basis since the
   topbar parent caps the overall width. */
.rcms-search-input--topbar {
    height: 36px;
    flex: 1 1 auto;
}

/* ============================================================
 * 11. Stream editor + block picker
 * ============================================================ */

/* #273 — Wagtail-style StreamField. The outer editor is flat —
   no box around the body — so the tree guide line is the only
   structural visual. Individual block cards are also flat (no
   border, no background): the dashed guide line on the left of
   each `.rcms-stream-nested` level + the circular block-type icon at
   the start of each header carry the whole hierarchy. */
.rcms-stream-editor {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 0;
    background: transparent;
}
.rcms-stream-editor .rcms-stream-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.rcms-stream-block {
    /* Flat — no border, no background. Sibling separation comes
       from the gap on `.rcms-stream-list` + the header's dotted
       underline. */
    border: none;
    border-radius: 0;
    background: transparent;
    overflow: visible;
    padding-bottom: 4px;
}
.rcms-stream-block-header {
    /* One tight row: circular type icon + title + circular action
       buttons, anchored by a dotted underline that picks up from
       the tree guide on the left. Border-bottom is 2 px so the
       dotted pattern actually renders as visible dots (1 px
       dotted aliases to a near-solid line in most browsers). */
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 4px;
    background: transparent;
    border-bottom: 2px dotted var(--md-sys-color-outline);
}
.rcms-stream-block-icon {
    /* Circular badge in the brand colour. Mirrors the Wagtail
       block-type indicator that anchors each row to its tree
       lineage at-a-glance. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex: 0 0 24px;
    border-radius: 50%;
    background: var(--md-sys-color-primary-container);
    color: var(--md-sys-color-on-primary-container);
    font-size: 14px;
}
.rcms-stream-block-title {
    flex: 1;
    font-size: 13px;
    font-weight: 500;
    color: var(--md-sys-color-on-surface);
}
.rcms-stream-block-actions {
    display: inline-flex;
    gap: 4px;
    align-items: center;
}
/* Round action buttons — match the type-icon badge so the whole
   row reads as a series of pills aligned along the dotted line. */
.rcms-stream-block-actions .rcms-btn {
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: 50%;
    font-size: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.rcms-stream-block-actions .rcms-btn .material-symbols-rounded { font-size: 16px; }
.rcms-stream-block-actions .rcms-btn-icon { width: 28px; }
.rcms-stream-block-body {
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
/* `.rcms-stream-field > label` is styled by the canonical form-label rule
   (M3 form fields section). */
/* #273 — Wagtail-style tree nesting. Each `.rcms-stream-nested` is a
   level of depth; its DASHED left border becomes a vertical guide
   that visually owns the indented column of nested blocks
   beneath it. Multiple levels stack naturally because
   `.rcms-stream-nested` is recursive — every nested level draws its
   own guide, so 3-deep nesting reads as three indented columns
   side-by-side. The dashed style mirrors Wagtail's tree-guide
   convention (vs. solid lines which read as borders). */
.rcms-stream-nested {
    border: none;
    /* 2 px so the dotted pattern actually renders as visible
       dots — 1 px dotted aliases to a near-solid line in most
       browsers. */
    border-left: 2px dotted var(--md-sys-color-outline);
    border-radius: 0;
    padding: 2px 0 4px 18px;
    margin: 6px 0 6px 6px;
}
/* Base look from the canonical form-label rule; legends only need
   their UA padding zeroed. */
.rcms-stream-nested > legend { padding: 0; }
/* Subtle highlight on the guide when a child block is focused —
   reinforces "this is the parent of what you're editing". */
.rcms-stream-nested:has(.rcms-stream-block:focus-within) {
    border-left-color: var(--md-sys-color-primary);
}
.rcms-stream-picker { align-self: flex-start; }
.rcms-stream-drift {
    padding: 12px;
    background: var(--md-sys-color-warning-container);
    border: 1px solid var(--md-sys-color-warning);
    color: var(--md-sys-color-on-warning-container);
    border-radius: var(--md-sys-radius-md);
    font-size: 13px;
}

.rcms-block-picker {
    border: 1px solid var(--border);
    border-radius: var(--md-sys-radius-xl);
    padding: 0;
    min-width: 360px;
    max-width: 90vw;
    background: var(--md-sys-color-surface-container-lowest);
    box-shadow: var(--md-sys-elevation-3);
}
.rcms-block-picker::backdrop { background: rgba(0, 0, 0, 0.3); }
/* min-width capped to the viewport so the dialog never exceeds a phone
 * screen (was a hard 420px, overflowing sub-420px devices). */
.rcms-block-picker { padding: 16px; min-width: min(420px, 90vw); }
.rcms-block-picker header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
}
/* #411 — block-picker search box. */
.rcms-block-picker-search {
    width: 100%;
    box-sizing: border-box;
    margin-bottom: 12px;
}
/* #411 — group section header inside the picker list. A presentational
   <li> that labels the blocks beneath it; hidden by the editor JS when
   the group has no visible (allowed + search-matching) blocks. */
.rcms-block-picker-group {
    list-style: none;
    margin: 8px 0 2px;
    padding: 0 4px;
    font-size: var(--md-sys-typescale-label-small-size, 0.6875rem);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--md-sys-color-on-surface-variant);
}
.rcms-block-picker-group:first-child { margin-top: 0; }
.rcms-block-picker-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    max-height: 60vh;
    overflow-y: auto;
}
.rcms-block-picker-tile {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    background: var(--md-sys-color-surface-container-lowest);
    cursor: pointer;
    font: inherit;
    font-size: 13px;
    color: var(--fg);
    width: 100%;
    text-align: left;
    transition: border-color 0.12s, box-shadow 0.12s;
}
.rcms-block-picker-tile::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--md-sys-color-primary);
    opacity: 0;
    border-radius: inherit;
    transition: opacity 0.12s;
}
.rcms-block-picker-tile:hover::before { opacity: 0.06; }
.rcms-block-picker-tile:hover { border-color: var(--md-sys-color-primary); }
.rcms-block-picker-tile .material-symbols-rounded {
    font-size: 22px;
    color: var(--accent);
    margin-left: auto; /* push icon to the right; label sits on the left */
    order: 2;
}

/* ============================================================
 * 12. Auth chrome (login / password-reset) — shared `_auth_layout`
 *
 * All five auth screens render inside a centered `.rcms-auth-card`,
 * styled with the same MD3 tokens as the dashboard. Labels, inputs
 * and their focus/hover states bind into the canonical `.rcms-field`
 * rules (M3 form fields section); submit buttons carry `.rcms-btn`.
 * Only auth-specific deltas live here. (The dead `.login-card`
 * alias — zero templates — was removed in the DRY pass.)
 * ============================================================ */

.rcms-auth-body {
    /* Auth pages have no sidebar — body becomes the centered
       container instead of a flex row. */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: var(--md-sys-color-surface-container-low);
    overflow: auto;
    height: 100vh;
}
.rcms-auth-card {
    width: 100%;
    max-width: 420px;
    background: var(--md-sys-color-surface-container-lowest);
    border: 1px solid color-mix(in srgb, var(--md-sys-color-outline-variant) 40%, transparent);
    border-radius: var(--md-sys-radius-xl);
    padding: 36px;
    box-shadow: var(--md-sys-elevation-2);
}
.rcms-auth-card h1 {
    margin: 0 0 8px;
    font: var(--md-sys-typescale-ui-header);
    font-size: 22px;
    line-height: 28px;
    color: var(--fg);
}
.rcms-auth-card .rcms-lead {
    margin: 0 0 24px;
    font-size: 13px;
    color: var(--muted);
}
.rcms-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 24px;
}
.rcms-brand-icon {
    width: 44px; height: 44px;
    border-radius: var(--md-sys-radius-lg);
    background: var(--md-sys-color-primary);
    color: var(--md-sys-color-on-primary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.rcms-brand-icon .material-symbols-rounded { font-size: 24px; vertical-align: 0; }
/* #199 — when a tenant logo/favicon is set, drop the coloured badge and show
   the image itself (like the sidebar brand logo). */
.rcms-brand-icon--img { background: none; }
.rcms-brand-icon-img { width: 44px; height: 44px; object-fit: contain; display: block; }
.rcms-brand-name {
    font: var(--md-sys-typescale-ui-header);
    color: var(--fg);
}
.rcms-brand-sub {
    font-size: 12px;
    color: var(--muted);
    margin-top: 2px;
}
/* Label + input chrome and focus/hover rings come from the canonical
   `.rcms-field` rules — only the auth layout deltas live here. */
.rcms-auth-card label { margin: 14px 0 0; }
.rcms-auth-card input[type="text"],
.rcms-auth-card input[type="password"],
.rcms-auth-card input[type="email"] {
    margin-top: 6px;
    font-size: 14px;
}
.rcms-auth-card .rcms-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 24px;
    gap: 12px;
}
/* Submit is a canonical `.rcms-btn` in the markup; auth keeps its slightly
   taller full-width shape. */
.rcms-auth-card button[type="submit"] {
    flex: 1;
    height: 44px;
}
.rcms-forgot {
    font-size: 13px;
    color: var(--muted);
    text-decoration: none;
}
.rcms-forgot:hover {
    color: var(--accent);
    text-decoration: underline;
}
.rcms-auth-card .rcms-error,
.rcms-auth-card .rcms-notice {
    margin: 16px 0 0;
    padding: 12px 16px;
    border-radius: var(--md-sys-radius-md);
    font-size: 13px;
}
.rcms-auth-card .rcms-error {
    background: var(--md-sys-color-error-container);
    border: 1px solid var(--md-sys-color-error);
    color: var(--md-sys-color-on-error-container);
}
.rcms-auth-card .rcms-notice {
    background: var(--md-sys-color-success-container);
    border: 1px solid var(--md-sys-color-success);
    color: var(--md-sys-color-on-success-container);
}
.rcms-center-cta {
    text-align: center;
    margin-top: 20px;
}
.rcms-center-cta a {
    color: var(--accent);
    text-decoration: none;
    font-weight: 500;
}
.rcms-center-cta a:hover { text-decoration: underline; }

/* ============================================================
 * 14. Page editor — extracted from `page_form.html`'s inline
 *     `<style>` block. Two-pane split (editor + live preview),
 *     tab strip, translation rows, viewport buttons, type chip.
 * ============================================================ */

/* Translation editor — side-by-side rows. */
.rcms-tr-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
    margin-bottom: 18px;
}
.rcms-tr-row .rcms-canonical {
    background: var(--md-sys-color-surface-container-low);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    padding: 10px 12px;
    font-size: 14px;
    color: var(--muted);
    white-space: pre-wrap;
    word-break: break-word;
}
.rcms-tr-row .rcms-canonical .rcms-lbl,
.rcms-tr-row label.rcms-tr-lbl {
    display: flex; align-items: center; gap: 4px;
    font: var(--md-sys-typescale-ui-label);
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--muted-2); margin-bottom: 6px;
}
.rcms-tr-row label.rcms-tr-lbl { color: var(--md-sys-color-tertiary); }
.rcms-tr-row textarea,
.rcms-tr-row input[type="text"] {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--md-sys-color-tertiary-fixed-dim);
    border-radius: var(--md-sys-radius-md);
    background: var(--md-sys-color-surface-container-lowest);
    font: inherit;
    color: var(--md-sys-color-on-surface);
    transition: border-color 0.12s, box-shadow 0.12s;
}
.rcms-tr-row textarea:focus,
.rcms-tr-row input:focus {
    outline: none;
    border-color: var(--md-sys-color-tertiary);
    box-shadow: 0 0 0 1px var(--md-sys-color-tertiary);
}
.rcms-tr-row .rcms-placeholder { color: var(--muted-2); font-style: italic; }

/* #i18n — per-leaf StreamField translation groups (incl. nested blocks). */
.rcms-tr-stream { margin-bottom: 16px; }
.rcms-tr-stream-field {
    display: flex; align-items: center; gap: 6px;
    font: var(--md-sys-typescale-label-large);
    color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em;
    margin: 10px 0 6px;
}
.rcms-tr-block {
    border-left: 2px solid var(--md-sys-color-outline-variant);
    padding-left: 12px; margin-bottom: 10px;
}
/* Nested-block indent for the stream-translation view — a bounded class
 * set instead of a computed inline style (page_form.html is locked
 * inline-style-free by admin_surface #325). Depth is clamped to 6 in the
 * template; deeper nesting shares the deepest indent. */
.rcms-tr-block--d1 { margin-left: 20px; }
.rcms-tr-block--d2 { margin-left: 40px; }
.rcms-tr-block--d3 { margin-left: 60px; }
.rcms-tr-block--d4 { margin-left: 80px; }
.rcms-tr-block--d5 { margin-left: 100px; }
.rcms-tr-block--d6 { margin-left: 120px; }
.rcms-tr-block-head {
    display: flex; align-items: center; gap: 6px;
    font-weight: 600; font-size: 13px; color: var(--fg);
    margin-bottom: 6px;
}

/* Two-pane edit experience — editor on the left, live preview on
   the right with a draggable splitter. Only mounted in edit mode;
   the "new" form is single-column. */
.rcms-edit-with-preview {
    display: flex; flex-direction: row; align-items: stretch;
    width: 100%; min-height: calc(100vh - 220px);
}
.rcms-edit-pane {
    flex: 1 1 60%; min-width: 320px;
    padding-right: 12px; overflow-x: auto;
}
/* On phones the 320px floor exceeds the content width (~304px at 360px)
 * and pushed the page sideways. Below the drawer breakpoint the pane is
 * already full-width-stacked, so drop the floor. */
@media (max-width: 768px) {
    .rcms-edit-pane { min-width: 0; padding-right: 0; }
}
/* Drop the inline 760px cap on cards inside the edit pane so the
   form expands to fill the available width. */
.rcms-edit-pane > .rcms-card,
.rcms-edit-pane > section.rcms-card,
.rcms-edit-pane > form.rcms-card { max-width: none !important; }

/* #256 — split handle: 6px visual stays narrow but the hit target
   extends ±9px via `::before` so the effective pointer area is
   24px (Material guidance). The handle aligns with the preview
   pane (`align-self: stretch` + sticky `top`) so it visually
   pairs with the right column, not the full editor stack. */
.rcms-split-handle {
    flex: 0 0 6px;
    background: var(--md-sys-color-outline-variant);
    cursor: col-resize; position: relative;
    touch-action: none; user-select: none;
    /* Sticky pairing with the preview column. Same `top` as
       `.rcms-preview-pane` (48px topbar + 40px action bar + 12px). */
    position: sticky;
    top: 100px;
    align-self: flex-start;
    height: calc(100vh - 110px);
    transition: background 0.1s;
}
/* Invisible hit-target overlay — 24px wide, centered on the visible
   handle. The pseudo-element is `pointer-events: auto` so clicks /
   drags here register on the handle even though the visual stays 6px. */
.rcms-split-handle::before {
    content: "";
    position: absolute;
    inset: 0 -9px;
    z-index: 1;
    cursor: col-resize;
}
.rcms-split-handle::after {
    content: ""; position: absolute;
    top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 2px; height: 56px; border-radius: 1px;
    background: var(--md-sys-color-outline);
    transition: background 0.1s, height 0.1s;
    z-index: 2;
    pointer-events: none;
}
.rcms-split-handle:hover {
    background: var(--md-sys-color-primary-fixed);
}
.rcms-split-handle:hover::after {
    background: var(--md-sys-color-primary);
    height: 80px;
}
.rcms-split-handle.dragging { background: var(--md-sys-color-primary-fixed); }
.rcms-split-handle.dragging::after { background: var(--md-sys-color-primary); }

.rcms-preview-pane {
    flex: 1 1 40%; min-width: 280px;
    display: flex; flex-direction: column;
    padding-left: 12px;
    position: sticky;
    /* 48px topbar + 40px action bar = 88px; +12px breathing */
    top: 100px; align-self: flex-start;
    /* #158 — full sticky-pane height: use `height` instead of
       `max-height` so the iframe inside can fill 100% of the
       available space. */
    height: calc(100vh - 110px);
}
.rcms-preview-stage {
    flex: 1 1 auto;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    background: var(--md-sys-color-surface-container-low);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-lg);
    padding: 8px;
    /* Clip, don't scroll. The iframe is the single scroll surface (its
       own document scrolls); a scrollable stage let the user pan into
       the centered gutters and fought the iframe's internal scroll
       (stuck scroll). */
    overflow: hidden;
    /* #158 — drop the 480px floor so short viewports don't clip the
       iframe. `min-height: 0` lets the flex child shrink. */
    min-height: 0;
}
/* Wagtail-style preview scaling: the sizer carries the SCALED
 * footprint (so flex layout reserves the right space + the iframe
 * appears centered), while the iframe inside keeps its DOM at the
 * device-pixel width and is `transform: scale()`-d to fit. JS
 * (`page_form.html` viewport handler) sets the inline custom
 * properties + `transform` after computing the right scale factor
 * for the current pane width. The default values fall back to
 * "full" — iframe fills the stage, no scaling. */
.rcms-preview-frame-sizer {
    width: var(--rcms-pv-sizer-w, 100%);
    height: var(--rcms-pv-sizer-h, 100%);
    /* Animate the layout collapse so device-picker clicks aren't
     * jarring. `transition: transform` lives on the iframe. */
    transition: width 0.15s ease, height 0.15s ease;
    flex: 0 0 auto;
    /* Clip the iframe's UNSCALED layout box (transform:scale shrinks
     * paint, not layout) to the scaled footprint, so a down-scaled
     * device (768/1280) doesn't overflow the stage. */
    overflow: hidden;
}
.rcms-preview-frame {
    /* DOM width — set in inline style by the JS viewport handler
     * to the chosen device width (375 / 768 / 1280 / 100%).
     * Media queries inside the iframe fire against THIS dimension,
     * matching the real device. */
    width: var(--rcms-pv-frame-w, 100%);
    height: var(--rcms-pv-frame-h, 100%);
    /* `transform-origin: top left` so the scaled iframe lines up
     * with the sizer's box. */
    transform-origin: top left;
    transform: scale(var(--rcms-pv-scale, 1));
    transition: transform 0.15s ease;
    border: 0;
    border-radius: var(--md-sys-radius-md);
    background: var(--md-sys-color-surface-container-lowest);
    box-shadow: var(--md-sys-elevation-1);
    /* No min-height: with `transform: scale()` a min-height floor
     * inflates the UNSCALED DOM box, so the browser computes a taller
     * scroll extent than is painted — the root of the stuck scroll.
     * The JS viewport handler always sets an explicit
     * `--rcms-pv-frame-h`, so the iframe can't collapse on first render. */
}

/* Viewport buttons in the action bar. */
.rcms-viewport-buttons {
    display: inline-flex; gap: 0;
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    overflow: hidden;
    margin-right: 4px;
}
.rcms-viewport-btn {
    height: 32px;
    padding: 0 8px !important;
    border-radius: 0 !important;
    border: none !important;
    border-right: 1px solid var(--md-sys-color-outline-variant) !important;
    background: transparent;
    color: var(--muted);
}
.rcms-viewport-btn:last-child { border-right: none !important; }
.rcms-viewport-btn[aria-pressed="true"] {
    background: var(--md-sys-color-primary) !important;
    color: var(--md-sys-color-on-primary) !important;
}
.rcms-viewport-btn[aria-pressed="true"] .material-symbols-rounded {
    color: var(--md-sys-color-on-primary) !important;
}

/* Mobile / narrow — stack and hide the splitter. */
@media (max-width: 1023px) {
    .rcms-edit-with-preview { flex-direction: column; min-height: auto; }
    .rcms-edit-pane { padding-right: 0; }
    .rcms-split-handle { display: none; }
    .rcms-preview-pane {
        padding-left: 0; padding-top: 16px;
        position: static; max-height: none;
        /* #158 — drop the static height on mobile so the pane
           naturally fills the column width. Iframe handles its own
           height below. */
        height: auto;
        flex: 0 0 auto;
        width: 100%;
    }
    .rcms-preview-stage {
        /* Full-width on mobile/tablet — collapse padding so the
           iframe gets the maximum horizontal space. */
        width: 100%;
        padding: 4px;
    }
    /* Force-disable scaling on narrow viewports where the editor
       stacks below the form — preview is for at-a-glance, not
       device-emulation, in that layout. The JS viewport handler
       still runs but its inline `transform` / sizer custom
       properties are overridden here. */
    .rcms-preview-frame-sizer {
        width: 100%;
        height: 60vh;
    }
    .rcms-preview-frame {
        min-height: 60vh;
        height: 60vh;
        width: 100%;
        transform: none !important;
    }
}

/* "Hide preview" state. */
[data-preview-hidden] .rcms-preview-pane,
[data-preview-hidden] .rcms-split-handle { display: none; }
[data-preview-hidden] .rcms-edit-pane { padding-right: 0; flex: 1 1 100%; }

/* Tab strip — lives inside the sticky action bar. */
.rcms-tab-strip {
    display: flex;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;
    /* Wrap to a second row rather than scroll-clipping the last tab(s). The
       phone media query switches this back to a horizontal swipe. */
    flex-wrap: wrap;
    row-gap: 4px;
    scrollbar-width: thin;
}
.rcms-tab-strip::-webkit-scrollbar { height: 6px; }
.rcms-tab-strip::-webkit-scrollbar-thumb {
    background: var(--md-sys-color-outline-variant);
    border-radius: 3px;
}
.rcms-tab-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    height: 32px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--md-sys-radius-full);
    cursor: pointer;
    font: var(--md-sys-typescale-ui-button);
    color: var(--muted);
    white-space: nowrap;
    flex: 0 0 auto;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.rcms-tab-tab:hover {
    background: var(--md-sys-color-surface-container);
    color: var(--fg);
}
.rcms-tab-tab[aria-selected="true"] {
    background: var(--md-sys-color-primary-fixed);
    color: var(--md-sys-color-on-primary-fixed);
    border-color: var(--md-sys-color-primary-fixed-dim);
}
.rcms-tab-tab .material-symbols-rounded { font-size: 16px; }
.rcms-preview-controls { display: flex; gap: 4px; flex: 0 0 auto; }
[data-preview-hidden] .rcms-preview-controls { display: none; }
.rcms-tab-tab .rcms-tab-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: var(--md-sys-radius-full);
    background: var(--md-sys-color-outline-variant);
    color: var(--muted);
    font-size: 10px;
    font-weight: 600;
    margin-left: 4px;
}
.rcms-tab-tab[aria-selected="true"] .rcms-tab-count {
    background: var(--md-sys-color-primary);
    color: var(--md-sys-color-on-primary);
}
.rcms-tab-panel[hidden] { display: none; }

/* #273 — Body content fieldset. Drop the border / padding /
   background — the StreamField inside provides its own tree
   visual; an outer card here just adds noise. Keep the legend
   styled so the section heading still anchors the tab. */
.rcms-content-fieldset {
    border: none;
    padding: 0;
    margin: 20px 0 0;
}
.rcms-content-fieldset > legend {
    padding: 0;
    margin-bottom: 8px;
    font: var(--md-sys-typescale-ui-button);
    color: var(--md-sys-color-primary);
}

/* #269 — page-type breadcrumb strip. Slim, muted, sits above the
   title field so the type reads as page-context (Wagtail-style)
   rather than a same-weight form input. The `title` attr carries
   the machine name + "permanent" note so editors can still see
   the detail on hover without it dominating the layout. */
.rcms-page-type-strip {
    display: inline-flex; align-items: center; gap: 6px;
    margin-bottom: 12px;
    padding: 4px 10px;
    background: var(--md-sys-color-surface-container-low);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-sm);
    font-size: 12px;
    color: var(--md-sys-color-on-surface-variant);
    cursor: help;
}
.rcms-page-type-strip .material-symbols-rounded {
    font-size: 14px;
    color: var(--md-sys-color-on-surface-variant);
}
.rcms-page-type-strip-name {
    font-weight: 500;
    color: var(--md-sys-color-on-surface);
}

/* Page form section banners — the small `.rcms-card` headers that wrap
   over the editor (translation notice, page-edit info). Extracted
   from inline `style="max-width: 760px; margin-bottom: 16px;"`. */
.rcms-page-form-banner {
    max-width: 760px;
    margin-bottom: 16px;
    padding: 10px 14px;
}
.rcms-page-form-banner .rcms-hint { margin: 0; }
.rcms-page-form-section {
    max-width: 760px;
}

/* Page-form notice banner — replaces inline
   `style="background: var(--accent-soft); ..."`. */
.rcms-page-form-notice {
    background: var(--md-sys-color-primary-fixed);
    border: 1px solid var(--md-sys-color-primary-fixed-dim);
    color: var(--md-sys-color-on-primary-fixed-variant);
    padding: 12px 16px;
    border-radius: var(--md-sys-radius-md);
    margin-bottom: 20px;
    font-size: 13px;
}
.rcms-page-form-notice--translation {
    background: var(--md-sys-color-tertiary-container);
    border: 1px solid var(--md-sys-color-tertiary);
    border-left-width: 4px;
    color: var(--md-sys-color-on-tertiary-container);
}

/* Revisions table header. */
.rcms-revisions-card h2 {
    margin: 0 0 12px;
    font: var(--md-sys-typescale-ui-header);
}
.rcms-revisions-card .rcms-data { margin-top: 8px; }
.rcms-revisions-card .rcms-data td.timestamp {
    color: var(--muted);
    font-size: 12px;
}
.rcms-revisions-card .rcms-data .rcms-system-author {
    color: var(--muted-2);
}

/* Page-form group headings (translation editor + custom keys
   subsection). #270 — each heading except the first carries a top
   divider so logical groups on non-Content tabs (Schedule / Tags /
   Social sharing on the Promote tab; equivalent splits elsewhere)
   read as Wagtail-style sectioned panels rather than a single jumbled
   block. */
.rcms-page-form-subheading {
    /* h3 is block-level so the border spans the full container
       width — that's what reads as the section divider. */
    display: block;
    margin: 32px 0 14px;
    padding-top: 24px;
    border-top: 2px solid var(--md-sys-color-outline);
    font: var(--md-sys-typescale-ui-label);
    color: var(--muted);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.rcms-page-form-subheading:first-of-type {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}
.rcms-page-form-subhint { margin-top: 0; }
.rcms-page-form-subscribe { display: flex; align-items: center; gap: 8px; margin: 8px 0 0; padding: 0; flex-wrap: wrap; }
/* Subscribe hint styling comes from the base `.rcms-hint` rule. */

/* #116 — accessibility checker side panel. */
.rcms-a11y-card { margin-bottom: 16px; padding: 12px 16px; }
.rcms-a11y-summary { cursor: pointer; font-weight: 500; display: flex; align-items: center; gap: 8px; }
.rcms-a11y-list { list-style: none; padding: 0; margin: 12px 0 0; }
.rcms-a11y-row { display: flex; align-items: flex-start; gap: 8px; padding: 8px 0; border-top: 1px solid var(--md-sys-color-outline-variant); font-size: 13px; }
.rcms-a11y-row:first-child { border-top: none; }
.rcms-a11y-row--error .material-symbols-rounded { color: var(--md-sys-color-error); }
.rcms-a11y-row--warning .material-symbols-rounded { color: #b8860b; }
.rcms-a11y-row--info .material-symbols-rounded { color: var(--md-sys-color-on-surface-variant); }
.rcms-a11y-msg { flex: 1; }
.rcms-a11y-field { font-size: 11px; padding: 2px 6px; background: var(--md-sys-color-surface-container); border-radius: var(--md-sys-radius-sm); color: var(--md-sys-color-on-surface-variant); }
.rcms-a11y-footer { margin: 12px 0 0; font-size: 11px; }
.rcms-a11y-tag-error { background: var(--md-sys-color-error-container); color: var(--md-sys-color-on-error-container); font-size: 10px; margin-left: 4px; }
.rcms-a11y-tag-warning { background: #fff4cc; color: #5a4500; font-size: 10px; margin-left: 4px; }
.rcms-a11y-tag-info { background: var(--md-sys-color-surface-container); color: var(--md-sys-color-on-surface-variant); font-size: 10px; margin-left: 4px; }

/* #117 — InlinePanel: 1-N sub-row collections on the page editor. */
.rcms-inline-panel { margin-top: 16px; }
.rcms-inline-panel legend { display: flex; align-items: center; gap: 8px; }
.rcms-inline-panel-cap { font-size: 11px; color: var(--md-sys-color-on-surface-variant); margin-left: 4px; }
.rcms-inline-panel-help { margin: 0 0 8px; font-size: 12px; }
.rcms-inline-panel-rows { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.rcms-inline-panel-row { padding: 12px; background: var(--md-sys-color-surface-container-low); border-radius: var(--md-sys-radius-md); }
.rcms-inline-panel-row-header,
.rcms-inline-panel-list-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.rcms-inline-panel-row-header strong { font-size: 12px; color: var(--md-sys-color-on-surface-variant); }
.rcms-inline-panel-row-header .rcms-inline-panel-remove { margin-left: auto; }

/* Inline-panel list — all panels share one fieldset, each panel is a list item */
.rcms-inline-panel-card { margin-top: 16px; }
/* Shared vertical list-reset stack (also `.rcms-dashboard-list` and
   `.rcms-upload-queue` — their sections keep only their own gap). */
.rcms-inline-panel-list,
.rcms-dashboard-list,
.rcms-upload-queue { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.rcms-inline-panel-list { gap: 16px; }
.rcms-inline-panel-list-item { padding: 12px; background: var(--md-sys-color-surface-container-lowest); border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--md-sys-radius-md); }
.rcms-inline-panel-list-item + .rcms-inline-panel-list-item { margin-top: 0; }
/* `.rcms-inline-panel-list-head` layout is grouped with `.rcms-inline-panel-row-header`. */
.rcms-inline-panel-list-head strong { font-size: 14px; }

/* #121 — keyboard-shortcuts cheat-sheet modal. */
.rcms-kbd-modal { border: 0; border-radius: var(--md-sys-radius-lg); padding: 0; max-width: 480px; width: 90vw; background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); box-shadow: var(--md-sys-elevation-3); }
.rcms-kbd-modal::backdrop { background: rgba(0, 0, 0, 0.4); }
.rcms-kbd-modal article { padding: 16px 20px; }
.rcms-kbd-modal header { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.rcms-kbd-modal header h2 { flex: 1; margin: 0; font-size: 16px; }
.rcms-kbd-modal h3 { font-size: 12px; color: var(--md-sys-color-on-surface-variant); text-transform: uppercase; letter-spacing: 0.05em; margin: 12px 0 6px; }
.rcms-kbd-modal dl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; margin: 0; font-size: 13px; }
.rcms-kbd-modal dt { display: flex; align-items: center; gap: 4px; }
.rcms-kbd-modal kbd { display: inline-block; padding: 2px 6px; border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--md-sys-radius-sm); background: var(--md-sys-color-surface-container); font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 11px; }

/* #122 — revision storage report prune form. */
.rcms-revisions-prune-form { margin-bottom: 16px; padding: 16px; }
.rcms-revisions-prune-heading { margin: 0 0 12px; font-size: 14px; }

/* #123 — snippet history card. */
.rcms-snippet-history-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-top: 1px solid var(--md-sys-color-outline-variant); }
.rcms-snippet-history-row:first-child { border-top: none; }
.rcms-snippet-history-row-text { flex: 1; font-size: 13px; }

/* #128 — plugin-contributed account settings panels. */
.rcms-account-plugin-panel { margin-top: 16px; padding: 16px; }
.rcms-account-plugin-panel-heading { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: 16px; }

/* #129 — styleguide / component gallery. */
.rcms-styleguide-section { margin: 24px 0 8px; font-size: 16px; }
.rcms-styleguide-card { padding: 16px; }
.rcms-styleguide-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 12px; }
.rcms-styleguide-row:last-child { margin-bottom: 0; }
.rcms-styleguide-tag-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 4px; font-size: 13px; }

/* #133 — per-user column picker. Tables opt in with
   `data-col-picker="<list-name>"`; JS appends a Columns popover
   that toggles `col-hidden` on the matching th + td cells. */
.rcms-col-hidden { display: none !important; }
.rcms-col-picker { display: inline-block; position: relative; margin: 0 0 8px; }
/* Summary reset comes from the shared <details> menu rule (kebab section). */
.rcms-col-picker-panel { display: none; position: absolute; right: 0; top: calc(100% + 4px); min-width: 220px; padding: 8px; background: var(--md-sys-color-surface-container-high); border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--md-sys-radius-md); box-shadow: var(--md-sys-elevation-2); z-index: 8; }
.rcms-col-picker[open] .rcms-col-picker-panel { display: flex; flex-direction: column; gap: 4px; }
.rcms-col-picker-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; font-size: 13px; cursor: pointer; border-radius: var(--md-sys-radius-sm); }
.rcms-col-picker-row:hover { background: var(--md-sys-color-surface-container); }
.rcms-col-picker-reset { margin-top: 4px; align-self: flex-start; }
/* The snippet listing is a bare `table.rcms-data` like every other admin
   list — its own outline + radius, no card shadow wrapped around it.
   The toolbar above it just parks the Columns picker on the right;
   it collapses when the picker script hasn't mounted anything. */
.rcms-library-list-toolbar { display: flex; justify-content: flex-end; margin-bottom: 8px; }
.rcms-library-list-toolbar:empty { display: none; }
.rcms-library-list-bulk-th { width: 32px; }

/* #147 — autosave chip. Floats bottom-right; mode-driven palette
   (saving / idle / warn). Polite-live for screen readers. */
.rcms-autosave-chip {
    position: fixed;
    right: 16px; bottom: 16px;
    padding: 6px 12px;
    background: var(--md-sys-color-surface-container-high);
    color: var(--md-sys-color-on-surface-variant);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-full);
    box-shadow: var(--md-sys-elevation-1);
    font: var(--md-sys-typescale-ui-label);
    z-index: 5;
    opacity: 0;
    transition: opacity 0.18s;
    pointer-events: none;
}
.rcms-autosave-chip[data-mode="saving"],
.rcms-autosave-chip[data-mode="idle"],
.rcms-autosave-chip[data-mode="warn"] {
    opacity: 1;
}
/* Warn fill lives in the shared semantic fills (tag section). */

/* #144 — Dashboard home grid. Two-column on desktop, single-column
   below 760px. The .rcms-dashboard-span-2 modifier lets a card claim the
   full grid width (used for the Recent edits feed). */
.rcms-dashboard-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
}
.rcms-dashboard-card { padding: 16px 20px; }
.rcms-dashboard-card h2 {
    display: flex; align-items: center; gap: 8px;
    margin: 0 0 12px;
    font-size: 14px;
}
.rcms-dashboard-card h2 .material-symbols-rounded { color: var(--md-sys-color-primary); }
.rcms-dashboard-span-2 { grid-column: 1 / -1; }
.rcms-dashboard-counts {
    list-style: none; padding: 0; margin: 0 0 12px;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 12px;
}
.rcms-dashboard-counts li {
    display: flex; flex-direction: column;
    padding: 12px;
    background: var(--md-sys-color-surface-container);
    border-radius: var(--md-sys-radius-md);
}
.rcms-dashboard-count { font-size: 22px; font-weight: 600; line-height: 1.1; }
.rcms-dashboard-count-label { font-size: 11px; color: var(--md-sys-color-on-surface-variant); text-transform: uppercase; letter-spacing: 0.04em; }
/* `.rcms-dashboard-list` base comes from the shared list-reset stack
   (inline-panel section). */
.rcms-dashboard-list li {
    display: flex; flex-direction: column;
    padding: 8px 0;
    border-top: 1px solid var(--md-sys-color-outline-variant);
}
.rcms-dashboard-list li:first-child { border-top: none; }
.rcms-dashboard-list a { color: var(--md-sys-color-on-surface); text-decoration: none; }
.rcms-dashboard-list a:hover { color: var(--md-sys-color-primary); }
.rcms-dashboard-list code.rcms-hint { font-size: 11px; margin-left: 8px; }
@media (max-width: 760px) {
    .rcms-dashboard-grid { grid-template-columns: 1fr; }
    .rcms-dashboard-span-2 { grid-column: 1; }
}

/* #135 — side-panel rail. Sticky vertical toolbar on the right edge
   with one button per side panel (Comments, A11y, History, Status).
   Buttons scroll to + open the matching `<details>` block; the
   matching tab activates if `data-side-rail-tab` is set. Mobile
   collapses (hidden below 1100px). */
.rcms-side-panel-rail {
    position: fixed;
    right: 16px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 8px 6px;
    background: var(--md-sys-color-surface-container);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-full);
    box-shadow: var(--md-sys-elevation-1);
    z-index: 6;
}
.rcms-side-rail-btn {
    position: relative;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    color: var(--md-sys-color-on-surface-variant);
    transition: background 0.12s, color 0.12s;
}
.rcms-side-rail-btn:hover { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }
/* `.rcms-side-rail-btn.is-active` fill comes from the shared active-tool rule
   (minimap section). */
/* #324 — badge family note. The admin's small count/label indicators —
   `.rcms-side-rail-badge` (absolute count overlay, here), `.rcms-tab-count` (inline
   count inside a tab), `.rcms-locale-badge` (breadcrumb locale pill),
   `.rcms-canonical-tag` (locale-menu marker pill), and `.a11y-tag-*` (a11y-panel
   severity tags) — are intentionally SEPARATE, single-use, context-scoped
   elements, NOT one component behind competing class APIs. Each already has
   exactly one canonical class; they were reviewed for the #324 "one API per
   component" pass and deliberately NOT unified under a single `.badge` base —
   doing so would couple unrelated UI (an absolute notification overlay vs an
   inline count vs uppercase label pills) for no real dedup. Add new context
   badges as their own scoped class rather than overloading these. */
.rcms-side-rail-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    font-size: 10px;
    line-height: 16px;
    text-align: center;
    background: var(--md-sys-color-primary);
    color: var(--md-sys-color-on-primary);
    border-radius: 8px;
}
.rcms-side-rail-badge--error { background: var(--md-sys-color-error); color: var(--md-sys-color-on-error); }
.rcms-side-rail-badge--warning { background: var(--md-sys-color-warning); color: var(--md-sys-color-on-warning); }
.rcms-side-panel-flash { animation: side-panel-flash-keyframes 1.2s ease-out; }
@keyframes side-panel-flash-keyframes {
    0% { box-shadow: 0 0 0 0 var(--md-sys-color-primary); }
    50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--md-sys-color-primary) 35%, transparent); }
    100% { box-shadow: 0 0 0 0 transparent; }
}
@media (max-width: 1100px) {
    .rcms-side-panel-rail { display: none; }
}

/* #415 — section minimap / outline. Left-edge collapsible toggle + panel
   (mirror of the right-side rail), JS-injected by `wireSectionMinimap`. */
.rcms-section-minimap {
    position: fixed;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    z-index: 6;
    display: flex;
    align-items: flex-start;
    gap: 8px;
}
.rcms-section-minimap-toggle {
    flex: none;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--md-sys-color-surface-container-lowest);
    color: var(--md-sys-color-on-surface-variant);
    cursor: pointer;
    box-shadow: var(--md-sys-elevation-1);
}
.rcms-section-minimap-toggle:hover {
    color: var(--md-sys-color-primary);
    border-color: var(--md-sys-color-primary);
}
/* Expanded fill comes from the shared active-tool rule below. */
.rcms-section-minimap-panel {
    width: 240px;
    max-height: 70vh;
    overflow-y: auto;
    padding: 8px;
    border: 1px solid var(--border);
    border-radius: var(--md-sys-radius-l, 12px);
    background: var(--md-sys-color-surface-container-lowest);
    box-shadow: var(--md-sys-elevation-3);
    display: flex;
    flex-direction: column;
    gap: 1px;
}
/* The `hidden` attribute must beat the `display: flex` above: an author
   `display` rule on the class outweighs the UA `[hidden] { display: none }`
   default, so without this the (often empty) panel stays visible on the left
   edge even while "hidden". Mirrors `.rcms-tab-panel[hidden]` above. */
.rcms-section-minimap-panel[hidden] { display: none; }
.rcms-section-minimap-entry {
    display: block;
    padding: 4px 8px;
    border-radius: var(--md-sys-radius-s, 6px);
    font-size: 0.8125rem;
    color: var(--md-sys-color-on-surface);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.rcms-section-minimap-entry:hover {
    background: var(--md-sys-color-surface-container-high);
}
.rcms-section-minimap-entry--tab,
.rcms-section-minimap-entry--tab-active {
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.6875rem;
    color: var(--md-sys-color-on-surface-variant);
    margin-top: 6px;
}
.rcms-section-minimap-entry--tab-active {
    color: var(--md-sys-color-primary);
}
.rcms-section-minimap-entry--section { padding-left: 16px; }
.rcms-section-minimap-entry--block { padding-left: 24px; color: var(--md-sys-color-on-surface-variant); }
@media (max-width: 1100px) {
    .rcms-section-minimap { display: none; }
}

/* ------------------------------------------------------------
 * Shared active-tool fill (DRY): every "this tool/entry is the
 * active one" state uses the same primary-container pair. Placed
 * AFTER each member's own `:hover` rule (equal specificity) so an
 * active tool keeps its fill while hovered.
 * ------------------------------------------------------------ */
.rcms-richtext-tool.is-active,
.rcms-side-rail-btn.is-active,
.rcms-section-minimap-toggle[aria-expanded="true"],
.rcms-section-minimap-entry.is-active {
    background: var(--md-sys-color-primary-container);
    color: var(--md-sys-color-on-primary-container);
}

/* ============================================================
 * 13. Mobile drawer
 * ============================================================ */

@media (max-width: 768px) {
    body { flex-direction: column; }
    .rcms-sidebar {
        position: fixed;
        width: 240px;
        z-index: 10;
        transform: translateX(-100%);
        transition: transform 0.2s ease;
        box-shadow: var(--md-sys-elevation-3);
    }
    input#__nav_toggle:checked ~ .rcms-sidebar { transform: translateX(0); }
    .rcms-app-main { width: 100%; }
    .rcms-nav-toggle-label {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 36px;
        height: 36px;
        border-radius: var(--md-sys-radius-md);
        cursor: pointer;
        color: var(--muted);
    }
    .rcms-nav-toggle-label:hover { background: var(--md-sys-color-surface-container); }
    .rcms-nav-backdrop {
        display: none;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.4);
        z-index: 9;
    }
    input#__nav_toggle:checked ~ .rcms-nav-backdrop { display: block; }
}
@media (min-width: 769px) {
    .rcms-nav-toggle-label, .rcms-nav-backdrop, #__nav_toggle { display: none; }
}
#__nav_toggle { position: absolute; left: -9999px; }

/* ============================================================
 * #25 — Modal confirm + toast notifications.
 *
 * `<dialog class="rcms-confirm-dialog">` is the native HTML5
 * element promoted to modal via showModal(). Backdrop styling via
 * ::backdrop. Layout: title + message + actions row.
 *
 * `.rcms-toast-stack` sits fixed bottom-right. Each toast is a
 * card with a leading icon, body text, and a dismiss button.
 * In/out transitions via the `--in` / `--out` modifier classes
 * the JS adds. Per-level colors pull from the same MD3 surface
 * palette the rest of the chrome uses.
 * ============================================================ */

.rcms-confirm-dialog {
    border: none;
    border-radius: var(--md-sys-radius-xl);
    background: var(--md-sys-color-surface-container-lowest);
    color: var(--md-sys-color-on-surface);
    box-shadow: var(--md-sys-elevation-3);
    padding: 0;
    max-width: 460px;
    width: calc(100% - 32px);
}
.rcms-confirm-dialog::backdrop {
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(2px);
}
.rcms-confirm-form {
    padding: 24px;
}
.rcms-confirm-form h2 {
    margin: 0 0 12px;
    font: var(--md-sys-typescale-ui-header);
    color: var(--md-sys-color-on-surface);
}
.rcms-confirm-form p {
    margin: 0 0 20px;
    font: var(--md-sys-typescale-ui-body);
    color: var(--md-sys-color-on-surface-variant);
}
.rcms-confirm-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    align-items: center;
}

.rcms-toast-stack {
    position: fixed;
    right: 20px;
    bottom: 20px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    z-index: 1000;
    max-width: min(420px, calc(100vw - 40px));
    pointer-events: none;
}
.rcms-toast {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    background: var(--md-sys-color-inverse-surface);
    color: var(--md-sys-color-inverse-on-surface);
    border-radius: var(--md-sys-radius-lg);
    box-shadow: var(--md-sys-elevation-3);
    font: var(--md-sys-typescale-ui-body);
    pointer-events: auto;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.rcms-toast--in { opacity: 1; transform: translateY(0); }
.rcms-toast--out { opacity: 0; transform: translateY(8px); }
.rcms-toast-icon { font-size: 20px; flex-shrink: 0; }
.rcms-toast-body { flex: 1; line-height: 1.4; }
.rcms-toast-close {
    background: transparent;
    border: 0;
    color: inherit;
    cursor: pointer;
    padding: 4px;
    border-radius: var(--md-sys-radius-sm);
    opacity: 0.7;
}
.rcms-toast-close:hover { opacity: 1; background: rgba(255, 255, 255, 0.08); }
/* #324 — per-level toasts on md-sys tokens (was hardcoded dark hex that
   ignored theming + dark mode — the #293 disconnect for the status surface
   users actually see, since flash messages hoist to toasts on load). Keeps
   the dark toast identity via the inverted container pair: `on-<level>-
   container` (dark) as the surface + `<level>-container` (light) as the
   text/icon — a designed contrast pair, now theme-aware. `--info` keeps the
   neutral inverse-surface base with a primary-tinted icon. */
.rcms-toast--success { background: var(--md-sys-color-on-success-container); color: var(--md-sys-color-success-container); }
.rcms-toast--warning { background: var(--md-sys-color-on-warning-container); color: var(--md-sys-color-warning-container); }
.rcms-toast--error { background: var(--md-sys-color-on-error-container); color: var(--md-sys-color-error-container); }
.rcms-toast--info .rcms-toast-icon { color: var(--md-sys-color-inverse-primary); }

/* ============================================================
 * #28 — MD3 input restyling for checkbox / radio / file / range
 * and the standalone date/time inputs that aren't inside `.rcms-field`.
 *
 * The `.rcms-field input[type=text|date|…]` rule above covers form-row
 * cases. Filter bars (media list, page list) use bare inputs
 * outside `.rcms-field`; this rule gives them the same MD3 chrome.
 *
 * `::-webkit-calendar-picker-indicator` paints the native browser
 * calendar/clock icon; we tint it to match the on-surface palette
 * so it doesn't look glued on.
 * ============================================================ */

/* #314 — scope the standalone bare form-control chrome (date/time/file/
   color/range inputs, the MD3 checkbox+radio, standalone <select>) to the
   admin shell, stopping at inline user-content wrappers, so a user's
   stored <input>/<select> in a rich-text / stream / block preview renders
   with native UA controls instead of admin chrome. Rules are kept at their
   original indentation to keep the diff to the @scope wrapper itself. */
@scope (.rcms-admin) to (.rcms-block-paragraph, .rcms-block-rich-text, .rcms-block-raw-html, .rcms-block-snippet-chooser, .rcms-block-picker-tile-preview, .rcms-stream-block, .rcms-display-chip-value) {
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"],
input[type="week"] {
    font: var(--md-sys-typescale-ui-body);
    color: var(--md-sys-color-on-surface);
    background: var(--md-sys-color-surface-container);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-lg);
    padding: 0 12px;
    height: 40px;
    min-height: 40px;
    box-sizing: border-box;
    transition: border-color 0.12s, box-shadow 0.12s;
}
input[type="date"]:hover,
input[type="time"]:hover,
input[type="datetime-local"]:hover,
input[type="month"]:hover,
input[type="week"]:hover,
select:not(.rcms-field select):hover {
    border-color: var(--md-sys-color-outline);
}
input[type="date"]:focus,
input[type="time"]:focus,
input[type="datetime-local"]:focus,
input[type="month"]:focus,
input[type="week"]:focus,
select:not(.rcms-field select):focus {
    outline: none;
    border-color: var(--md-sys-color-primary);
    box-shadow: 0 0 0 1px var(--md-sys-color-primary);
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    opacity: 0.65;
    cursor: pointer;
    transition: opacity 0.12s;
}
input[type="date"]:hover::-webkit-calendar-picker-indicator,
input[type="time"]:hover::-webkit-calendar-picker-indicator,
input[type="datetime-local"]:hover::-webkit-calendar-picker-indicator {
    opacity: 1;
}

/* MD3 checkbox + radio. The native input is hidden via opacity:0
 * (kept clickable for keyboard a11y) and the ::before / ::after on
 * the parent label render the visible shape. We keep the bare
 * `input[type="checkbox"]` selector unscoped so legacy templates
 * pick up the styling without needing a wrapper class. */
input[type="checkbox"],
input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;
    width: 18px;
    height: 18px;
    border: 1.5px solid var(--md-sys-color-outline);
    background: transparent;
    cursor: pointer;
    position: relative;
    flex-shrink: 0;
    transition: border-color 0.12s, background-color 0.12s;
    vertical-align: middle;
}
input[type="checkbox"] {
    border-radius: 3px;
}
input[type="radio"] {
    border-radius: 50%;
}
input[type="checkbox"]:hover,
input[type="radio"]:hover {
    border-color: var(--md-sys-color-on-surface);
}
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
    outline: 2px solid var(--md-sys-color-primary);
    outline-offset: 2px;
}
input[type="checkbox"]:checked {
    background: var(--md-sys-color-primary);
    border-color: var(--md-sys-color-primary);
}
input[type="checkbox"]:checked::after {
    content: "";
    position: absolute;
    left: 4px;
    top: 0px;
    width: 6px;
    height: 11px;
    border: solid var(--md-sys-color-on-primary);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}
input[type="radio"]:checked {
    border-color: var(--md-sys-color-primary);
}
input[type="radio"]:checked::after {
    content: "";
    position: absolute;
    inset: 3px;
    border-radius: 50%;
    background: var(--md-sys-color-primary);
}
input[type="checkbox"]:disabled,
input[type="radio"]:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* File input. The native button is restyled; the file-name
 * indicator carries inherited text colour. */
input[type="file"] {
    font: var(--md-sys-typescale-ui-body);
    color: var(--md-sys-color-on-surface-variant);
    border: 1px dashed var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    padding: 10px 12px;
    background: var(--md-sys-color-surface-container-low);
    cursor: pointer;
    max-width: 100%;
}
input[type="file"]:hover {
    border-color: var(--md-sys-color-primary);
}
input[type="file"]::file-selector-button {
    font: var(--md-sys-typescale-ui-button);
    color: var(--md-sys-color-on-primary);
    background: var(--md-sys-color-primary);
    border: none;
    border-radius: var(--md-sys-radius-lg);
    padding: 6px 14px;
    margin-right: 12px;
    cursor: pointer;
    transition: background-color 0.12s;
}
input[type="file"]::file-selector-button:hover {
    background: color-mix(in srgb, var(--md-sys-color-primary) 88%, black);
}

/* Color swatch. Native `input[type=color]` is a small button; we
 * shape it into a 32x32 rounded swatch. */
input[type="color"] {
    appearance: none;
    -webkit-appearance: none;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    cursor: pointer;
    background: transparent;
}
input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
input[type="color"]::-webkit-color-swatch { border: none; border-radius: 4px; }

/* Range slider — primary thumb on outline-variant track. */
input[type="range"] {
    appearance: none;
    -webkit-appearance: none;
    width: 100%;
    height: 4px;
    background: var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-full);
    outline: none;
    cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
    appearance: none;
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--md-sys-color-primary);
    cursor: pointer;
    border: none;
}
input[type="range"]::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--md-sys-color-primary);
    cursor: pointer;
    border: none;
}

/* Standalone <select> outside `.rcms-field` — restyle to match the
 * date-input chrome above so filter bars don't drop into native
 * OS chrome. Native chevron is hidden; we draw our own via a
 * background SVG masked by `--md-sys-color-on-surface-variant`. */
select:not(.rcms-field select) {
    font: var(--md-sys-typescale-ui-body);
    color: var(--md-sys-color-on-surface);
    background-color: var(--md-sys-color-surface-container);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    padding: 8px 32px 8px 10px;
    min-height: 36px;
    box-sizing: border-box;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23757688'><path d='M7 10l5 5 5-5z'/></svg>");
    background-repeat: no-repeat;
    background-position: right 8px center;
    background-size: 18px 18px;
    cursor: pointer;
}
/* Hover/focus rings are grouped with the date-input rules above. */
} /* end @scope .rcms-admin (#314 — standalone form controls) */

/* ============================================================
 * #28 — Media card uniform image presentation.
 *
 * Cards shipped with `background: surface-container` + a fixed
 * 140px height + `object-fit: cover` which forced upscale of
 * tiny images into a beige noise wash. New shape:
 *   - Aspect-ratio: 1 (square) so the grid stays uniform.
 *   - `object-fit: contain` so small images sit centered without
 *     blowing up. Cover only when the source is at least the
 *     thumb's natural size (handled via inline `data-fit`).
 *   - Subtle background checkerboard suggests transparency for
 *     PNG / SVG sources.
 *   - Hover lifts elevation a notch so the gallery reads as
 *     interactive.
 * Templates apply `.rcms-media-card-thumb` to the wrapper div + opt
 * in to `data-fit="cover"` when the source dims fill the thumb.
 * ============================================================ */

.rcms-media-card-thumb {
    aspect-ratio: 1 / 1;
    border-radius: var(--md-sys-radius-lg);
    background:
        linear-gradient(45deg, color-mix(in srgb, var(--md-sys-color-outline-variant) 18%, transparent) 25%, transparent 25%),
        linear-gradient(-45deg, color-mix(in srgb, var(--md-sys-color-outline-variant) 18%, transparent) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, color-mix(in srgb, var(--md-sys-color-outline-variant) 18%, transparent) 75%),
        linear-gradient(-45deg, transparent 75%, color-mix(in srgb, var(--md-sys-color-outline-variant) 18%, transparent) 75%),
        var(--md-sys-color-surface-container-low);
    background-size: 16px 16px;
    background-position: 0 0, 0 8px, 8px -8px, -8px 0;
    overflow: hidden;
    position: relative;
    box-shadow: var(--md-sys-elevation-1);
    transition: box-shadow 0.12s;
    margin-bottom: 8px;
}
.rcms-media-card-thumb:hover { box-shadow: var(--md-sys-elevation-2); }
.rcms-media-card-thumb img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 12px;
    box-sizing: border-box;
    display: block;
}
.rcms-media-card-thumb[data-fit="cover"] img {
    object-fit: cover;
    padding: 0;
}

/* ============================================================
 * #44 — WP-style menu builder. Two-column layout: picker on the
 * left, draggable tree on the right.
 *
 * Tree depth is driven by `--menu-builder-depth` (set per-card by
 * the JS); the card's left padding scales with depth so the
 * indent reads visually without nesting in the DOM.
 * ============================================================ */

.rcms-menu-builder {
    display: grid;
    grid-template-columns: 320px 1fr;
    gap: 16px;
    align-items: start;
}
@media (max-width: 900px) {
    .rcms-menu-builder { grid-template-columns: 1fr; }
}

.rcms-menu-builder-picker { padding: 16px; }
.rcms-menu-builder-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.rcms-menu-builder-tab {
    background: transparent;
    border: 0;
    padding: 8px 12px;
    cursor: pointer;
    font: var(--md-sys-typescale-ui-button);
    color: var(--md-sys-color-on-surface-variant);
    border-bottom: 2px solid transparent;
}
.rcms-menu-builder-tab.active {
    color: var(--md-sys-color-primary);
    border-bottom-color: var(--md-sys-color-primary);
}
.rcms-menu-builder-page-search {
    width: 100%;
    margin-bottom: 8px;
}
.rcms-menu-builder-page-list {
    max-height: 60vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.rcms-menu-builder-picker-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    background: var(--md-sys-color-surface-container-low);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    cursor: grab;
    font: var(--md-sys-typescale-ui-body);
    color: var(--md-sys-color-on-surface);
    text-align: left;
    transition: background-color 0.12s, border-color 0.12s;
}
.rcms-menu-builder-picker-item:hover {
    background: var(--md-sys-color-surface-container);
    border-color: var(--md-sys-color-primary);
}
.rcms-menu-builder-picker-item:active { cursor: grabbing; }
.rcms-menu-builder-picker-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rcms-menu-builder-picker-add {
    font-size: 18px;
    line-height: 1;
    color: var(--md-sys-color-primary);
    font-weight: 600;
    opacity: 0;
    transition: opacity 0.12s;
}
.rcms-menu-builder-picker-item:hover .rcms-menu-builder-picker-add { opacity: 1; }

.rcms-menu-builder-tree-wrap { padding: 16px; }
.rcms-menu-builder-tree-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
}
.rcms-menu-builder-dirty {
    font-size: 12px;
    color: var(--md-sys-color-tertiary);
    background: var(--md-sys-color-tertiary-container);
    padding: 4px 8px;
    border-radius: var(--md-sys-radius-full);
}
.rcms-menu-builder-tree {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-height: 80px;
}
.rcms-menu-builder-card {
    --depth: var(--menu-builder-depth, 0);
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    margin-left: calc(var(--depth) * 28px);
    background: var(--md-sys-color-surface-container-lowest);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    box-shadow: var(--md-sys-elevation-1);
    transition: box-shadow 0.12s, opacity 0.12s, transform 0.12s;
}
.rcms-menu-builder-card:hover { box-shadow: var(--md-sys-elevation-2); }
.rcms-menu-builder-card--dragging { opacity: 0.4; }
.rcms-menu-builder-drop-target {
    border-color: var(--md-sys-color-primary);
    border-style: dashed;
}
.rcms-menu-builder-handle {
    color: var(--md-sys-color-outline);
    cursor: grab;
    font-size: 20px;
}
.rcms-menu-builder-card-body {
    flex: 1;
    min-width: 0;
}
.rcms-menu-builder-card-label {
    font: var(--md-sys-typescale-ui-button);
    color: var(--md-sys-color-on-surface);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Shared truncated meta line (also `.rcms-upload-queue-meta` and
   `.rcms-picker-row__sub` — deltas live at their sites). */
.rcms-menu-builder-card-target,
.rcms-upload-queue-meta,
.rcms-picker-row__sub {
    font-size: 11px;
    color: var(--md-sys-color-on-surface-variant);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rcms-menu-builder-card-target { margin-top: 2px; }
.rcms-menu-builder-card-target code {
    background: transparent;
    color: inherit;
    font-size: 11px;
}
.rcms-menu-builder-card-actions {
    display: flex;
    gap: 2px;
    flex-shrink: 0;
}
.rcms-menu-builder-empty {
    border: 2px dashed var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    padding: 32px;
    text-align: center;
    color: var(--md-sys-color-on-surface-variant);
}

/* #49 — menu-builder inline disclosure. Hidden by default; click
 * the card body to expand. The panel sits BELOW the card-actions
 * row (after the flex children) so the card stays as a tidy strip
 * until opened. */
.rcms-menu-builder-card { flex-wrap: wrap; }
.rcms-menu-builder-card .rcms-menu-builder-card-disclosure {
    flex-basis: 100%;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--md-sys-color-outline-variant);
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.rcms-menu-builder-card .rcms-menu-builder-card-disclosure .rcms-field {
    margin-bottom: 0;
}
.rcms-menu-builder-card .rcms-menu-builder-card-disclosure .rcms-checkbox-field {
    gap: 8px;
    font: var(--md-sys-typescale-ui-body);
    color: var(--md-sys-color-on-surface);
}
.rcms-menu-builder-card.expanded {
    box-shadow: var(--md-sys-elevation-2);
}
.rcms-menu-builder-card [data-toggle-disclosure] { cursor: pointer; }

/* #49b — drag-to-indent preview. While the editor drags a card
 * horizontally past the indent threshold the card slides to its
 * would-be depth via `--menu-builder-depth`. The preview class adds
 * a slight outline tint so the indent decision reads visually
 * before drop. */
.rcms-menu-builder-card--indent-preview {
    outline: 2px dashed var(--md-sys-color-primary);
    outline-offset: -2px;
    transition: margin-left 0.08s ease, outline-color 0.12s;
}

/* #49c — recent-tab picker items carry a relative-time `<time>`
 * chip after the label. Sits between the label and the `+` add
 * indicator, muted so the page title stays the primary text. */
.rcms-menu-builder-picker-meta {
    font-size: 10px;
    color: var(--md-sys-color-on-surface-variant);
    background: var(--md-sys-color-surface-container-high);
    padding: 2px 6px;
    border-radius: var(--md-sys-radius-full);
    margin-left: 4px;
}

/* ============================================================
 * #57 — Bulk media upload — drop zone + per-file queue.
 * The dropzone is a dashed-border drop area that wraps the file
 * input; the queue is a vertical stack of rows (thumb / inputs /
 * status / remove).
 * ============================================================ */

.rcms-upload-dropzone {
    position: relative;
    border: 2px dashed var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-lg);
    background: var(--md-sys-color-surface-container-low);
    padding: 24px;
    text-align: center;
    transition: border-color 0.12s, background-color 0.12s;
}
.rcms-upload-dropzone:hover,
.rcms-upload-dropzone--active {
    border-color: var(--md-sys-color-primary);
    background: color-mix(in srgb, var(--md-sys-color-primary) 4%, var(--md-sys-color-surface-container-low));
}
/* The file input is a hidden, NON-interactive overlay: a bare `<input
   type=file>` only opens the picker when its native "browse" sub-button is
   clicked, so covering the dropzone with it leaves most of the area dead.
   Instead we make it pointer-events:none and let the dropzone's JS click
   handler call `fileInput.click()` — reliable from anywhere in the zone. */
.rcms-upload-dropzone input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    pointer-events: none;
    width: 100%;
    height: 100%;
}
.rcms-upload-dropzone { cursor: pointer; }
.rcms-upload-dropzone-hint { pointer-events: none; }

/* `.rcms-upload-queue` base comes from the shared list-reset stack
   (inline-panel section). */
.rcms-upload-queue-row {
    display: grid;
    grid-template-columns: 60px 1fr auto auto;
    gap: 12px;
    align-items: center;
    padding: 10px 12px;
    background: var(--md-sys-color-surface-container-lowest);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
}
.rcms-upload-queue-thumb {
    width: 60px;
    height: 60px;
    border-radius: var(--md-sys-radius-sm);
    background: var(--md-sys-color-surface-container);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--md-sys-color-on-surface-variant);
}
.rcms-upload-queue-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.rcms-upload-queue-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.rcms-upload-queue-body input[type="text"] {
    width: 100%;
    padding: 6px 8px;
    font: var(--md-sys-typescale-ui-body);
    background: var(--md-sys-color-surface-container);
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-sm);
}
/* `.rcms-upload-queue-meta` comes from the shared truncated-meta rule
   (menu-builder section). */
.rcms-upload-queue-status {
    font-size: 12px;
    color: var(--md-sys-color-on-surface-variant);
    min-width: 80px;
    text-align: right;
}
.rcms-upload-queue-row[data-status="uploading"] .rcms-upload-queue-status {
    color: var(--md-sys-color-primary);
}
.rcms-upload-queue-row[data-status="done"] .rcms-upload-queue-status {
    color: #047857;
}
.rcms-upload-queue-row[data-status="failed"] .rcms-upload-queue-status {
    color: var(--md-sys-color-error);
}

/* ============================================================
 * #58 — Theme picker on /cms-admin/settings. Grid of swatch tiles
 * — one form per tile so the click submits a POST. The active tile
 * outlines in primary; the others outline in the default border.
 * ============================================================ */

.rcms-theme-picker {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 12px;
}
.rcms-theme-picker-form { margin: 0; }
.rcms-theme-picker-tile {
    width: 100%;
    background: var(--md-sys-color-surface-container-lowest);
    border: 2px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-lg);
    padding: 12px;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.12s, transform 0.06s, box-shadow 0.12s;
}
.rcms-theme-picker-tile:hover {
    border-color: var(--md-sys-color-outline);
    box-shadow: var(--md-sys-elevation-1);
}
.rcms-theme-picker-tile:active { transform: scale(0.98); }
.rcms-theme-picker-tile--active {
    border-color: var(--md-sys-color-primary);
    box-shadow: 0 0 0 1px var(--md-sys-color-primary);
}
.rcms-theme-picker-swatch-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 2px;
    aspect-ratio: 2 / 1;
    border-radius: var(--md-sys-radius-md);
    overflow: hidden;
    margin-bottom: 8px;
    border: 1px solid var(--md-sys-color-outline-variant);
}
.rcms-theme-picker-swatch-grid span {
    display: block;
}
.rcms-theme-picker-name {
    font: var(--md-sys-typescale-ui-button);
    color: var(--md-sys-color-on-surface);
}
.rcms-theme-picker-badge {
    font-size: 11px;
    color: var(--md-sys-color-primary);
    font-weight: 600;
    margin-top: 2px;
}

/* ============================================================
 * #59 — Page editor utility classes. Extracts repeated inline
 * style="…" patterns from `page_form.html` so future theme
 * overrides cascade against named selectors instead of inline
 * (which beats every external rule).
 * ============================================================ */

/* Tertiary-tinted icon — used for translate / palette / locale
 * accents in the page editor's chrome. */
.rcms-icon-tertiary { color: var(--md-sys-color-tertiary); }
/* Primary-tinted icon — used for "history" / accent markers. */
.rcms-icon-primary { color: var(--md-sys-color-primary); }
/* Small material-symbols inline next to text — corrects the
 * baseline so icon + text read level. */
.rcms-icon-bullet { vertical-align: -4px; }
/* Muted meta cell in revision tables. Inherits the muted color
 * and a slightly smaller font-size. */
.rcms-cell-meta {
    color: var(--md-sys-color-on-surface-variant);
    font-size: 12px;
}
/* Smaller status tag used inside dense tables (revision list). */
.rcms-tag--small { font-size: 11px; }
/* Page-form translate card layout — keeps the translate / promote
 * form from stretching across very wide viewports. */
.rcms-page-form-translate { max-width: 920px; }
.rcms-text-muted-soft { color: var(--md-sys-color-outline); }

/* ============================================================
 * #325 / #314 — rcms- atomic utility layer.
 *
 * The admin templates carry ~545 inline `style="…"` declarations,
 * dominated by a small set of atomic patterns (type sizes, flex,
 * spacing, the muted colour). Inline styles beat every external
 * rule, so they silently defeat theming and host overrides.
 *
 * This layer names those patterns behind the single `rcms-` prefix
 * (#314) so the de-inline sweep (#325) has prefixed, collision-free
 * classes to sweep into. Atomic + composable: e.g. `margin:0 0 12px`
 * → `rcms-m-0 rcms-mb-3`. Spacing scale is the 4px base the inline
 * values already use (1=4 2=8 3=12 4=16px); the `margin:0` shorthand
 * is declared before the directional longhands so combining them
 * resolves correctly by source order.
 * ============================================================ */

/* Type scale (raw px, matching the values already inlined). */
.rcms-text-3xs  { font-size: 10px; }
.rcms-text-2xs  { font-size: 11px; }
.rcms-text-xs   { font-size: 12px; }
.rcms-text-sm   { font-size: 13px; }
.rcms-text-base { font-size: 14px; }
.rcms-text-lg   { font-size: 16px; }
.rcms-text-xl   { font-size: 18px; }
.rcms-text-2xl  { font-size: 32px; }
.rcms-fw-medium   { font-weight: 500; }
.rcms-fw-semibold { font-weight: 600; }
.rcms-italic         { font-style: italic; }
.rcms-leading-normal { line-height: 1.5; }
.rcms-font-mono      { font-family: ui-monospace, Menlo, monospace; }

/* Colour helpers (token-backed so theming still applies). `--muted` is
 * `--md-sys-color-on-surface-variant`; `--muted-2` is `--md-sys-color-outline`. */
.rcms-text-muted   { color: var(--md-sys-color-on-surface-variant); }
.rcms-text-muted-2 { color: var(--md-sys-color-outline); }
.rcms-text-primary { color: var(--md-sys-color-primary); }
.rcms-text-on-surface { color: var(--md-sys-color-on-surface); }
.rcms-text-error   { color: var(--md-sys-color-error); }
.rcms-text-tertiary { color: var(--md-sys-color-tertiary); }
.rcms-text-accent  { color: var(--accent); }
.rcms-text-inherit { color: inherit; }
.rcms-bg-surface-container { background: var(--md-sys-color-surface-container); }
.rcms-opacity-55 { opacity: 0.55; }
.rcms-opacity-60 { opacity: 0.6; }
.rcms-opacity-70 { opacity: 0.7; }

/* Display + flex/grid alignment helpers. */
.rcms-flex         { display: flex; }
.rcms-inline-flex  { display: inline-flex; }
.rcms-inline-block { display: inline-block; }
.rcms-inline       { display: inline; }
.rcms-block        { display: block; }
.rcms-grid         { display: grid; }
/* Auto-fill card grids (media / library / role pickers, #325). */
.rcms-autogrid-180 { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.rcms-autogrid-220 { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.rcms-autogrid-280 { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.rcms-items-center { align-items: center; }
.rcms-items-start  { align-items: flex-start; }
.rcms-justify-between { justify-content: space-between; }
.rcms-justify-center  { justify-content: center; }
.rcms-flex-wrap    { flex-wrap: wrap; }
.rcms-flex-1       { flex: 1; }
/* Redirect-list search form — grows but never collapses below a usable
 * width (was an inline style; redirect_list.html is locked style-free). */
.rcms-redirect-search-form { flex: 1 1 260px; }
.rcms-flex-2       { flex: 2; }
.rcms-flex-3       { flex: 3; }
.rcms-flex-none    { flex: none; }
.rcms-text-center  { text-align: center; }
.rcms-gap-1 { gap: 4px; }
.rcms-gap-2 { gap: 8px; }
.rcms-gap-3 { gap: 12px; }
.rcms-gap-4 { gap: 16px; }

/* Spacing scale (4px base: 1=4 2=8 3=12 4=16 6=24px). The `margin`/`padding`
 * shorthands are declared before the directional longhands so combining
 * them (e.g. `rcms-m-0 rcms-mb-3` for `margin:0 0 12px`) resolves by
 * source order. */
.rcms-m-0  { margin: 0; }
.rcms-mx-0 { margin-left: 0; margin-right: 0; }
.rcms-mt-0 { margin-top: 0; }
.rcms-mt-1 { margin-top: 4px; }
.rcms-mt-2 { margin-top: 8px; }
.rcms-mt-3 { margin-top: 12px; }
.rcms-mt-4 { margin-top: 16px; }
.rcms-mt-5 { margin-top: 20px; }
.rcms-mt-6 { margin-top: 24px; }
.rcms-mt-8 { margin-top: 32px; }
.rcms-mb-0 { margin-bottom: 0; }
.rcms-mb-1 { margin-bottom: 4px; }
.rcms-mb-2 { margin-bottom: 8px; }
.rcms-mb-3 { margin-bottom: 12px; }
.rcms-mb-4 { margin-bottom: 16px; }
.rcms-mb-6 { margin-bottom: 24px; }
.rcms-ml-1 { margin-left: 4px; }
.rcms-ml-2 { margin-left: 8px; }
.rcms-ml-3 { margin-left: 12px; }
.rcms-p-0  { padding: 0; }
.rcms-p-3  { padding: 12px; }
.rcms-p-bar { padding: 16px 20px; }
.rcms-px-0 { padding-left: 0; padding-right: 0; }
.rcms-px-4 { padding-left: 16px; padding-right: 16px; }
.rcms-pl-5 { padding-left: 20px; }
.rcms-pl-6 { padding-left: 24px; }
.rcms-py-2 { padding-top: 8px; padding-bottom: 8px; }
.rcms-py-3 { padding-top: 12px; padding-bottom: 12px; }
.rcms-pt-0 { padding-top: 0; }

/* Borders / radii (token-backed). */
/* Shorthands (border / border:0) BEFORE directional longhands so combining
 * them — e.g. `rcms-border-0 rcms-border-top` for `border:0; border-top:1px` —
 * resolves to the longhand by source order. */
.rcms-border        { border: 1px solid var(--md-sys-color-outline-variant); }
.rcms-border-0      { border: 0; }
.rcms-border-top    { border-top: 1px solid var(--md-sys-color-outline-variant); }
.rcms-border-bottom { border-bottom: 1px solid var(--md-sys-color-outline-variant); }
.rcms-rounded        { border-radius: var(--md-sys-radius-md); }
.rcms-rounded-circle { border-radius: 50%; }
.rcms-rounded-0      { border-radius: 0; }

/* Misc one-property helpers. */
.rcms-relative        { position: relative; }
.rcms-absolute        { position: absolute; }
.rcms-w-full          { width: 100%; }
/* Fixed column widths — table <th> sizing swept off inline styles (#325). */
.rcms-w-32            { width: 32px; }
.rcms-w-60            { width: 60px; }
.rcms-w-70            { width: 70px; }
.rcms-w-100           { width: 100px; }
.rcms-w-180           { width: 180px; }
.rcms-w-320           { width: 320px; }
.rcms-maxw-full       { max-width: 100%; }
.rcms-minw-0          { min-width: 0; }
.rcms-minw-160        { min-width: 160px; }
.rcms-minw-180        { min-width: 180px; }
.rcms-minw-200        { min-width: 200px; }
.rcms-minw-220        { min-width: 220px; }
.rcms-minw-280        { min-width: 280px; }
.rcms-maxw-120        { max-width: 120px; }
.rcms-maxw-600        { max-width: 600px; }
.rcms-maxw-640        { max-width: 640px; }
.rcms-maxw-720        { max-width: 720px; }
.rcms-maxw-760        { max-width: 760px; }
.rcms-maxw-900        { max-width: 900px; }
.rcms-overflow-hidden  { overflow: hidden; }
.rcms-overflow-visible { overflow: visible; }
.rcms-overflow-auto    { overflow: auto; }
.rcms-maxh-260         { max-height: 260px; }
.rcms-truncate        { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rcms-border-collapse { border-collapse: collapse; }
.rcms-pre-wrap        { white-space: pre-wrap; }
.rcms-cursor-pointer  { cursor: pointer; }
.rcms-select-none     { user-select: none; }
.rcms-list-none       { list-style: none; }
.rcms-no-underline    { text-decoration: none; }

/* #325 — avatar: circular user image with an initial/icon fallback.
 * `--sm` is the 28px sidebar size; the bare class is the 80px profile
 * size. `object-fit` only bites on the <img> variant; it's inert on
 * the fallback <div>/<span>. */
.rcms-avatar     { width: 80px; height: 80px; object-fit: cover; }
.rcms-avatar--sm { width: 28px; height: 28px; }
.rcms-avatar-fallback             { background: var(--md-sys-color-surface-container); }
.rcms-avatar--sm.rcms-avatar-fallback { background: var(--md-sys-color-surface-container-high); }

/* #205 — markdown toolbar. Sits directly above the textarea it
 * targets; revealed only after cms-ux.js attaches handlers
 * (defaults to `hidden` in the template). */
.rcms-md-toolbar {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
    padding: 4px 6px;
    border: 1px solid var(--md-sys-color-outline-variant);
    border-bottom: none;
    border-top-left-radius: var(--md-sys-radius-md);
    border-top-right-radius: var(--md-sys-radius-md);
    background: var(--md-sys-color-surface-container-low);
}
.rcms-md-toolbar + textarea {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    margin-top: 0;
}
.rcms-md-btn {
    background: transparent;
    border: 1px solid transparent;
    border-radius: 4px;
    padding: 3px 8px;
    font-size: 12px;
    line-height: 1.4;
    color: var(--md-sys-color-on-surface);
    cursor: pointer;
    font-family: inherit;
}
.rcms-md-btn:hover {
    background: var(--md-sys-color-surface-container-high);
    border-color: var(--md-sys-color-outline-variant);
}
.rcms-md-btn:active {
    background: var(--md-sys-color-secondary-container);
}
.rcms-md-sep {
    width: 1px;
    height: 16px;
    background: var(--md-sys-color-outline-variant);
    margin: 0 2px;
}

/* #263 — richtext widget preview pane. Wraps a richtext textarea
 * with a Preview/Edit toggle row; the preview pane swaps in on blur
 * and renders the sanitized HTML so the editor sees styled output
 * instead of literal `<h3>` tags. */
.rcms-richtext-wrap {
    display: flex;
    flex-direction: column;
}
.rcms-richtext-toolbar-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}
.rcms-richtext-toolbar-row > .rcms-md-toolbar {
    flex: 1 1 auto;
}
.rcms-richtext-toggle {
    align-self: flex-end;
    margin-left: auto;
    background: transparent;
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    padding: 4px 10px;
    font-size: 11px;
    color: var(--md-sys-color-on-surface);
    cursor: pointer;
    font-family: inherit;
}
.rcms-richtext-toggle:hover {
    background: var(--md-sys-color-surface-container-high);
}
.rcms-richtext-toggle:active {
    background: var(--md-sys-color-secondary-container);
}
.rcms-richtext-preview {
    padding: 10px 12px;
    min-height: 140px;
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    background: var(--md-sys-color-surface);
    color: var(--md-sys-color-on-surface);
    cursor: text;
    font: inherit;
    line-height: 1.55;
}
.rcms-richtext-preview:hover {
    border-color: var(--md-sys-color-outline);
}
.rcms-richtext-preview:focus {
    outline: 2px solid var(--md-sys-color-primary);
    outline-offset: -2px;
}
/* Style the rendered output so headings/lists/quotes look like
 * the public render, not naked. Scoped to the preview to avoid
 * bleeding into other admin chrome. */
.rcms-richtext-preview > :first-child { margin-top: 0; }
.rcms-richtext-preview > :last-child { margin-bottom: 0; }
.rcms-richtext-preview h1,
.rcms-richtext-preview h2,
.rcms-richtext-preview h3,
.rcms-richtext-preview h4 {
    margin: 12px 0 6px;
    font-weight: 600;
}
.rcms-richtext-preview p { margin: 0 0 10px; }
.rcms-richtext-preview ul,
.rcms-richtext-preview ol { margin: 0 0 10px 20px; }
.rcms-richtext-preview blockquote {
    margin: 0 0 10px;
    padding: 4px 12px;
    border-left: 3px solid var(--md-sys-color-outline-variant);
    color: var(--md-sys-color-on-surface-variant);
}
.rcms-richtext-preview code {
    background: var(--md-sys-color-surface-container);
    padding: 1px 4px;
    border-radius: 3px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.92em;
}
.rcms-richtext-preview pre {
    background: var(--md-sys-color-surface-container);
    padding: 10px 12px;
    border-radius: var(--md-sys-radius-md);
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
}
.rcms-richtext-preview a { color: var(--md-sys-color-primary); }
.rcms-richtext-preview a:hover { text-decoration: underline; }

/* #205 Phase 2 — block picker tile description. The picker tile
 * now shows the block name + (optional) one-line description. */
.rcms-block-picker-tile-label {
    display: inline-flex;
    flex-direction: column;
    text-align: left;
    gap: 2px;
    order: 1; /* sit before the icon; icon has order:2 + margin-left:auto */
    flex: 1 1 auto;
    min-width: 0;
}
.rcms-block-picker-tile-name {
    font-weight: 500;
}
.rcms-block-picker-tile-desc {
    font-size: 11px;
    color: var(--md-sys-color-on-surface-variant);
}
/* Wagtail-parity Meta.preview_value — inline mini-render of the
   block with a sample value so editors see "what does this look
   like" before inserting. Scoped down + opacity-muted so it
   doesn't distract from the tile's primary label. */
.rcms-block-picker-tile-preview {
    display: block;
    margin-top: 6px;
    padding: 6px 8px;
    border-radius: var(--md-sys-radius-sm);
    background: var(--md-sys-color-surface-container-low);
    color: var(--md-sys-color-on-surface-variant);
    font-size: 11px;
    line-height: 1.3;
    opacity: 0.85;
    pointer-events: none;
    overflow: hidden;
    max-height: 64px;
}
.rcms-block-picker-tile-preview * { font-size: inherit !important; margin: 0 !important; }
/* #205 Phase 2 — collapsed stream-block hides body, keeps header
 * visible so the editor can still drag / remove / expand. JS toggles
 * the `data-collapsed` attribute via the header click handler. */
.rcms-stream-block[data-collapsed] .rcms-stream-block-body {
    display: none;
}

/* Phases 4–5 — page / snippet / document chooser widgets share a
 * single dialog overlay (`rcms-chooser-dialog`) and the same inline
 * wrapper (`rcms-chooser`). The dialog mounts lazily under
 * document.body once an editor clicks Open. */
.rcms-chooser {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}
/* Selected-image preview thumbnail for the media chooser. */
.rcms-media-preview {
    width: 48px;
    height: 48px;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid var(--md-sys-color-outline-variant);
    background: var(--md-sys-color-surface-container-high);
    flex: none;
}
.rcms-chooser-dialog {
    border: none;
    padding: 0;
    border-radius: var(--md-sys-radius-lg);
    background: var(--md-sys-color-surface);
    color: var(--md-sys-color-on-surface);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
}
.rcms-chooser-dialog::backdrop {
    background: rgba(0, 0, 0, 0.4);
}
/* Media picker — the chooser dialog's full-experience mode for
 * kind="media": toolbar (search / collection / view toggle / upload),
 * thumbnail grid or list, a detail-preview pane, and drag-drop upload.
 * Non-media kinds share the same shell (header/toolbar/results/footer)
 * with the plain row list. Every displayed element gets a `[hidden]`
 * guard — an author `display` beats the UA `[hidden]` rule otherwise. */
.rcms-chooser-dialog { width: min(720px, 94vw); }
.rcms-chooser-dialog--media { width: min(960px, 94vw); height: min(640px, 84vh); }
.rcms-picker { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.rcms-picker__header {
    display: flex; align-items: center; gap: 8px; padding: 12px 16px;
    border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.rcms-picker__header strong { flex: 1; }
.rcms-picker__toolbar {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 10px 16px; border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.rcms-picker__search { flex: 1; min-width: 140px; padding: 6px 10px; }
.rcms-picker__media-tools,
.rcms-picker__multi { display: inline-flex; align-items: center; gap: 8px; }
.rcms-picker__media-tools[hidden] { display: none; }
.rcms-picker__collection { max-width: 220px; padding: 6px 8px; }
.rcms-picker__viewtoggle { display: inline-flex; border: 1px solid var(--md-sys-color-outline-variant); border-radius: 8px; overflow: hidden; }
.rcms-picker__viewtoggle button {
    border: 0; background: transparent; padding: 5px 8px; cursor: pointer;
    color: var(--md-sys-color-on-surface-variant); display: inline-flex;
}
/* Pressed fill is grouped with the selected-row rule below. */
.rcms-picker__body { position: relative; display: flex; flex: 1; min-height: 0; }
.rcms-picker__results {
    list-style: none; padding: 4px 0; margin: 0; flex: 1;
    max-height: 60vh; overflow-y: auto;
}
.rcms-picker__results[hidden] { display: none; }
.rcms-picker-row {
    display: flex; align-items: center; gap: 12px; width: 100%;
    padding: 8px 16px; cursor: pointer; text-align: left;
    border: 0; border-top: 1px solid var(--md-sys-color-outline-variant);
    background: transparent; color: inherit; font: inherit;
}
.rcms-picker-row:hover { background: var(--md-sys-color-surface-container-high); }
.rcms-picker-row__thumb {
    width: 40px; height: 40px; object-fit: cover; border-radius: 4px; flex: none;
    background: var(--md-sys-color-surface-container-high);
}
.rcms-picker-row__thumb--icon {
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--md-sys-color-on-surface-variant);
}
/* Selected/pressed fill — the list-view selected row and the grid/list
   view toggle share the secondary-container pair. */
.rcms-picker-row--media.rcms-picker-card--selected,
.rcms-picker__viewtoggle button[aria-pressed="true"] {
    background: var(--md-sys-color-secondary-container);
    color: var(--md-sys-color-on-secondary-container);
}
.rcms-picker-row__text { flex: 1; min-width: 0; display: block; }
.rcms-picker-row__title { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Base truncated-meta look shared with the menu builder / upload queue. */
.rcms-picker-row__sub { display: block; font-family: ui-monospace, monospace; }
.rcms-picker-row__meta { flex: none; font-size: 11px; color: var(--md-sys-color-on-surface-variant); }
.rcms-picker__empty { padding: 24px; text-align: center; color: var(--md-sys-color-on-surface-variant); font-size: 13px; }
.rcms-picker__error { padding: 16px; color: var(--md-sys-color-error); }
.rcms-picker__media { display: flex; flex: 1; min-height: 0; }
.rcms-picker__media[hidden] { display: none; }
.rcms-picker__grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 10px; align-content: start; padding: 12px 16px; flex: 1; overflow-y: auto;
}
.rcms-picker__list { display: flex; flex-direction: column; padding: 4px 0; flex: 1; overflow-y: auto; }
.rcms-picker-card {
    position: relative; display: flex; flex-direction: column; gap: 4px;
    border: 1px solid var(--md-sys-color-outline-variant); border-radius: 8px;
    padding: 4px; background: transparent; cursor: pointer; font: inherit;
    color: inherit; text-align: left; min-width: 0;
}
.rcms-picker-card:hover { border-color: var(--md-sys-color-primary); }
.rcms-picker-card--selected {
    outline: 2px solid var(--md-sys-color-primary);
    outline-offset: -1px; border-color: var(--md-sys-color-primary);
}
.rcms-picker-card__thumb {
    width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 5px;
    background: var(--md-sys-color-surface-container-high); display: block;
}
.rcms-picker-card__icon {
    width: 100%; aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
    font-size: 32px; color: var(--md-sys-color-on-surface-variant);
    background: var(--md-sys-color-surface-container-high); border-radius: 5px;
}
.rcms-picker-card__name {
    font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    color: var(--md-sys-color-on-surface); padding: 0 2px 2px;
}
.rcms-picker-card__check { position: absolute; top: 8px; left: 8px; z-index: 1; }
.rcms-picker__list .rcms-picker-card__check { position: static; }
.rcms-picker__detail {
    width: 240px; flex: none; border-left: 1px solid var(--md-sys-color-outline-variant);
    padding: 14px; overflow-y: auto;
}
.rcms-picker__detail-img {
    width: 100%; max-height: 180px; object-fit: contain; border-radius: 6px;
    background: var(--md-sys-color-surface-container-high); margin-bottom: 10px;
}
.rcms-picker__detail-name { font-weight: 600; font-size: 13px; margin-bottom: 8px; overflow-wrap: anywhere; }
.rcms-picker__detail-meta { margin: 0; font-size: 12px; }
.rcms-picker__detail-meta dt { color: var(--md-sys-color-on-surface-variant); font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; margin-top: 8px; }
.rcms-picker__detail-meta dd { margin: 1px 0 0; overflow-wrap: anywhere; }
.rcms-picker__detail-empty {
    color: var(--md-sys-color-on-surface-variant); font-size: 12px;
    text-align: center; padding: 32px 8px;
}
.rcms-picker__dropzone {
    position: absolute; inset: 8px; z-index: 2;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
    border: 2px dashed var(--md-sys-color-primary); border-radius: 10px;
    background: color-mix(in srgb, var(--md-sys-color-surface) 88%, var(--md-sys-color-primary));
    color: var(--md-sys-color-primary); font-weight: 600; pointer-events: none;
}
.rcms-picker__dropzone[hidden] { display: none; }
.rcms-picker__footer {
    display: flex; align-items: center; gap: 8px; justify-content: flex-end;
    padding: 10px 16px; border-top: 1px solid var(--md-sys-color-outline-variant);
}
/* `.rcms-btn` carries its own display, which outranks the UA [hidden] rule —
 * guard every hideable picker control explicitly. */
.rcms-picker [hidden] { display: none !important; }
.rcms-picker__notice { flex: 1; font-size: 12px; color: var(--md-sys-color-on-surface-variant); overflow-wrap: anywhere; }
.rcms-picker__notice--error { color: var(--md-sys-color-error); }
/* Layout comes from the `.rcms-picker__media-tools` group above. */
.rcms-picker__multi [data-pc-multi-count] { font-size: 12px; color: var(--md-sys-color-on-surface-variant); }
@media (max-width: 719px) {
    .rcms-picker__detail { display: none; }
}
@media (max-width: 480px) {
    .rcms-chooser-dialog--media {
        width: 100vw; height: 100dvh; max-height: 100dvh;
        max-width: 100vw; border-radius: 0; margin: 0;
    }
    .rcms-picker__grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
}

/* Phase 6 — Wagtail-parity MultiFieldPanel / FieldRowPanel /
 * HelpPanel. TabBody::Group + TabBody::Help in the page editor's
 * custom tabs. */
.rcms-panel-group {
    border: 1px solid var(--md-sys-color-outline-variant);
    border-radius: var(--md-sys-radius-md);
    padding: 12px 16px 16px;
    margin: 12px 0;
    background: var(--md-sys-color-surface);
}
.rcms-panel-group > legend {
    padding: 0 6px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--md-sys-color-on-surface-variant);
}
.rcms-panel-group-help {
    margin: 0 0 12px;
}
.rcms-panel-group-body {
    display: flex;
    gap: 12px;
}
.rcms-panel-group--column .rcms-panel-group-body {
    flex-direction: column;
}
.rcms-panel-group--row .rcms-panel-group-body {
    flex-direction: row;
    flex-wrap: wrap;
}
.rcms-panel-group--row .rcms-panel-group-body > .rcms-field {
    flex: 1 1 240px;
    min-width: 0;
}
@media (max-width: 640px) {
    .rcms-panel-group--row .rcms-panel-group-body {
        flex-direction: column;
    }
}
.rcms-panel-help {
    margin: 12px 0;
    padding: 12px 16px;
    border-left: 3px solid var(--md-sys-color-primary);
    background: var(--md-sys-color-surface-container-low);
    border-radius: 0 var(--md-sys-radius-md) var(--md-sys-radius-md) 0;
}
.rcms-panel-help :first-child {
    margin-top: 0;
}
.rcms-panel-help :last-child {
    margin-bottom: 0;
}

/* Phase 7 — SwitchInput. Wagtail-style iOS toggle on
 * `WidgetKind::Boolean` when `variant = "switch"`. The native
 * checkbox stays in the DOM (hidden but accessible to AT) so the
 * form submission shape is identical to the default checkbox. */
.rcms-checkbox-field--switch {
    align-items: center;
    gap: 8px;
}
.rcms-checkbox-field--switch > input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
}
.rcms-switch-track {
    display: inline-block;
    position: relative;
    width: 36px;
    height: 20px;
    border-radius: 999px;
    background: var(--md-sys-color-surface-container-high);
    border: 1px solid var(--md-sys-color-outline-variant);
    cursor: pointer;
    transition: background 0.18s;
    flex: 0 0 auto;
}
.rcms-switch-knob {
    position: absolute;
    top: 1px;
    left: 1px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--md-sys-color-on-surface-variant);
    transition: transform 0.18s, background 0.18s;
}
.rcms-checkbox-field--switch > input:checked + .rcms-switch-track {
    background: var(--md-sys-color-primary);
    border-color: var(--md-sys-color-primary);
}
.rcms-checkbox-field--switch > input:checked + .rcms-switch-track .rcms-switch-knob {
    transform: translateX(16px);
    background: var(--md-sys-color-on-primary);
}
.rcms-checkbox-field--switch > input:focus-visible + .rcms-switch-track {
    outline: 2px solid var(--md-sys-color-primary);
    outline-offset: 2px;
}
.rcms-checkbox-field--switch > input:disabled + .rcms-switch-track {
    opacity: 0.5;
    cursor: not-allowed;
}

/* ===================================================================
   Visual Form Builder (#533, FB-04..FB-08)
   =================================================================== */
.rcms-form-builder { --fb-border: var(--md-sys-color-outline-variant, #cdcdd5); }
.rcms-fb-topbar {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; flex-wrap: wrap; margin-bottom: 12px;
}
.rcms-fb-topbar-left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.rcms-fb-form-title { font-size: 1.2rem; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rcms-fb-topbar-right,
.rcms-fb-page-head { display: flex; align-items: center; gap: 8px; }
.rcms-fb-badge { font-size: 0.72rem; font-weight: 600; padding: 3px 8px; border-radius: 999px; background: var(--md-sys-color-tertiary-container, #ffe08a); color: var(--md-sys-color-on-tertiary-container, #5b4300); }

.rcms-fb-pagetabs { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; border-bottom: 1px solid var(--fb-border); margin-bottom: 14px; }
.rcms-fb-tab {
    border: 1px solid transparent; border-bottom: none; background: transparent;
    padding: 8px 14px; border-radius: 8px 8px 0 0; cursor: pointer; font: inherit;
    color: var(--md-sys-color-on-surface-variant, #444); margin-bottom: -1px;
}
.rcms-fb-tab.active {
    background: var(--md-sys-color-surface, #fff);
    border-color: var(--fb-border); border-bottom-color: var(--md-sys-color-surface, #fff);
    color: var(--md-sys-color-on-surface, #111); font-weight: 600;
}
.rcms-fb-tab-add { margin-left: 4px; }

.rcms-fb-workspace { display: grid; grid-template-columns: 1fr 320px; gap: 16px; align-items: start; }
/* The Settings/Preview toggles hide the workspace via the `hidden` attribute,
 * but an author `display: grid` overrides the UA `[hidden]{display:none}` —
 * so without this the workspace stayed visible and the settings/preview
 * panels rendered BELOW the canvas instead of replacing it. */
.rcms-fb-workspace[hidden] { display: none; }
@media (max-width: 900px) { .rcms-fb-workspace { grid-template-columns: 1fr; } }

.rcms-fb-canvas { min-width: 0; display: flex; flex-direction: column; gap: 14px; }
/* `.rcms-fb-page-head` layout is grouped with `.rcms-fb-topbar-right` above. */
.rcms-fb-page-name { flex: 1; font-size: 1.05rem; font-weight: 600; padding: 6px 8px; border: 1px solid transparent; border-radius: 6px; background: transparent; }
.rcms-fb-page-name:hover, .rcms-fb-page-name:focus { border-color: var(--fb-border); background: var(--md-sys-color-surface, #fff); }
.rcms-fb-page-tools, .rcms-fb-section-tools, .rcms-fb-row-tools, .rcms-fb-col-tools, .rcms-fb-field-tools { display: flex; gap: 2px; }

.rcms-fb-section { border: 1px solid var(--fb-border); border-radius: 10px; padding: 12px; background: var(--md-sys-color-surface-container-low, #fafafa); }
.rcms-fb-section-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.rcms-fb-section-title { flex: 1; padding: 5px 8px; border: 1px dashed var(--fb-border); border-radius: 6px; background: transparent; font-weight: 600; }

.rcms-fb-row { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 8px; }
.rcms-fb-row-grid { display: flex; gap: 8px; flex: 1; flex-wrap: wrap; }
.rcms-fb-col { min-width: 120px; border: 1px dashed var(--fb-border); border-radius: 8px; padding: 8px; background: var(--md-sys-color-surface, #fff); display: flex; flex-direction: column; gap: 6px; }
.rcms-fb-col-head { display: flex; align-items: center; justify-content: space-between; }
.rcms-fb-width { font-size: 0.75rem; padding: 2px 4px; border: 1px solid var(--fb-border); border-radius: 4px; }

.rcms-fb-field { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 10px; border: 1px solid var(--fb-border); border-radius: 8px; background: var(--md-sys-color-surface-container, #f3f3f7); cursor: pointer; }
.rcms-fb-field.selected { border-color: var(--md-sys-color-primary, #4f46e5); box-shadow: 0 0 0 2px var(--md-sys-color-primary, #4f46e5) inset; }
.rcms-fb-field-main { display: flex; align-items: center; gap: 8px; min-width: 0; }
.rcms-fb-field-label { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rcms-fb-field-meta { font-size: 0.72rem; color: var(--md-sys-color-on-surface-variant, #666); }

.rcms-fb-add-field-wrap { position: relative; }
.rcms-fb-add-field, .rcms-fb-add-row, .rcms-fb-add-section { display: inline-flex; align-items: center; gap: 4px; padding: 6px 10px; border: 1px dashed var(--fb-border); border-radius: 8px; background: transparent; cursor: pointer; font: inherit; color: var(--md-sys-color-primary, #4f46e5); }
.rcms-fb-add-section { align-self: flex-start; }
.rcms-fb-palette { position: absolute; z-index: 30; top: 100%; left: 0; margin-top: 4px; width: 220px; max-height: 320px; overflow: auto; background: var(--md-sys-color-surface, #fff); border: 1px solid var(--fb-border); border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,0.16); padding: 6px; }
.rcms-fb-palette-group { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--md-sys-color-on-surface-variant, #888); padding: 6px 8px 2px; }
.rcms-fb-palette-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 8px; border: none; background: transparent; border-radius: 6px; cursor: pointer; font: inherit; text-align: left; }
.rcms-fb-palette-item:hover { background: var(--md-sys-color-surface-container-high, #ececf2); }

.rcms-fb-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: none; background: transparent; border-radius: 6px; cursor: pointer; color: var(--md-sys-color-on-surface-variant, #555); }
.rcms-fb-icon-btn:hover { background: var(--md-sys-color-surface-container-high, #ececf2); }
.rcms-fb-icon-btn.rcms-fb-danger:hover { background: var(--md-sys-color-error-container, #fcd8d8); color: var(--md-sys-color-error, #b3261e); }

.rcms-fb-inspector { border: 1px solid var(--fb-border); border-radius: 10px; padding: 14px; background: var(--md-sys-color-surface, #fff); position: sticky; top: 12px; }
.rcms-fb-inspector-empty { text-align: center; color: var(--md-sys-color-on-surface-variant, #888); padding: 24px 8px; }
.rcms-fb-inspector-head { display: flex; align-items: center; gap: 8px; font-weight: 700; margin-bottom: 12px; }
.rcms-fb-prop,
.rcms-fb-prev-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
.rcms-fb-prop-label { font-size: 0.78rem; font-weight: 600; color: var(--md-sys-color-on-surface-variant, #555); }
.rcms-fb-prop input[type="text"], .rcms-fb-prop input[type="number"], .rcms-fb-prop textarea { width: 100%; padding: 7px 8px; border: 1px solid var(--fb-border); border-radius: 6px; font: inherit; }
.rcms-fb-prop-check { flex-direction: row; align-items: center; gap: 8px; }
.rcms-fb-options { margin-top: 6px; }
.rcms-fb-option { display: flex; gap: 6px; align-items: center; margin-bottom: 6px; }
.rcms-fb-option input { flex: 1; padding: 6px; border: 1px solid var(--fb-border); border-radius: 6px; }
.rcms-fb-option-value { max-width: 90px; }
.rcms-fb-add-option { display: inline-flex; align-items: center; gap: 4px; border: none; background: transparent; color: var(--md-sys-color-primary, #4f46e5); cursor: pointer; padding: 4px; font: inherit; }
.rcms-fb-rules { margin-top: 10px; border-top: 1px solid var(--fb-border); padding-top: 10px; }
.rcms-fb-rule { border: 1px solid var(--fb-border); border-radius: 8px; padding: 8px; margin-bottom: 8px; }
.rcms-fb-rule-head { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; font-size: 0.82rem; }
.rcms-fb-cond { display: flex; gap: 4px; align-items: center; margin-bottom: 4px; }
.rcms-fb-cond select, .rcms-fb-cond input, .rcms-fb-rule-head select { padding: 4px; border: 1px solid var(--fb-border); border-radius: 4px; font-size: 0.82rem; }
.rcms-fb-cond input { flex: 1; min-width: 60px; }

/* Preview + settings replace the workspace when toggled — centre them so
 * they read as the page's main panel, not a stray block hugging the left. */
.rcms-fb-preview,
.rcms-fb-settings { border: 1px solid var(--fb-border); border-radius: 10px; padding: 24px 28px; background: var(--md-sys-color-surface, #fff); margin: 0 auto; }
.rcms-fb-preview { max-width: 760px; }
.rcms-fb-translate-menu { position: relative; display: inline-block; }
/* Summary reset comes from the shared <details> menu rule (kebab section). */
.rcms-fb-translate-list { position: absolute; right: 0; top: 100%; margin-top: 4px; z-index: 30; background: var(--md-sys-color-surface, #fff); border: 1px solid var(--fb-border); border-radius: 8px; box-shadow: 0 6px 24px rgba(0,0,0,0.16); min-width: 160px; padding: 4px; }
.rcms-fb-translate-list a { display: block; padding: 7px 10px; border-radius: 6px; text-decoration: none; color: inherit; }
.rcms-fb-translate-list a:hover { background: var(--md-sys-color-surface-container-high, #ececf2); }
.rcms-fb-settings { max-width: 560px; }
.rcms-fb-settings h3 { margin-top: 0; margin-bottom: 14px; font-size: 1.05rem; }
.rcms-fb-settings .rcms-fb-prop { margin-bottom: 14px; }
.rcms-fb-settings textarea, .rcms-fb-settings input[type="text"] { width: 100%; padding: 8px; border: 1px solid var(--fb-border); border-radius: 6px; font: inherit; }
.rcms-fb-prev-page { margin-bottom: 22px; }
.rcms-fb-prev-row { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; }
.rcms-fb-prev-col { min-width: 140px; }
/* `.rcms-fb-prev-field` layout is grouped with `.rcms-fb-prop` above. */
.rcms-fb-prev-field label { font-weight: 600; font-size: 0.85rem; }
.rcms-fb-prev-field input, .rcms-fb-prev-field textarea, .rcms-fb-prev-field select { padding: 8px; border: 1px solid var(--fb-border); border-radius: 6px; font: inherit; }
.rcms-fb-prev-choices { display: flex; flex-direction: column; gap: 4px; }
.rcms-fb-prev-choice { display: flex; align-items: center; gap: 6px; font-weight: 400; }
.rcms-fb-prev-rating { display: flex; gap: 2px; color: #e0a800; }
.rcms-fb-prev-static { margin-bottom: 12px; }

/* Submissions table (FB-12) */
.rcms-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.rcms-table th, .rcms-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--md-sys-color-outline-variant, #cdcdd5); vertical-align: top; }
.rcms-table thead th { font-weight: 600; color: var(--md-sys-color-on-surface-variant, #555); white-space: nowrap; }
.rcms-table tbody tr:hover { background: var(--md-sys-color-surface-container, #f3f3f7); }

/* ---- Block tree (block_tree.js) — sidebar outline of stream blocks ----
 * Activating swaps the main sidebar's nav for a live tree of the page's
 * blocks (incl. nested Stream/Repeat children). Every hideable element is
 * `[hidden]`-guarded: author display rules outrank the UA [hidden] rule. */
.rcms-sidebar--blocktree .rcms-sidebar-nav,
.rcms-sidebar--blocktree .rcms-sidebar-footer { display: none; }
.rcms-sidebar-blocktree {
    display: flex; flex-direction: column; flex: 1; min-height: 0;
    padding: 8px 10px;
}
.rcms-sidebar-blocktree[hidden] { display: none !important; }
.rcms-blocktree-back {
    display: flex; align-items: center; gap: 6px;
    border: 0; background: transparent; cursor: pointer; font: inherit;
    color: var(--md-sys-color-on-surface-variant);
    padding: 6px 8px; border-radius: 8px; text-align: left;
}
.rcms-blocktree-back:hover { background: var(--md-sys-color-surface-container-high); }
.rcms-blocktree-toolbar {
    display: flex; align-items: center; gap: 2px;
    padding: 6px 8px 8px; border-bottom: 1px solid var(--md-sys-color-outline-variant);
    margin-bottom: 6px;
}
.rcms-blocktree-title {
    flex: 1; font-size: 11px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.05em; color: var(--md-sys-color-on-surface-variant);
}
.rcms-blocktree-body { flex: 1; overflow-y: auto; min-height: 0; }
.rcms-blocktree-node {
    display: flex; align-items: center; gap: 2px;
    padding-left: calc(var(--bt-depth, 0) * 14px);
    border-radius: 8px;
}
.rcms-blocktree-node:hover { background: var(--md-sys-color-surface-container-high); }
.rcms-blocktree-jump {
    flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px;
    border: 0; background: transparent; cursor: pointer; font: inherit;
    font-size: 13px; color: var(--md-sys-color-on-surface);
    padding: 6px 4px 6px 8px; text-align: left; border-radius: 8px;
}
.rcms-blocktree-label {
    flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rcms-blocktree-node.is-collapsed .rcms-blocktree-label { color: var(--md-sys-color-on-surface-variant); }
.rcms-blocktree-fold {
    flex: none; border: 0; background: transparent; cursor: pointer;
    color: var(--md-sys-color-on-surface-variant);
    padding: 4px; border-radius: 6px; display: inline-flex;
}
.rcms-blocktree-fold:hover { background: var(--md-sys-color-surface-container-highest); }
.rcms-blocktree-group {
    padding: 6px 8px 2px calc(var(--bt-depth, 0) * 14px + 8px);
    font-size: 10px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.06em; color: var(--md-sys-color-on-surface-variant);
}
.rcms-blocktree-empty {
    padding: 18px 8px; font-size: 12px; text-align: center;
    color: var(--md-sys-color-on-surface-variant);
}
/* Validation state — red label + error-count badge. */
.rcms-blocktree-node.is-invalid .rcms-blocktree-jump,
.rcms-blocktree-node.is-invalid .rcms-blocktree-label { color: var(--md-sys-color-error); }
.rcms-blocktree-errors {
    flex: none; min-width: 16px; height: 16px; border-radius: 999px;
    background: var(--md-sys-color-error); color: var(--md-sys-color-on-error);
    font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
    padding: 0 4px;
}
/* Canvas mirror: a block with invalid fields gets a red dot in its header. */
.rcms-stream-block--invalid > .rcms-stream-block-header .rcms-stream-block-title::after {
    content: ""; display: inline-block; width: 7px; height: 7px;
    border-radius: 999px; background: var(--md-sys-color-error);
    margin-left: 6px; vertical-align: 1px;
}
/* Per-stream-root section header in the block tree (multi-root pages:
 * page-builder zones + repeaters). */
.rcms-blocktree-root {
    padding: 10px 8px 3px;
    font-size: 10px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.07em; color: var(--md-sys-color-primary);
    border-top: 1px solid var(--md-sys-color-outline-variant);
    margin-top: 6px;
}
.rcms-blocktree-root:first-child { border-top: 0; margin-top: 0; }
