/* cbui/variants/aero */
/* ==========================================================================
 * Aero Flavor — Soft pill shapes + Liquid Glass plasticity
 *
 * Aero shares the pill geometry of the Soft flavor and adds *plasticity* on
 * top: solid buttons get a multi-layer drop shadow + inner sheen so they
 * look like physical objects you could press, not flat paint. The effect is
 * what differentiates Aero from Soft — same shape, different physical weight.
 *
 *   • solid buttons:     embossed shadow + top sheen + hover lift
 *   • soft / outline:    subtle frost + tinted hover backdrop blur
 *   • ghost:             flat at rest, frosted on hover
 *   • cards / modals:    backdrop blur + frosted surface
 *
 * Effects are gated:
 *   .cbui-cap-button-shadow           → drop shadow + sheen on buttons
 *   :not(.a11y-reduced-motion)        → hover-lift + active press-down
 *   :not(.a11y-reduced-transparency)  → backdrop blur effects
 *
 * Capability classes are emitted by the chrome / Storybook scope wrapper based
 * on user prefs. The motion gate uses the standard a11y class so the OS-level
 * prefers-reduced-motion is honoured uniformly across all modules.
 *
 * Applied via [data-flavor="aero"] on a container.
 * ========================================================================== */

[data-flavor="aero"] {
    --aero-glass-blur: 10px;
    --aero-glass-saturation: 140%;

    /* Asymmetric "Bringer pill" embossed shadow — top-left highlight,
       bottom-right depth. Multi-layer for a soft falloff. */
    --aero-shadow-emboss:
        -0.75px -0.75px 0.75px rgb(0 0 0 / 0.06),
        -0.75px -0.75px 0.4px rgb(0 0 0 / 0.04),
        0.75px 0.75px 1.5px rgb(0 0 0 / 0.18),
        0.75px 0.75px 0.75px rgb(0 0 0 / 0.12);
    --aero-shadow-emboss-hover:
        0 0 8px rgb(0 0 0 / 0.22),
        -0.75px -0.75px 0.75px rgb(0 0 0 / 0.06),
        -0.75px -0.75px 0.4px rgb(0 0 0 / 0.04),
        0.75px 0.75px 1.5px rgb(0 0 0 / 0.18),
        0.75px 0.75px 0.75px rgb(0 0 0 / 0.12);

    /* Inner highlight on the top edge — the "Apple sheen" trick. The colour
       is calibrated for solid coloured surfaces; pure white at moderate alpha. */
    --aero-inner-sheen: inset 0 1px 0 rgb(255 255 255 / 0.28);

    /* Subtle bottom bevel on solid surfaces — gives the illusion of curvature.
       Pairs with the top sheen above. */
    --aero-inner-bevel: inset 0 -1px 0 rgb(0 0 0 / 0.10);

    /* No edge ring needed in light mode — the surrounding white background
       and shadow are enough definition. */
    --aero-edge: transparent;
}

[data-flavor="aero"][data-scheme="dark"],
.dark [data-flavor="aero"] {
    /* Dark mode does NOT use the asymmetric outer shadow — at low light it
       reads as fog rather than depth. We replace it with a clean inset
       treatment: a brighter top edge (sheen), a darker bottom edge (bevel),
       and a tight 1px outer ring on hover for focus.
       This is the modern dark-mode look (think Linear, Vercel, GitHub). */
    --aero-shadow-emboss:
        inset 0 1px 0 rgb(255 255 255 / 0.22),
        inset 0 -1px 0 rgb(0 0 0 / 0.30),
        0 1px 2px rgb(0 0 0 / 0.40);
    --aero-shadow-emboss-hover:
        inset 0 1px 0 rgb(255 255 255 / 0.28),
        inset 0 -1px 0 rgb(0 0 0 / 0.30),
        0 2px 6px rgb(0 0 0 / 0.50),
        0 0 0 1px rgb(255 255 255 / 0.10);

    /* The sheen is encoded in the shadow above; keep this var defined for
       backwards-compat callers but make it a no-op. */
    --aero-inner-sheen: 0 0 transparent;
    --aero-inner-bevel: 0 0 transparent;

    /* Dark mode edge ring — kept subtle; the inset bevel does most of the
       definition work now. */
    --aero-edge: rgb(0 0 0 / 0.45);
}

/* ── Cards ──────────────────────────────────────────────────────────────
 *
 * Aero treats Card variants like Buttons: only the LOUD variant (elevated)
 * gets the full plastic recipe (emboss + sheen + bevel). The QUIET variants
 * stay flat:
 *   • flat     — no Aero treatment, the bg-subtle surface is the affordance
 *   • outlined — translucent frosted surface + diluted border (chrome cue)
 *   • elevated — frosted surface + emboss/sheen/bevel + 1px anchor edge
 *
 * Same loud-vs-quiet rule that keeps Outline/Ghost/Link buttons flat in
 * Aero — see the §1a Quality Bar entry "Aero-Plastik ist exklusiv für
 * solid-Variants".
 */

[data-flavor="aero"] .cbui-card-outlined {
    background-color: color-mix(in srgb, var(--color-surface) 86%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
}
/* Elevated in Aero is the loud, "physical card hovering above the page"
   variant — it needs an OPAQUE surface that reads heavier than the
   translucent outlined treatment, otherwise both look the same in dark
   mode. The plastic recipe (emboss + sheen + bevel below) gives the
   actual lift; the glass blur is dropped because elevated is a tile,
   not a window. */
[data-flavor="aero"] .cbui-card-elevated {
    background-color: var(--color-bg-elevated);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
/* Only retune the neutral border — toned outlined cards keep their
   tone-coloured border so the semantic cue survives the glass treatment.
   (Same loud-vs-quiet thinking as the Navbar Aero rules.) */
[data-flavor="aero"] .cbui-card-outlined.cbui-card-tone-neutral {
    border-color: color-mix(in srgb, var(--color-border) 60%, transparent);
}
/* Elevated is the loud Card variant in Aero — full plastic recipe like
   solid buttons. Anchor edge + emboss + sheen + bevel. */
[data-flavor="aero"] .cbui-card-elevated {
    border-color: var(--aero-edge);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-card-elevated {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}
/* Interactive elevated Aero cards: keep the plastic on hover/focus, add a
   subtle hover lift and focus ring. */
[data-flavor="aero"]:not(.a11y-reduced-motion) .cbui-card-elevated.cbui-card-interactive:hover {
    transform: translateY(-1px);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-card-elevated.cbui-card-interactive:hover {
    box-shadow:
        var(--aero-shadow-emboss-hover),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-card-elevated.cbui-card-interactive:focus-visible {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel),
        0 0 0 var(--focus-ring-offset, 2px) var(--color-bg-base),
        0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring, var(--color-primary));
}

/* ─────────────────────────────────────────────────────────────────────────
 * BUTTONS — solid variants get the Bringer-style emboss + hover glow.
 *
 * The new variant model: solid buttons all share the .cbui-button-solid
 * class regardless of tone. So a single rule applies the plasticity.
 * ───────────────────────────────────────────────────────────────────────── */

[data-flavor="aero"].cbui-cap-button-shadow .cbui-button-solid {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-button-solid:hover {
    box-shadow:
        var(--aero-shadow-emboss-hover),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-button-solid:active {
    /* Pressed state: shrink the outer shadow and intensify the inner bevel,
       giving the illusion the button just dipped into the surface. */
    box-shadow:
        inset 0 2px 4px rgb(0 0 0 / 0.18),
        inset 0 -1px 0 rgb(255 255 255 / 0.10);
}

/* Dark-mode edge ring on solid variants — gives the shadow something to anchor
   against. Light mode keeps --aero-edge: transparent. */
[data-flavor="aero"] .cbui-button-solid {
    border-color: var(--aero-edge);
}

/* Focus-visible composition — the emboss shadow has higher specificity than
   the base focus rule, so we re-emit both layers explicitly when the button
   is keyboard-focused. The ring sits *outside* the emboss, both stay visible. */
[data-flavor="aero"].cbui-cap-button-shadow .cbui-button-solid:focus-visible {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel),
        0 0 0 var(--focus-ring-offset, 2px) var(--color-bg-base),
        0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring, var(--color-primary));
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-button-soft.cbui-button-tone-neutral:focus-visible {
    box-shadow:
        var(--aero-inner-sheen),
        0 1px 2px rgb(0 0 0 / 0.06),
        0 0 0 var(--focus-ring-offset, 2px) var(--color-bg-base),
        0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring, var(--color-primary));
}

/* Soft + neutral (the old "secondary" treatment) — frosted surface in Aero.
   Using the soft+neutral combo because that's what visually maps onto the
   old secondary button: a quiet filled-but-defined affordance. */
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-neutral {
    background-color: color-mix(in srgb, var(--color-surface) 70%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    border-color: color-mix(in srgb, var(--color-border) 70%, transparent);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-button-soft.cbui-button-tone-neutral {
    box-shadow: var(--aero-inner-sheen), 0 1px 2px rgb(0 0 0 / 0.06);
}
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-neutral:hover {
    background-color: color-mix(in srgb, var(--color-surface) 92%, transparent);
}

/* Outline + Ghost are quiet variants — they MUST NOT pick up the solid-
   button plasticity (emboss / sheen / lift). Aero reserves the physical-
   pill treatment for `solid` only; everything else stays a flat affordance
   so the loud/quiet visual contrast is preserved. The earlier Aero rules
   that added inner-sheen on outline-hover and a backdrop blur on ghost-hover
   are intentionally omitted. */

/* Soft (coloured tones) — STRONGER tint than the base flavor, to match Aero's
   higher visual energy. Tones that aren't listed fall back to the base CSS. */
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-primary         { background-color: color-mix(in srgb, var(--color-primary) 22%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-primary:hover   { background-color: color-mix(in srgb, var(--color-primary) 32%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-primary:active  { background-color: color-mix(in srgb, var(--color-primary) 42%, transparent); }

[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-danger          { background-color: color-mix(in srgb, var(--color-danger)  22%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-danger:hover    { background-color: color-mix(in srgb, var(--color-danger)  32%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-danger:active   { background-color: color-mix(in srgb, var(--color-danger)  42%, transparent); }

[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-warning         { background-color: color-mix(in srgb, var(--color-warning) 22%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-warning:hover   { background-color: color-mix(in srgb, var(--color-warning) 32%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-warning:active  { background-color: color-mix(in srgb, var(--color-warning) 42%, transparent); }

[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-info            { background-color: color-mix(in srgb, var(--color-info)    22%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-info:hover      { background-color: color-mix(in srgb, var(--color-info)    32%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-info:active     { background-color: color-mix(in srgb, var(--color-info)    42%, transparent); }

[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-success         { background-color: color-mix(in srgb, var(--color-success) 22%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-success:hover   { background-color: color-mix(in srgb, var(--color-success) 32%, transparent); }
[data-flavor="aero"] .cbui-button-soft.cbui-button-tone-success:active  { background-color: color-mix(in srgb, var(--color-success) 42%, transparent); }

/* ── Motion (gated by reducedMotion a11y class) ───────────────────────── */

/* Lift is part of the Aero solid plasticity — outline / ghost / link buttons
   stay flat. They are quieter affordances and a hover-lift would make them
   feel like physical pills, blurring the loud/quiet visual contrast. */
[data-flavor="aero"]:not(.a11y-reduced-motion) .cbui-button-solid:not(.cbui-button-disabled):not([disabled]):hover {
    transform: translateY(-1px);
}
[data-flavor="aero"]:not(.a11y-reduced-motion) .cbui-button-solid:not(.cbui-button-disabled):not([disabled]):active {
    transform: translateY(0);
}

/* ── Reduced transparency fallback ────────────────────────────────────── */

[data-flavor="aero"].a11y-reduced-transparency .cbui-card-outlined,
[data-flavor="aero"].a11y-reduced-transparency .cbui-card-elevated,
[data-flavor="aero"].a11y-reduced-transparency .cbui-button-soft.cbui-button-tone-neutral {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--color-surface);
}

/* surface means opaque bg-base in every flavour (CB-3704). aero.css is unlayered and
   would otherwise beat the layered .cbui-card-mod-surface rule with the 86 % glass fill. */
[data-flavor="aero"] .cbui-card.cbui-card-mod-surface {
    background-color: var(--color-bg-base);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* ─────────────────────────────────────────────────────────────────────────
 * MODAL — Aero plastic, exclusive to the loud `elevated` variant.
 *
 * Mirrors the Card-elevated recipe (anchor edge + emboss + sheen + bevel).
 * Outlined modals keep their neutral border and the soft default shadow —
 * the loud-vs-quiet contrast is preserved.
 * ───────────────────────────────────────────────────────────────────────── */

[data-flavor="aero"] .cbui-modal-elevated {
    background-color: var(--color-bg-elevated);
    border-color: var(--aero-edge);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-modal-elevated {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel),
        0 24px 64px rgb(0 0 0 / 0.35);
}

/* LocalModal — scope-bound elevated panel. Mirrors the Modal plastic, since
   the elevated panel is the same loud/elevated surface (CB-27). */
[data-flavor="aero"] .cbui-local-modal-elevated {
    background-color: var(--color-bg-elevated);
    border-color: var(--aero-edge);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-local-modal-elevated {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel),
        0 24px 64px rgb(0 0 0 / 0.35);
}
/* size=full is edge-to-edge + frameless — suppress the Aero emboss/border. */
[data-flavor="aero"] .cbui-local-modal-elevated.cbui-local-modal-size-full,
[data-flavor="aero"].cbui-cap-button-shadow .cbui-local-modal-elevated.cbui-local-modal-size-full {
    border: 0;
    box-shadow: none;
}

/* Aero scope picks up a subtle 3px backdrop blur via the
   --overlay-backdrop-blur token (set in AeroFlavor). No flavor-scoped
   ::backdrop rule needed — the base CSS already reads the token. */

/* ─────────────────────────────────────────────────────────────────────────
 * DRAWER — Aero plastic on the panel surface.
 * ───────────────────────────────────────────────────────────────────────── */

[data-flavor="aero"] .cbui-drawer {
    background-color: var(--color-bg-elevated);
    border-color: var(--aero-edge);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-drawer {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}

/* ─────────────────────────────────────────────────────────────────────────
 * POPOVER — Aero glass treatment.
 * Subtle blur on the panel + tone-tinted backdrop fill.
 * ───────────────────────────────────────────────────────────────────────── */

/* Aero adds frosted-glass behind the popover but keeps the popover's own
   border (defined in tooltip.css via color-mix on --color-text). The
   earlier `border-color: var(--aero-edge)` override killed the visible
   border in light mode — aero-edge is transparent on light, only
   appearing on dark. */
[data-flavor="aero"] .cbui-popover {
    background-color: color-mix(in srgb, var(--color-bg-elevated) 88%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
}
[data-flavor="aero"].a11y-reduced-transparency .cbui-popover {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--color-bg-elevated);
}

/* ─────────────────────────────────────────────────────────────────────────
 * TOAST — Aero plastic on solid variant.
 * ───────────────────────────────────────────────────────────────────────── */

[data-flavor="aero"].cbui-cap-button-shadow .cbui-toast-solid {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}

/* ─────────────────────────────────────────────────────────────────────────
 * NAVBAR — Aero plastic.
 *
 * Solid bars get a translucent backdrop blur even without +bg-blur — the bar
 * IS the surface, so the glass treatment is the baseline. Floating bars
 * additionally get the emboss + sheen + bevel composition (same recipe as
 * solid Aero buttons but at bar scale).
 * ───────────────────────────────────────────────────────────────────────── */

/* Use --cbui-navbar-bg as the source so tone classes (which override that
   variable to e.g. --color-primary) keep their colour — Aero just wraps the
   tone-coloured surface in a translucent glass treatment.
   The :not(.cbui-navbar-transparent-until-scroll) guard yields the bar to
   the transparent-until-scroll variant so the at-rest transparent state
   actually paints transparent in Aero too. */
[data-flavor="aero"] .cbui-navbar-solid.cbui-navbar-tone-neutral:not(.cbui-navbar-transparent-until-scroll) {
    background-color: color-mix(in srgb, var(--cbui-navbar-bg) 82%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
}
/* The base navbar.css rule sets the divider on whichever side matches the
   placement (border-bottom for top, border-top for bottom); we use the
   `border-color` shorthand here so this Aero override stays placement-neutral
   — only the side with non-zero border-width actually paints. */
[data-flavor="aero"] .cbui-navbar-solid.cbui-navbar-tone-neutral.cbui-navbar-with-divider {
    border-color: color-mix(in srgb, var(--color-border) 60%, transparent);
}

/* transparent_until_scroll — Aero's scrolled state mirrors the standard
   solid+blur recipe; the not-scrolled state must defeat any Aero defaults. */
[data-flavor="aero"] .cbui-navbar-transparent-until-scroll:not(.is-scrolled) {
    background-color: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-color: transparent;
}
[data-flavor="aero"] .cbui-navbar-transparent-until-scroll.is-scrolled {
    background-color: color-mix(in srgb, var(--color-bg-elevated) 82%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
}

[data-flavor="aero"] .cbui-navbar-floating.cbui-navbar-tone-neutral {
    background-color: color-mix(in srgb, var(--cbui-navbar-bg) 86%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent);
}

/* Tone-coloured floating bars: keep the tone background, just add the plastic
   shadow + sheen for the lifted-card effect. */
[data-flavor="aero"] .cbui-navbar-floating:not(.cbui-navbar-tone-neutral) {
    border: 1px solid var(--cbui-navbar-border);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-navbar-floating {
    box-shadow: var(--aero-shadow-emboss), var(--aero-inner-sheen), var(--aero-inner-bevel);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-navbar-floating.is-scrolled {
    box-shadow:
        var(--aero-shadow-emboss-hover),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}

/* ─────────────────────────────────────────────────────────────────────────
 * WINDOW — Aero plastic (UiWindow, Wave 3a).
 *
 * Aero plastik is EXCLUSIVE to the floating frame: a floating window IS a
 * floating object, so heavy plastic is right here (the inverse of the
 * table.css lesson, where a table is a working surface). The docked frame
 * stays quiet — loud-vs-quiet. The header band gets a frosted translucency,
 * and the focused window carries the full emboss + sheen + bevel + a deep
 * cast shadow; background windows keep just the emboss so the stack still
 * reads as layered.
 * ───────────────────────────────────────────────────────────────────────── */

[data-flavor="aero"] .cbui-window-frame-floating {
    background-color: var(--color-bg-elevated);
    /* Drive the border via the token so the focus cascade keeps working —
       the focused window re-points --cbui-window-frame-border to the accent. */
    --cbui-window-frame-border: var(--aero-edge);
}
[data-flavor="aero"] .cbui-window-frame-floating .cbui-window-frame-header {
    /* The frosted header reads --cbui-window-header-bg, so the focus tint
       (accent-washed surface) flows through automatically. */
    background-color: color-mix(in srgb, var(--cbui-window-header-bg) 80%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
}
[data-flavor="aero"].a11y-reduced-transparency .cbui-window-frame-floating .cbui-window-frame-header {
    background-color: var(--cbui-window-header-bg);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
/* Focused window — the accent border, explicit so it beats the per-frame
   border token rules above regardless of source order. */
[data-flavor="aero"] .cbui-window-focused .cbui-window-frame {
    --cbui-window-frame-border: var(--cbui-window-accent);
}
/* Window shadow stays at Clean defaults across ALL flavors — Aero's
   plastic emboss/sheen/bevel on the floating frame is intentionally NOT
   layered here. The window is a piece of chrome the eye returns to many
   times per session; the Clean drop-shadow is the right amount of lift,
   the Aero plastic was reading "loud". (Card / Button / Navbar plastic
   stays — those are decorative objects, not chrome.) */

/* Docked frame — quiet: just the Aero edge, no plastic. */
[data-flavor="aero"] .cbui-window-frame-docked {
    background-color: var(--color-bg-elevated);
    --cbui-window-frame-border: var(--aero-edge);
}

/* NavItem + Brand share one frosted-hover treatment in Aero — both use the
   .cbui-nav-item-base recipe in CSS. The :not() chains keep the at-rest state
   transparent for transparent-until-scroll bars. */
[data-flavor="aero"]:not(.a11y-reduced-transparency) .cbui-nav-item:hover,
[data-flavor="aero"]:not(.a11y-reduced-transparency) .cbui-navbar-brand:hover {
    backdrop-filter: blur(6px) saturate(140%);
    -webkit-backdrop-filter: blur(6px) saturate(140%);
}

/* Reduced-transparency fallback for navbar */
[data-flavor="aero"].a11y-reduced-transparency .cbui-navbar-solid,
[data-flavor="aero"].a11y-reduced-transparency .cbui-navbar-floating {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--color-bg-elevated);
}
[data-flavor="aero"].a11y-reduced-transparency .cbui-nav-item:hover,
[data-flavor="aero"].a11y-reduced-transparency .cbui-navbar-brand:hover {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* ─────────────────────────────────────────────────────────────────────────
 * HEADER & TOOLBAR — Aero plastic on the shared .cbui-bar recipe.
 *
 * Same liquid-glass baseline as Navbar (frosted backdrop on solid+neutral),
 * but using the bar tokens so any future .cbui-bar consumer gets the same
 * treatment automatically. The :not(.cbui-bar-navbar) guard keeps these rules
 * from double-applying to Navbar (which has its own rules above).
 * ───────────────────────────────────────────────────────────────────────── */

[data-flavor="aero"] .cbui-bar-solid.cbui-bar-tone-neutral:not(.cbui-bar-navbar) {
    background-color: color-mix(in srgb, var(--cbui-bar-bg) 82%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
}
[data-flavor="aero"] .cbui-bar-solid.cbui-bar-tone-neutral:not(.cbui-bar-navbar).cbui-bar-with-divider-bottom,
[data-flavor="aero"] .cbui-bar-solid.cbui-bar-tone-neutral:not(.cbui-bar-navbar).cbui-bar-with-divider-top {
    border-color: color-mix(in srgb, var(--color-border) 60%, transparent);
}

[data-flavor="aero"].a11y-reduced-transparency .cbui-bar-solid:not(.cbui-bar-navbar) {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--color-bg-elevated);
}
@media (prefers-reduced-transparency: reduce) {
    [data-flavor="aero"] .cbui-bar-solid:not(.cbui-bar-navbar) {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background-color: var(--color-bg-elevated);
    }
}

@media (prefers-reduced-transparency: reduce) {
    [data-flavor="aero"] .cbui-navbar-solid,
    [data-flavor="aero"] .cbui-navbar-floating {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background-color: var(--color-bg-elevated);
    }
}

/* OS-level prefers-* still honoured outside the scope system */
@media (prefers-reduced-transparency: reduce) {
    [data-flavor="aero"] .cbui-card,
    [data-flavor="aero"] .cbui-button-soft.cbui-button-tone-neutral {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background-color: var(--color-surface);
    }
}
@media (prefers-reduced-motion: reduce) {
    [data-flavor="aero"] .cbui-button-solid:hover,
    [data-flavor="aero"] .cbui-button-solid:active {
        transform: none;
    }
}

/* ──────────────────────────────────────────────────────────────────────────
 * Nav (vertical) — Aero plastic: the same liquid-glass treatment as Navbar,
 * adapted for vertical layouts. Only neutral-toned Navs get the frosted bg
 * (tone-coloured Navs keep their tone). Floating Navs additionally pick up
 * the emboss + sheen + bevel composition for the lifted-card look.
 * ────────────────────────────────────────────────────────────────────────── */
[data-flavor="aero"] .cbui-nav-default.cbui-nav-tone-neutral {
    background-color: color-mix(in srgb, var(--cbui-nav-bg) 82%, transparent);
}

[data-flavor="aero"] .cbui-nav-floating.cbui-nav-tone-neutral {
    background-color: color-mix(in srgb, var(--cbui-nav-bg) 86%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent);
}

[data-flavor="aero"] .cbui-nav-floating:not(.cbui-nav-tone-neutral) {
    border: 1px solid var(--cbui-nav-border);
}

[data-flavor="aero"].cbui-cap-button-shadow .cbui-nav-floating {
    box-shadow: var(--aero-shadow-emboss), var(--aero-inner-sheen), var(--aero-inner-bevel);
}

/* Reduced-transparency fallback */
[data-flavor="aero"].a11y-reduced-transparency .cbui-nav-default,
[data-flavor="aero"].a11y-reduced-transparency .cbui-nav-floating {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--color-bg-elevated);
}
@media (prefers-reduced-transparency: reduce) {
    [data-flavor="aero"] .cbui-nav-default,
    [data-flavor="aero"] .cbui-nav-floating {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background-color: var(--color-bg-elevated);
    }
}

/* ──────────────────────────────────────────────────────────────────────────
 * Tabnav — Aero plastic.
 * The loud-only rule applies here too: BOTH segmented variants — segmented
 * and segmented-pill — get the full plastic recipe (frosted capsule + lifted
 * active item with emboss + sheen + bevel, same recipe as Aero Buttons and
 * Cards). Underline / pill / enclosed / plain stay flat to preserve the
 * loud-vs-quiet contrast. The `pill` variant is intentionally calmer than
 * `segmented-pill` — the latter opts into the lifted-capsule treatment.
 * ────────────────────────────────────────────────────────────────────────── */

/* Segmented (both sub-variants) — glass capsule.
   Container bg comes from the base `--cbui-tabnav-segmented-fill` token
   (subtle text-tint), so light Aero stays a soft gray distinct from the
   white active card, and dark Aero stays a subtle light wash distinct
   from the near-black active card. The Aero-specific bits are only
   the backdrop-filter (frosted glass) + a softened border so the
   capsule edge reads against the page underneath. */
[data-flavor="aero"] .cbui-tabnav-segmented .cbui-tabnav-strip-wrapper,
[data-flavor="aero"] .cbui-tabnav-segmented-pill .cbui-tabnav-strip-wrapper {
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    border: 1px solid color-mix(in srgb, var(--color-border) 55%, transparent);
}

/* Segmented (both sub-variants) active item — full Aero plastic recipe:
   emboss + inner sheen + inner bevel. Same shadow stack as Aero Buttons
   and elevated Cards. Uses the `--cbui-tabnav-segmented-active` token
   (not raw `--color-bg-elevated`) so the dark-mode override that lifts
   the surface noticeably above the container takes effect — without
   that, the active card and the container both resolve to bg-elevated
   in dark mode and the contrast disappears. */
[data-flavor="aero"].cbui-cap-button-shadow .cbui-tabnav-segmented .cbui-tabnav-item-active,
[data-flavor="aero"].cbui-cap-button-shadow .cbui-tabnav-segmented .cbui-tabnav-item[aria-selected="true"],
[data-flavor="aero"].cbui-cap-button-shadow .cbui-tabnav-segmented .cbui-tabnav-item[aria-current="page"],
[data-flavor="aero"].cbui-cap-button-shadow .cbui-tabnav-segmented-pill .cbui-tabnav-item-active,
[data-flavor="aero"].cbui-cap-button-shadow .cbui-tabnav-segmented-pill .cbui-tabnav-item[aria-selected="true"],
[data-flavor="aero"].cbui-cap-button-shadow .cbui-tabnav-segmented-pill .cbui-tabnav-item[aria-current="page"] {
    background-color: var(--cbui-tabnav-segmented-active);
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}

/* Non-segmented Aero stays close to the base design; we only tint the bg-fill
   container so it reads as glass rather than a flat panel. */
[data-flavor="aero"] .cbui-tabnav-mod-bg-fill .cbui-tabnav-strip-wrapper {
    background-color: color-mix(in srgb, var(--color-bg-elevated) 70%, transparent);
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
}

/* Reduced-transparency fallback */
[data-flavor="aero"].a11y-reduced-transparency .cbui-tabnav-segmented .cbui-tabnav-strip-wrapper,
[data-flavor="aero"].a11y-reduced-transparency .cbui-tabnav-segmented-pill .cbui-tabnav-strip-wrapper,
[data-flavor="aero"].a11y-reduced-transparency .cbui-tabnav-mod-bg-fill .cbui-tabnav-strip-wrapper {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--color-bg-elevated);
}
@media (prefers-reduced-transparency: reduce) {
    [data-flavor="aero"] .cbui-tabnav-segmented .cbui-tabnav-strip-wrapper,
    [data-flavor="aero"] .cbui-tabnav-segmented-pill .cbui-tabnav-strip-wrapper,
    [data-flavor="aero"] .cbui-tabnav-mod-bg-fill .cbui-tabnav-strip-wrapper {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background-color: var(--color-bg-elevated);
    }
}

/* ──────────────────────────────────────────────────────────────────────────
 * STEPPER — Aero
 *
 * Plastic rule: only the *filled* indicator states get plastic (complete +
 * current). Upcoming stays quiet (outlined). The panel-variant gets full
 * card plastic on its step boxes, mirroring `.cbui-card-elevated`.
 *
 * Connector stays a flat 2px line — the directional accent doesn't need
 * embossing and would muddy the row at small thicknesses.
 * ────────────────────────────────────────────────────────────────────────── */

/* Filled indicators get the plastic recipe. Anchor border keeps shadow
 * legible; emboss + sheen + bevel match the solid-button family. */
[data-flavor="aero"] .cbui-stepper-step-complete .cbui-stepper-step-indicator,
[data-flavor="aero"] .cbui-stepper-step-error .cbui-stepper-step-indicator {
    border-color: var(--aero-edge);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-stepper-step-complete .cbui-stepper-step-indicator,
[data-flavor="aero"].cbui-cap-button-shadow .cbui-stepper-step-error .cbui-stepper-step-indicator {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}

/* Current indicator keeps the focus halo, but adds a subtle inner sheen so
 * it doesn't read as a flat outlined chip on glassy backgrounds. */
[data-flavor="aero"].cbui-cap-button-shadow .cbui-stepper-step-current .cbui-stepper-step-indicator {
    box-shadow:
        var(--cbui-stepper-current-ring),
        var(--aero-inner-sheen);
}

/* dot variant: filled dots get a tiny anchor border for legibility. */
[data-flavor="aero"] .cbui-stepper-dot .cbui-stepper-step-indicator {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--aero-edge) 50%, transparent);
}

/* panel variant — each step box.
 *
 * Aero plastic applies ONLY to the active panels (current + error). Non-
 * current panels are kept neutral, deliberately WITHOUT the Aero-edge
 * border override: we want non-current panels to use the universal
 * `--color-border` so their visual weight matches between light and dark
 * mode. The Aero-edge border (white-tint) was invisible in light and
 * visible in dark — an inconsistency the user (rightfully) flagged.
 */
[data-flavor="aero"] .cbui-stepper-panel .cbui-stepper-step-current {
    background: var(--cbui-stepper-current-panel-bg);
}
[data-flavor="aero"] .cbui-stepper-panel .cbui-stepper-step-error {
    background: var(--cbui-stepper-error-panel-bg);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-stepper-panel .cbui-stepper-step-current {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-stepper-panel .cbui-stepper-step-error {
    box-shadow:
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen),
        var(--aero-inner-bevel);
}
[data-flavor="aero"] .cbui-stepper-panel .cbui-stepper-step-interactive:hover:not(.cbui-stepper-step-disabled, .cbui-stepper-step-current) {
    transform: translateY(-1px);
}

/* Chrome modifiers — frosted bar around the strip. */
[data-flavor="aero"] .cbui-stepper-mod-bg-fill .cbui-stepper-chrome,
[data-flavor="aero"] .cbui-stepper-mod-bg-blur .cbui-stepper-chrome {
    background-color: color-mix(in srgb, var(--color-bg-elevated) 80%, transparent);
    -webkit-backdrop-filter: blur(12px) saturate(160%);
    backdrop-filter: blur(12px) saturate(160%);
    border-color: var(--aero-edge);
}

[data-flavor="aero"].a11y-reduced-transparency .cbui-stepper-mod-bg-fill .cbui-stepper-chrome,
[data-flavor="aero"].a11y-reduced-transparency .cbui-stepper-mod-bg-blur .cbui-stepper-chrome {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--color-bg-elevated);
}
@media (prefers-reduced-transparency: reduce) {
    [data-flavor="aero"] .cbui-stepper-mod-bg-fill .cbui-stepper-chrome,
    [data-flavor="aero"] .cbui-stepper-mod-bg-blur .cbui-stepper-chrome {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background-color: var(--color-bg-elevated);
    }
}

/* ──────────────────────────────────────────────────────────────────────────
 * Inputs + Textareas — Aero plastic.
 *
 * The loud-only rule applies here too: only the `default` Input/Textarea
 * variant picks up the inset sheen + bevel treatment. `filled` and `flush`
 * stay flat in every flavor so the loud-vs-quiet contrast survives.
 *
 * The treatment is intentionally subtle — inputs are working surfaces, not
 * pressable objects. We give them an *inset* sheen (light pouring in from
 * the top edge) and a faint bottom bevel, which together suggest "this is
 * a recessed field carved into glass". No outer drop-shadow (would compete
 * with focus-ring) and no hover-lift transform (an input lifting on hover
 * would be disorienting).
 * ────────────────────────────────────────────────────────────────────────── */
[data-flavor="aero"] .cbui-input-default,
[data-flavor="aero"] .cbui-textarea-default {
    background-color: color-mix(in srgb, var(--color-bg-base) 92%, transparent);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / 0.55),
        inset 0 -1px 0 rgb(0 0 0 / 0.04);
}

[data-flavor="aero"] .cbui-input-default:hover:not(:disabled):not(:focus),
[data-flavor="aero"] .cbui-textarea-default:hover:not(:disabled):not(:focus) {
    background-color: var(--color-bg-base);
}

/* Focus keeps the plastic inset and adds the variant's standard soft
   tone-mixed glow on top — the base CBUI input focus, composed with
   Aero's plastic. Themes that want the gapped ring instead can apply
   the `+focus-ring` modifier or override this rule. */
[data-flavor="aero"] .cbui-input-default:focus,
[data-flavor="aero"] .cbui-input-default:focus-visible,
[data-flavor="aero"] .cbui-input-default[data-cbui-demo-state="focused"],
[data-flavor="aero"] .cbui-textarea-default:focus,
[data-flavor="aero"] .cbui-textarea-default:focus-visible,
[data-flavor="aero"] .cbui-textarea-default[data-cbui-demo-state="focused"] {
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / 0.55),
        inset 0 -1px 0 rgb(0 0 0 / 0.04),
        0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--cbui-input-tone-ring) var(--cbui-input-glow-alpha), transparent);
}

/* Dark mode: less white in the inset highlight (otherwise reads as a
   glowing edge); the bottom bevel goes deeper to suggest depth without a
   fully filled surface. The glow-alpha is bumped on the base rule for
   dark mode so the soft glow stays visible on dark surfaces. */
[data-flavor="aero"][data-scheme="dark"] .cbui-input-default,
[data-flavor="aero"][data-scheme="dark"] .cbui-textarea-default,
.dark [data-flavor="aero"] .cbui-input-default,
.dark [data-flavor="aero"] .cbui-textarea-default {
    background-color: color-mix(in srgb, var(--color-bg-base) 95%, transparent);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / 0.08),
        inset 0 -1px 0 rgb(0 0 0 / 0.30);
}

[data-flavor="aero"][data-scheme="dark"] .cbui-input-default:focus,
[data-flavor="aero"][data-scheme="dark"] .cbui-textarea-default:focus,
.dark [data-flavor="aero"] .cbui-input-default:focus,
.dark [data-flavor="aero"] .cbui-textarea-default:focus {
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / 0.10),
        inset 0 -1px 0 rgb(0 0 0 / 0.30),
        0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--cbui-input-tone-ring) var(--cbui-input-glow-alpha), transparent);
}

/* +focus-ring modifier in Aero: replaces the soft glow with the gap+ring
   composition while keeping the plastic insets. */
[data-flavor="aero"] .cbui-input.cbui-input-mod-focus-ring:focus,
[data-flavor="aero"] .cbui-input.cbui-input-mod-focus-ring:focus-visible,
[data-flavor="aero"] .cbui-textarea.cbui-textarea-mod-focus-ring:focus,
[data-flavor="aero"] .cbui-textarea.cbui-textarea-mod-focus-ring:focus-visible {
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / 0.55),
        inset 0 -1px 0 rgb(0 0 0 / 0.04),
        0 0 0 var(--cbui-input-ring-gap) var(--cbui-input-ring-gap-color),
        0 0 0 calc(var(--cbui-input-ring-gap) + var(--cbui-input-ring-width)) var(--cbui-input-tone-ring);
}

/* Reduced-transparency fallback — drop the insets, keep the glow. */
[data-flavor="aero"].a11y-reduced-transparency .cbui-input-default,
[data-flavor="aero"].a11y-reduced-transparency .cbui-textarea-default {
    background-color: var(--color-bg-base);
    box-shadow: none;
}

[data-flavor="aero"].a11y-reduced-transparency .cbui-input-default:focus,
[data-flavor="aero"].a11y-reduced-transparency .cbui-textarea-default:focus {
    box-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--cbui-input-tone-ring) var(--cbui-input-glow-alpha), transparent);
}

@media (prefers-reduced-transparency: reduce) {
    [data-flavor="aero"] .cbui-input-default,
    [data-flavor="aero"] .cbui-textarea-default {
        background-color: var(--color-bg-base);
        box-shadow: none;
    }
    [data-flavor="aero"] .cbui-input-default:focus,
    [data-flavor="aero"] .cbui-textarea-default:focus {
        box-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--cbui-input-tone-ring) var(--cbui-input-glow-alpha), transparent);
    }
}

/* ──────────────────────────────────────────────────────────────────────
 * Switch — Aero default is the pill thumb (Apple Liquid Glass look).
 *
 * Aero's design language leans toward soft, glassy, mid-radius surfaces;
 * the pill thumb fits that vocabulary far better than the round dot.
 * Re-applies the same widening that the `+pill` modifier does, but as
 * the flavor's default — every Switch in Aero is pill-thumbed unless
 * a future opposing modifier forces it round.
 *
 * Track widens slightly per size to give the wider thumb travel room;
 * the thumb-w / track-w / pad / thumb-d math is identical to
 * `.cbui-switch-mod-pill` in switch.css.
 * ────────────────────────────────────────────────────────────────────── */
[data-flavor="aero"] .cbui-switch {
    --cbui-switch-track-w: 2.625rem;
    --cbui-switch-thumb-w: calc(var(--cbui-switch-thumb-d) * 1.5);
}
[data-flavor="aero"] .cbui-switch.cbui-switch-size-sm {
    --cbui-switch-track-w: 2.125rem;
    --cbui-switch-thumb-w: calc(var(--cbui-switch-thumb-d) * 1.5);
}
[data-flavor="aero"] .cbui-switch.cbui-switch-size-lg {
    --cbui-switch-track-w: 3.125rem;
    --cbui-switch-thumb-w: calc(var(--cbui-switch-thumb-d) * 1.5);
}

/* ──────────────────────────────────────────────────────────────────────
 * Aero — SecretInput action buttons default to icon-only
 *
 * In Aero, the input field already carries its own emboss / sheen
 * plastic and the tinted action buttons compete with it visually.
 * Switching to icon-only by default lets the reveal / copy glyphs read
 * as plain affordances floating in the input gutter while keeping the
 * larger 2.5rem touch-target width unchanged. The token-override
 * approach means call-sites can still force-set the tinted treatment
 * via class composition if they need it.
 *
 * This is the canonical pattern for "flavor opts a component into a
 * different default style" — flip the relevant tokens on the host
 * via a `[data-flavor="X"] .cbui-COMPONENT { … }` rule and the rest of
 * the component CSS cascades naturally.
 * ────────────────────────────────────────────────────────────────────── */
[data-flavor="aero"] .cbui-secret-input {
    --cbui-secret-input-action-bg:        transparent;
    --cbui-secret-input-action-hover-bg:  color-mix(in srgb, currentColor 8%, transparent);
    --cbui-secret-input-action-active-bg: color-mix(in srgb, currentColor 16%, transparent);
    --cbui-secret-input-action-divider:   0;
}

/* ──────────────────────────────────────────────────────────────────────
 * Aero — Table + Timeline (UiData, Wave 4)
 *
 * A data table is quiet content chrome, not a floating object, so Aero
 * stays restrained here: the `bordered` variant picks up the Aero edge +
 * a soft lift, and the header band gets a frosted translucency. No emboss
 * plastic on rows — the eye scans them, plasticity would be noise. The
 * loud-vs-quiet rule still holds: only the icon-marker on a Timeline (a
 * small, deliberate object) gets the embossed plastic.
 * ────────────────────────────────────────────────────────────────────── */
[data-flavor="aero"] .cbui-table-wrap-bordered {
    border-color: var(--aero-edge);
}
[data-flavor="aero"].cbui-cap-button-shadow .cbui-table-wrap-bordered {
    box-shadow:
        0 1px 2px rgb(0 0 0 / 0.06),
        0 4px 12px -6px rgb(0 0 0 / 0.14);
}
[data-flavor="aero"] .cbui-table-head th {
    background-color: color-mix(in srgb, var(--color-bg-subtle) 82%, transparent);
}
[data-flavor="aero"]:not(.a11y-reduced-transparency) .cbui-table-sticky-header .cbui-table-head th {
    backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
    -webkit-backdrop-filter: blur(var(--aero-glass-blur)) saturate(var(--aero-glass-saturation));
}
[data-flavor="aero"].a11y-reduced-transparency .cbui-table-head th {
    background-color: var(--color-bg-subtle);
}
/* A head cell pinned sideways (stickyFirstColumn corner, with or without the select cell) is opaque like the
 * pinned data cells: the translucent fill and the blur above would let the head text of the columns scrolling
 * past shine through it (CB-2939). Same tone, no alpha, no blur. */
[data-flavor="aero"] .cbui-table-sticky-first > .cbui-table-head > tr > th:first-child:not(.cbui-table-colgroup),
[data-flavor="aero"] .cbui-table-sticky-first-select > .cbui-table-head > tr > th:nth-child(-n+2):not(.cbui-table-colgroup) {
    background-color: var(--color-bg-subtle);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
/* The same for a pinned column of the ResourceDisplay (`cbui-table-cell-pinned`: selection and title column, CB-4118):
 * they are pinned by class, not by position, so the two rules above do not reach them. */
[data-flavor="aero"] .cbui-table-head > tr > th.cbui-table-cell-pinned:not(.cbui-table-colgroup) {
    background-color: var(--color-bg-subtle);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* Timeline — only the icon marker (a deliberate small object) gets plastic. */
[data-flavor="aero"].cbui-cap-button-shadow .cbui-timeline-item-marker-icon .cbui-timeline-item-marker {
    box-shadow:
        0 0 0 4px var(--cbui-timeline-marker-bg),
        var(--aero-shadow-emboss),
        var(--aero-inner-sheen);
}

/* ──────────────────────────────────────────────────────────────────────
 * Aero — Disclosure / Attachment (UiComponent)
 *
 * The chat body is a MultiColumnLayout — it carries its own flavor
 * surfaces, so no chat-specific Aero rule is needed. The chat composer
 * field is intentionally left ALONE in every flavor: it must read as a
 * permanent, normal-Textarea-style chrome (Light Mode Aero once swapped
 * its border to --aero-edge / transparent, which made the field look
 * borderless — that is wrong). Only Disclosure detail bodies and
 * Attachment chips pick up the Aero edge.
 * ────────────────────────────────────────────────────────────────────── */
[data-flavor="aero"] .cbui-disclosure-tone-neutral .cbui-disclosure-body,
[data-flavor="aero"] .cbui-disclosure-tone-muted .cbui-disclosure-body,
[data-flavor="aero"] .cbui-attachment {
    border-color: var(--aero-edge);
}

/* ──────────────────────────────────────────────────────────────────────
 * Aero — ListGroup (UiComponent)
 *
 * Aero deactivates the hover surface on a list. The plastic chrome of the
 * surrounding window / card already carries the "clickable" affordance —
 * a hover wash inside reads loud. The CLICK / FOCUS surfaces (active /
 * selected / focus-visible) stay; only the gratuitous hover bg is muted.
 * Tonal hover (danger / warning / etc.) is also muted for the same reason.
 * ────────────────────────────────────────────────────────────────────── */
[data-flavor="aero"] .cbui-list-interactive .cbui-list-item:hover,
[data-flavor="aero"] .cbui-list-interactive .cbui-list-item:active,
[data-flavor="aero"] .cbui-list-interactive .cbui-list-item-tone-danger:hover,
[data-flavor="aero"] .cbui-list-interactive .cbui-list-item-tone-warning:hover,
[data-flavor="aero"] .cbui-list-interactive .cbui-list-item-tone-success:hover,
[data-flavor="aero"] .cbui-list-interactive .cbui-list-item-tone-info:hover {
    background-color: transparent;
}
/* Active hover is allowed to lift a touch (existing rule), but its hover
   tint relies on the base hover bg's existence — re-state the active wash
   so the existing :hover override above does not flatten it. */
[data-flavor="aero"] .cbui-list-interactive .cbui-list-item-active:hover {
    background-color: var(--cbui-list-item-active-bg);
}

/* The hover-based divider suppression in list.css is already scoped to a
 * positive flavor list (clean / soft / sharp / glass) — it does NOT fire
 * in Aero. No override needed here. The active / selected / label /
 * separator divider suppressions DO still fire (those reflect intrinsic
 * row state, not interaction) — that's correct: hovering the active row
 * must NOT reveal a divider that was hidden because of the active state. */

/* cbui/variants/glass */
/* ==========================================================================
 * Glass Flavor — Full Liquid-Glass Treatment
 *
 * Strong backdrop-filter, saturation boost, tinted borders.
 * Applied via [data-flavor="glass"] on a container.
 * ========================================================================== */

[data-flavor="glass"] {
    --glass-blur: 20px;
    --glass-saturation: 180%;
    --glass-opacity: 0.7;
    --glass-border-opacity: 0.3;
}

/* Glass treatment is reserved for the LOUD card variants (outlined / elevated)
   — flat stays the affordance for "embedded card on a tinted surface" and
   keeps its solid bg-subtle. Same loud-vs-quiet rule as Aero. */
[data-flavor="glass"] .cbui-card-outlined,
[data-flavor="glass"] .cbui-card-elevated {
    position: relative;
    overflow: hidden;
}

[data-flavor="glass"] .cbui-card-outlined::before,
[data-flavor="glass"] .cbui-card-elevated::before {
    content: '';
    position: absolute;
    inset: 0;
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    background: color-mix(in srgb, var(--color-surface) calc(var(--glass-opacity) * 100%), transparent);
    border: 1px solid color-mix(in srgb, white calc(var(--glass-border-opacity) * 100%), transparent);
    border-radius: inherit;
    z-index: 0;
}

[data-flavor="glass"] .cbui-card-outlined > *,
[data-flavor="glass"] .cbui-card-elevated > * {
    position: relative;
    z-index: 1;
}

[data-flavor="glass"] .cbui-button {
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}

[data-flavor="glass"] .cbui-button-ghost {
    background: color-mix(in srgb, var(--color-surface) 60%, transparent);
    border: 1px solid color-mix(in srgb, white 20%, transparent);
}

[data-flavor="glass"] .cbui-button-ghost:hover {
    background: color-mix(in srgb, var(--color-surface) 80%, transparent);
}

/* Glass frosting is reserved for the LOUD Input/Textarea variant (default).
   `filled` and `flush` are the quiet variants and stay flat — same loud-vs-
   quiet rule as Cards, Buttons, Navs. */
[data-flavor="glass"] .cbui-input-default,
[data-flavor="glass"] .cbui-textarea-default {
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    background-color: color-mix(in srgb, var(--color-surface) 50%, transparent);
}

[data-flavor="glass"] .cbui-input-default:focus,
[data-flavor="glass"] .cbui-textarea-default:focus {
    background-color: color-mix(in srgb, var(--color-surface) 70%, transparent);
}

[data-flavor="glass"] .cbui-modal-overlay {
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

[data-flavor="glass"] .cbui-dropdown-menu {
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    background: color-mix(in srgb, var(--color-surface) calc(var(--glass-opacity) * 100%), transparent);
    border: 1px solid color-mix(in srgb, white calc(var(--glass-border-opacity) * 100%), transparent);
}

/* Reduced transparency support */
@media (prefers-reduced-transparency: reduce) {
    [data-flavor="glass"] .cbui-card-outlined::before,
    [data-flavor="glass"] .cbui-card-elevated::before,
    [data-flavor="glass"] .cbui-button,
    [data-flavor="glass"] .cbui-button-ghost,
    [data-flavor="glass"] .cbui-input-default,
    [data-flavor="glass"] .cbui-textarea-default,
    [data-flavor="glass"] .cbui-modal-overlay,
    [data-flavor="glass"] .cbui-dropdown-menu {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }

    [data-flavor="glass"] .cbui-card-outlined::before,
    [data-flavor="glass"] .cbui-card-elevated::before {
        background: var(--color-surface);
        opacity: 1;
    }

    [data-flavor="glass"] .cbui-button-ghost,
    [data-flavor="glass"] .cbui-input-default,
    [data-flavor="glass"] .cbui-textarea-default,
    [data-flavor="glass"] .cbui-dropdown-menu {
        background: var(--color-surface);
    }
}

/* Dark mode adjustments */
.dark [data-flavor="glass"] {
    --glass-opacity: 0.5;
    --glass-border-opacity: 0.15;
}

/* ──────────────────────────────────────────────────────────────────────────
 * Tabnav — Glass treatment for both segmented sub-variants and the bg-fill
 * / bg-blur modifiers. Same loud-only rule as Aero: only the segmented
 * capsules get the full frosted-backdrop + raised-active-item treatment.
 * ────────────────────────────────────────────────────────────────────────── */
/* Glass container — backdrop blur + border only. Background colour comes
   from the base `--cbui-tabnav-segmented-fill` token so the container is
   visibly distinct from the active card in both light and dark schemes
   (instead of resolving to the same surface colour). */
[data-flavor="glass"] .cbui-tabnav-segmented .cbui-tabnav-strip-wrapper,
[data-flavor="glass"] .cbui-tabnav-segmented-pill .cbui-tabnav-strip-wrapper,
[data-flavor="glass"] .cbui-tabnav-mod-bg-fill .cbui-tabnav-strip-wrapper {
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    border: 1px solid color-mix(in srgb, white calc(var(--glass-border-opacity) * 100%), transparent);
}

/* Use the `--cbui-tabnav-segmented-active` token so the dark-mode lift
   (24% white-mix) takes effect — without it, the active surface would
   resolve to a colour close to the glass container and lose contrast. */
[data-flavor="glass"] .cbui-tabnav-segmented .cbui-tabnav-item-active,
[data-flavor="glass"] .cbui-tabnav-segmented .cbui-tabnav-item[aria-selected="true"],
[data-flavor="glass"] .cbui-tabnav-segmented .cbui-tabnav-item[aria-current="page"],
[data-flavor="glass"] .cbui-tabnav-segmented-pill .cbui-tabnav-item-active,
[data-flavor="glass"] .cbui-tabnav-segmented-pill .cbui-tabnav-item[aria-selected="true"],
[data-flavor="glass"] .cbui-tabnav-segmented-pill .cbui-tabnav-item[aria-current="page"] {
    background-color: var(--cbui-tabnav-segmented-active);
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.08), 0 0 0 1px rgb(255 255 255 / 0.18);
}

@media (prefers-reduced-transparency: reduce) {
    [data-flavor="glass"] .cbui-tabnav-segmented .cbui-tabnav-strip-wrapper,
    [data-flavor="glass"] .cbui-tabnav-segmented-pill .cbui-tabnav-strip-wrapper,
    [data-flavor="glass"] .cbui-tabnav-mod-bg-fill .cbui-tabnav-strip-wrapper {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background: var(--color-bg-elevated);
    }
}

/* ──────────────────────────────────────────────────────────────────────────
 * Stepper — Glass treatment. Frosted backdrops on the panel-variant boxes
 * and the chrome modifier. Indicators stay solid (a translucent step number
 * reads as a bug, not a feature).
 * ────────────────────────────────────────────────────────────────────────── */
/* Glass panels — only the active panels (current + error) carry the
 * frosted backdrop + tinted background. Non-current panels keep the
 * universal `--color-border` (set by the base panel rule) — using the
 * Glass-tinted-white border previously gave inconsistent visibility
 * between light (invisible) and dark (visible) modes. */
/* Glass identity on active panels = backdrop-filter (frosted view through
 * to the page bg behind), NOT a white-mix surface. The previous white-mix
 * recipe made the active panel render as a LIGHT surface in dark mode
 * (~rgb 163) — which collided with the description text colour
 * (`--color-text-secondary`, also ~rgb 163 in dark) and rendered the
 * description invisible. The base panel-bg recipe (tone-mix on transparent)
 * adapts naturally to both schemes: dark page → tinted-dark panel,
 * light page → tinted-light panel. Text contrast stays predictable. */
[data-flavor="glass"] .cbui-stepper-panel .cbui-stepper-step-current,
[data-flavor="glass"] .cbui-stepper-panel .cbui-stepper-step-error {
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
}

[data-flavor="glass"] .cbui-stepper-mod-bg-fill .cbui-stepper-chrome,
[data-flavor="glass"] .cbui-stepper-mod-bg-blur .cbui-stepper-chrome {
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    border: 1px solid color-mix(in srgb, white calc(var(--glass-border-opacity) * 100%), transparent);
    background: color-mix(in srgb, white calc(var(--glass-opacity) * 100%), transparent);
}

@media (prefers-reduced-transparency: reduce) {
    [data-flavor="glass"] .cbui-stepper-panel .cbui-stepper-step,
    [data-flavor="glass"] .cbui-stepper-mod-bg-fill .cbui-stepper-chrome,
    [data-flavor="glass"] .cbui-stepper-mod-bg-blur .cbui-stepper-chrome {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background: var(--color-bg-elevated);
    }
}

/* ──────────────────────────────────────────────────────────────────────
 * Glass — Table + Timeline (UiData, Wave 4)
 *
 * Glass frosts the header band of a table and the icon / avatar markers of
 * a timeline. The table body stays clear so the data reads at full
 * contrast — frosting rows would hurt legibility. Gated on
 * prefers-reduced-transparency.
 * ────────────────────────────────────────────────────────────────────── */
[data-flavor="glass"] .cbui-table-wrap-bordered {
    border-color: color-mix(in srgb, white calc(var(--glass-border-opacity) * 100%), var(--color-border));
}
[data-flavor="glass"] .cbui-table-head th {
    background-color: color-mix(in srgb, var(--color-bg-subtle) calc(var(--glass-opacity) * 100% + 30%), transparent);
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
}
[data-flavor="glass"] .cbui-timeline-item-marker-icon .cbui-timeline-item-marker,
[data-flavor="glass"] .cbui-timeline-item-marker-avatar .cbui-timeline-item-marker {
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
}

@media (prefers-reduced-transparency: reduce) {
    [data-flavor="glass"] .cbui-table-head th {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background-color: var(--color-bg-subtle);
    }
    [data-flavor="glass"] .cbui-timeline-item-marker-icon .cbui-timeline-item-marker,
    [data-flavor="glass"] .cbui-timeline-item-marker-avatar .cbui-timeline-item-marker {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
}

/* ──────────────────────────────────────────────────────────────────────
 * Glass — Window (UiWindow, Wave 3a)
 *
 * Glass treatment is reserved for the LOUD floating frame — a floating
 * window is a deliberate object, so the liquid-glass surface is right
 * here. The docked frame stays solid (a working surface). Both the frame
 * surface and the header band frost; the body stays clear so window
 * content reads at full contrast (same call as the table body).
 * ────────────────────────────────────────────────────────────────────── */
[data-flavor="glass"] .cbui-window-frame-floating {
    background-color: color-mix(in srgb, var(--color-surface) calc(var(--glass-opacity) * 100%), transparent);
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
    /* Drive the border via the token so the focus cascade keeps working. */
    --cbui-window-frame-border: color-mix(in srgb, white calc(var(--glass-border-opacity) * 100%), var(--color-border));
}
[data-flavor="glass"] .cbui-window-frame-floating .cbui-window-frame-header {
    /* Frost the focus-driven --cbui-window-header-bg token so the focus
       tint flows through. */
    background-color: color-mix(in srgb, var(--cbui-window-header-bg) calc(var(--glass-opacity) * 100% + 20%), transparent);
}
[data-flavor="glass"] .cbui-window-frame-floating .cbui-window-frame-body {
    background-color: transparent;
}
/* Focused window — the accent border, explicit so it beats the border
   token rule above regardless of source order. */
[data-flavor="glass"] .cbui-window-focused .cbui-window-frame {
    --cbui-window-frame-border: var(--cbui-window-accent);
}

@media (prefers-reduced-transparency: reduce) {
    [data-flavor="glass"] .cbui-window-frame-floating {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background-color: var(--color-surface);
    }
    [data-flavor="glass"] .cbui-window-frame-floating .cbui-window-frame-header {
        background-color: var(--cbui-window-header-bg);
    }
    [data-flavor="glass"] .cbui-window-frame-floating .cbui-window-frame-body {
        background-color: var(--color-surface);
    }
}

/* cbui/base */
/* ==========================================================================
 * CloudBringer UI Framework — base.css
 *
 * Reset + layer order declarations for the new Ui* module family.
 * No @import statements here — the cbui.js entrypoint loads every stylesheet
 * individually so AssetMapper can resolve each hashed URL correctly.
 *
 * Loaded via: import 'cbui/base' from assets/cbui.js
 * ========================================================================== */

/* Layer order declaration. Anything unlayered wins over these. */
@layer cbui-reset, cbui-base, components, cbui-utilities;

/* --------------------------------------------------------------------------
 * Color-scheme — tells the browser the surface tone so native UI
 * (date / time / color pickers, scrollbars, form-control defaults)
 * paints itself accordingly. Without this property the browser renders
 * a light picker popup even when our scheme is dark, which feels
 * broken. Storybook scopes carry data-scheme so each side picks its
 * own native UI tone; in production .dark on <html> drives it.
 * -------------------------------------------------------------------------- */
[data-scheme="light"] { color-scheme: light; }
[data-scheme="dark"]  { color-scheme: dark; }
.dark                 { color-scheme: dark; }

/* --------------------------------------------------------------------------
 * Minimal Reset — scoped to cbui/cbui_storybook entrypoints only.
 * Intentionally small. Not a full Tailwind Preflight clone.
 * -------------------------------------------------------------------------- */
@layer cbui-reset {
    *, *::before, *::after { box-sizing: border-box; }

    html { -webkit-text-size-adjust: 100%; line-height: 1.5; }

    body {
        margin: 0;
        font-family: var(--font-family, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
        color: var(--color-text);
        background-color: var(--color-bg-base);
    }

    h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }

    ul, ol { margin: 0; padding: 0; list-style: none; }

    a { color: inherit; text-decoration: inherit; }

    img, svg, video, canvas { display: block; max-width: 100%; }

    button, input, select, textarea {
        font: inherit;
        color: inherit;
        margin: 0;
    }

    button { background: none; border: 0; cursor: pointer; }

    table { border-collapse: collapse; border-spacing: 0; }

    code, kbd, samp, pre {
        font-family: var(--font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
        font-size: 1em;
    }
}

/* --------------------------------------------------------------------------
 * Z-index bands — the framework-wide stacking scale.
 *
 * Three ordered bands: page content < window stack < free overlays.
 * UiWindow's WindowManager re-sorts the open windows and assigns them
 * z-indices starting at --cbui-z-window-base; the band is wide enough for
 * far more windows than anyone opens at once. Free overlays that are NOT
 * native <dialog> (Popover / Tooltip / Dropdown) belong above the window
 * band so they escape a focused window.
 *
 * Native <dialog> overlays (Modal / Drawer) use .showModal() and the
 * browser top-layer — they sit above ALL of these automatically and need
 * no token. That is why the codebase had no z-index system until windows
 * arrived: nothing before windows needed cross-component coordination.
 *
 * Framework-structural, mode/flavor-independent — lives here, not in a
 * Flavor. -------------------------------------------------------------- */
@layer cbui-base {
    :root {
        --cbui-z-page: 0;
        --cbui-z-window-base: 1000;
        --cbui-z-window-ceiling: 1899;
        --cbui-z-overlay: 1900;
        --cbui-z-toast: 1950;

        /* ------------------------------------------------------------------
         * Bar / Header height scale — the shared vocabulary used by every
         * "bar-shaped" component (Navbar, Header, Toolbar) AND by the
         * dedicated header section of card-shaped components (Card, Modal,
         * Drawer, WindowFrame). Surfaces that need to align — e.g. the
         * Workspace top-bar and the top-Drawer header — pick the same
         * stage so their chromes match pixel-for-pixel.
         *
         * Default per component = md.
         * -------------------------------------------------------------- */
        --cbui-bar-h-xs: 2.25rem;   /* 36px — compact toolbars / footer chips */
        --cbui-bar-h-sm: 2.75rem;   /* 44px — sub-navigation, dense surfaces */
        --cbui-bar-h-md: 3.25rem;   /* 52px — the everyday default */
        --cbui-bar-h-lg: 4rem;      /* 64px — hero / app-shell header */
        --cbui-bar-h-xl: 4.75rem;   /* 76px — oversized / marketing chrome */

        /* ------------------------------------------------------------------
         * Motion scale (CB-2645) — the shared vocabulary for how long and how
         * fast something moves. Both durations become 0ms under reduced motion
         * (OS setting or the .a11y-reduced-motion class), so anything that
         * reads them stands still without a rule of its own.
         * -------------------------------------------------------------- */
        --cbui-motion-duration-short: 150ms;
        --cbui-motion-duration-medium: 300ms;
        --cbui-motion-ease-standard: cubic-bezier(0.2, 0, 0, 1);
        --cbui-motion-ease-exit: cubic-bezier(0.4, 0, 1, 1);
    }

    @media (prefers-reduced-motion: reduce) {
        :root {
            --cbui-motion-duration-short: 0ms;
            --cbui-motion-duration-medium: 0ms;
        }
    }

    .a11y-reduced-motion {
        --cbui-motion-duration-short: 0ms;
        --cbui-motion-duration-medium: 0ms;
    }
}

/* --------------------------------------------------------------------------
 * Image utilities — real, reusable classes for any <img> (and background
 * boxes). These are the centralised image treatments: change them once here
 * and every consumer (Avatar, FilePreview, prose, media grids, gallery
 * tiles) gets the update. There is intentionally NO Image Twig component —
 * `loading="lazy"`/`decoding="async"` are native HTML, the rest is CSS, so
 * these utility classes are the right place for the abstraction.
 *
 * Lives in the cbui-utilities layer (last layer → wins over component
 * defaults) so a utility on an <img> inside a component always applies.
 * Token-only: every value flows through theme custom properties, so all of
 * it is light/dark safe automatically.
 *
 * Demonstrated live in Storybook → Content → Images.
 * -------------------------------------------------------------------------- */
@layer cbui-utilities {

    /* ── Responsive base ──────────────────────────────────────────────
     * The everyday image: fills its container width, never overflows,
     * keeps its intrinsic proportions. `display:block` also kills the
     * inline-baseline whitespace gap. (The reset already gives every
     * <img> max-width:100% + display:block; this class adds the explicit
     * width:100% + height:auto so the image actively fills its parent.) */
    .cbui-img {
        display: block;
        width: 100%;
        height: auto;
        max-width: 100%;
    }

    /* ── Aspect-ratio box ─────────────────────────────────────────────
     * Reserves the final height BEFORE the image loads (no layout shift /
     * no CLS): the box gets its height from aspect-ratio while the image
     * is still downloading. Pair with an object-fit utility so the source
     * fills the reserved box. Default ratio is 1:1; add a ratio modifier
     * to change it. `width:100%` makes the box track its container. */
    .cbui-aspect {
        display: block;
        width: 100%;
        aspect-ratio: 1 / 1;
    }

    .cbui-aspect-square  { aspect-ratio: 1 / 1; }
    .cbui-aspect-4-3     { aspect-ratio: 4 / 3; }
    .cbui-aspect-3-2     { aspect-ratio: 3 / 2; }
    .cbui-aspect-16-9    { aspect-ratio: 16 / 9; }
    .cbui-aspect-3-4     { aspect-ratio: 3 / 4; }   /* portrait */
    .cbui-aspect-21-9    { aspect-ratio: 21 / 9; }  /* cinematic */

    /* ── Object-fit ───────────────────────────────────────────────────
     * How a fixed-size box is filled. `cover` crops to fill (no
     * distortion, edges clipped); `contain` fits the whole image inside
     * (no crop, leaves gutters). Both require the box to have a height —
     * either an aspect-ratio box above, or an explicit height/width. */
    .cbui-img-cover {
        width: 100%;
        height: 100%;
        object-fit: cover;
        object-position: center;
    }

    .cbui-img-contain {
        width: 100%;
        height: 100%;
        object-fit: contain;
        object-position: center;
    }

    /* ── Letterbox ────────────────────────────────────────────────────
     * `contain` leaves gutters where the image doesn't fill the box. Left
     * transparent those gutters show whatever is behind, which reads as a
     * hole. The letterbox fills the gutter with a neutral surface token
     * (NOT transparent) so the framing is intentional and dark-mode safe.
     * Apply to the BOX; the contained <img> goes inside. */
    .cbui-img-letterbox {
        background-color: var(--color-bg-muted);
        overflow: hidden;
    }

    /* ── Rounded steps ────────────────────────────────────────────────
     * The reset applies NO radius to images — opt in with one of these.
     * Steps map to the shape tokens so they stay in sync with the active
     * flavor. `full` produces a circle ONLY on a 1:1 box; on any other
     * ratio it yields an ellipse — pair it with .cbui-aspect-square (or an
     * equal width/height). Use overflow:hidden on aspect/letterbox boxes
     * so the rounding clips the contained image too. */
    .cbui-img-rounded-sm   { border-radius: var(--radius-sm); overflow: hidden; }
    .cbui-img-rounded-md   { border-radius: var(--radius-md); overflow: hidden; }
    .cbui-img-rounded-lg   { border-radius: var(--radius-lg); overflow: hidden; }
    .cbui-img-rounded-xl   { border-radius: var(--radius-xl); overflow: hidden; }
    .cbui-img-rounded-full { border-radius: var(--radius-full); overflow: hidden; }
}

/* cbui/components/alert */
/* cbui-alert
 *
 * Two-axis variant model (shared with Button/Badge):
 *   variant = visual treatment: solid | outline | ghost | (null → auto)
 *   tone    = semantic colour:  neutral | primary | accent | success | danger | warning | info
 *
 * Modifiers (composable via + in the variant string):
 *   border-pale          — de-saturates the box border (outline only)
 *   bg-fill              — adds soft tinted background (outline / ghost)
 *   border-accent-start  — 3px accent bar on inline-start (LTR: left)
 *   border-accent-end    — 3px accent bar on inline-end   (LTR: right)
 *   border-accent-top    — 3px accent bar on block-start  (top)
 *   border-accent-bottom — 3px accent bar on block-end    (bottom)
 *   bg-gradient-horizontal — subtle left→right tint gradient
 *
 * Each variant+tone combination sets --cbui-alert-accent to the correct
 * tone colour so accent-bar modifiers do not need per-tone rules.
 *
 * AUTO variant (.cbui-alert-auto): flavor-driven default.
 *   Base CSS applies the Clean flavor default (ghost+bg-fill+border-accent-start).
 *   Override with [data-flavor="X"] .cbui-alert-auto rules in flavor CSS files.
 */

@layer components {
  /* ─────────────────────────────────────────────────────────────────── */
  /*  BASE                                                                */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: var(--alert-padding, 1rem);
    border-radius: var(--alert-radius);
    border: var(--alert-border-width) solid transparent;
    font-size: var(--font-size-md, 1rem);
    line-height: 1.5;
    position: relative;
  }

  /* Same trap as `.cbui-button[hidden]` (button.css): the base rule's
     explicit `display: flex` beats the User-Agent `[hidden] { display: none }`
     rule, so an Alert toggled hidden by JS stayed visible (CB-1503 browser
     defect A — the "Prüfen" step showed both the success AND failure Alert
     at once after a probe, because neither ever actually disappeared). */
  .cbui-alert[hidden] {
    display: none;
  }

  .cbui-alert-icon {
    flex-shrink: 0;
    display: inline-flex;
    font-size: 1.375rem; /* 22px */
    margin-top: 0.0625rem;
  }

  .cbui-alert-body {
    flex: 1;
    min-width: 0;
  }

  .cbui-alert-title {
    font-weight: var(--font-weight-semibold, 600);
    margin-bottom: 0.25rem;
  }

  .cbui-alert-dismiss {
    flex-shrink: 0;
    align-self: flex-start;
    /* Pull into the alert's own padding so it doesn't add visual width */
    margin-block-start: -0.25rem;
    margin-inline-end: -0.25rem;
  }

  /* On solid alerts the ghost button's tone colour is invisible against
     the filled background — inherit the alert's contrast text instead.
     Extra .cbui-button class raises specificity above the ghost tone rules. */
  .cbui-alert-solid .cbui-alert-dismiss.cbui-button {
    color: currentColor;
    opacity: 0.75;
    &:hover { opacity: 1; background-color: var(--cbui-shade-soft); }
  }

  .cbui-alert-hidden {
    display: none;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  FLUSH — edgeless page/window band (the "Banner" use-case)          */
  /*                                                                     */
  /*  Zeros all four corners and the inline margin so the alert reads    */
  /*  as a full-bleed band whose shape follows its container. The        */
  /*  border-radius shorthand wins over the flavor --alert-radius AND    */
  /*  over the *-sharp accent-bar corner overrides.                      */
  /*                                                                     */
  /*  In a padded host, opt the host into .cbui-flush-host so the band   */
  /*  bleeds out to the container edges (negative inline margin equal to */
  /*  the host's inline padding, exposed as --flush-host-pad).           */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-flush {
    border-radius: 0;
    margin-inline: 0;
  }

  .cbui-flush-host > .cbui-alert-flush {
    margin-inline: calc(-1 * var(--flush-host-pad, 1.5rem));
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  COMPACT density — tighter band, vertically centred                 */
  /*                                                                     */
  /*  Orthogonal to flush. Thinner block padding + centre alignment for  */
  /*  a ribbon feel. Keeps the inline padding from --alert-padding.      */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-compact {
    align-items: center;
    padding-block: 0.5rem;
  }
  /* Title margin collapses the centred single-line ribbon — drop it. */
  .cbui-alert-compact .cbui-alert-title {
    margin-bottom: 0;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  AUTO variant — flavor-driven default                               */
  /*                                                                     */
  /*  Clean flavor default: ghost+bg-fill+border-accent-start.           */
  /*  Override per flavor in the flavor CSS file:                         */
  /*    [data-flavor="X"] .cbui-alert-auto { ... }                       */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-auto {
    border: none;
    border-inline-start: var(--alert-accent-width, 3px) solid;
  }
  .cbui-alert-auto.cbui-alert-tone-neutral {
    background-color: var(--color-bg-subtle);
    border-color: var(--color-border-strong);
    color: var(--color-text-secondary);
    --cbui-alert-accent: var(--color-border-strong);
  }
  .cbui-alert-auto.cbui-alert-tone-primary {
    background-color: color-mix(in srgb, var(--color-primary) 10%, transparent);
    border-color: var(--color-primary);
    color: var(--color-primary);
    --cbui-alert-accent: var(--color-primary);
  }
  .cbui-alert-auto.cbui-alert-tone-accent {
    background-color: color-mix(in srgb, var(--color-accent) 10%, transparent);
    border-color: var(--color-accent);
    color: var(--color-accent);
    --cbui-alert-accent: var(--color-accent);
  }
  .cbui-alert-auto.cbui-alert-tone-info {
    background-color: color-mix(in srgb, var(--color-info) 10%, transparent);
    border-color: var(--color-info);
    color: var(--color-info);
    --cbui-alert-accent: var(--color-info);
  }
  .cbui-alert-auto.cbui-alert-tone-success {
    background-color: color-mix(in srgb, var(--color-success) 10%, transparent);
    border-color: var(--color-success);
    color: var(--color-success);
    --cbui-alert-accent: var(--color-success);
  }
  .cbui-alert-auto.cbui-alert-tone-warning {
    background-color: color-mix(in srgb, var(--color-warning) 10%, transparent);
    border-color: var(--color-warning);
    color: var(--color-warning);
    --cbui-alert-accent: var(--color-warning);
  }
  .cbui-alert-auto.cbui-alert-tone-danger {
    background-color: color-mix(in srgb, var(--color-danger) 10%, transparent);
    border-color: var(--color-danger);
    color: var(--color-danger);
    --cbui-alert-accent: var(--color-danger);
  }

  /* Dark mode: tints need more presence on dark backgrounds */
  .dark .cbui-alert-auto.cbui-alert-tone-primary,  [data-scheme="dark"] .cbui-alert-auto.cbui-alert-tone-primary  { background-color: color-mix(in srgb, var(--color-primary) 16%, transparent); }
  .dark .cbui-alert-auto.cbui-alert-tone-accent,   [data-scheme="dark"] .cbui-alert-auto.cbui-alert-tone-accent   { background-color: color-mix(in srgb, var(--color-accent)  16%, transparent); }
  .dark .cbui-alert-auto.cbui-alert-tone-info,     [data-scheme="dark"] .cbui-alert-auto.cbui-alert-tone-info     { background-color: color-mix(in srgb, var(--color-info)    16%, transparent); }
  .dark .cbui-alert-auto.cbui-alert-tone-success,  [data-scheme="dark"] .cbui-alert-auto.cbui-alert-tone-success  { background-color: color-mix(in srgb, var(--color-success) 16%, transparent); }
  .dark .cbui-alert-auto.cbui-alert-tone-warning,  [data-scheme="dark"] .cbui-alert-auto.cbui-alert-tone-warning  { background-color: color-mix(in srgb, var(--color-warning) 16%, transparent); }
  .dark .cbui-alert-auto.cbui-alert-tone-danger,   [data-scheme="dark"] .cbui-alert-auto.cbui-alert-tone-danger   { background-color: color-mix(in srgb, var(--color-danger)  16%, transparent); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  SOLID variant — filled background, contrast-colour text            */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-solid.cbui-alert-tone-neutral {
    background-color: var(--color-text);
    color: var(--color-bg-base);
    --cbui-alert-accent: color-mix(in srgb, var(--color-text) 60%, black);
  }
  .cbui-alert-solid.cbui-alert-tone-primary {
    background-color: var(--color-primary);
    color: var(--color-text-on-primary);
    --cbui-alert-accent: color-mix(in srgb, var(--color-primary) 60%, black);
  }
  .cbui-alert-solid.cbui-alert-tone-accent {
    background-color: var(--color-accent);
    color: var(--color-text-on-accent);
    --cbui-alert-accent: color-mix(in srgb, var(--color-accent) 60%, black);
  }
  .cbui-alert-solid.cbui-alert-tone-info {
    background-color: var(--color-info);
    color: var(--color-text-on-info);
    --cbui-alert-accent: color-mix(in srgb, var(--color-info) 60%, black);
  }
  .cbui-alert-solid.cbui-alert-tone-success {
    background-color: var(--color-success);
    color: var(--color-text-on-success);
    --cbui-alert-accent: color-mix(in srgb, var(--color-success) 60%, black);
  }
  .cbui-alert-solid.cbui-alert-tone-warning {
    background-color: var(--color-warning);
    color: var(--color-text-on-warning);
    --cbui-alert-accent: color-mix(in srgb, var(--color-warning) 60%, black);
  }
  .cbui-alert-solid.cbui-alert-tone-danger {
    background-color: var(--color-danger);
    color: var(--color-text-on-danger);
    --cbui-alert-accent: color-mix(in srgb, var(--color-danger) 60%, black);
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  OUTLINE variant — transparent + tone-coloured box border + text    */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-outline {
    background-color: transparent;
  }
  .cbui-alert-outline.cbui-alert-tone-neutral {
    border-color: var(--color-border-strong);
    color: var(--color-text-secondary);
    --cbui-alert-accent: var(--color-border-strong);
  }
  .cbui-alert-outline.cbui-alert-tone-primary {
    border-color: var(--color-primary);
    color: var(--color-primary);
    --cbui-alert-accent: var(--color-primary);
  }
  .cbui-alert-outline.cbui-alert-tone-accent {
    border-color: var(--color-accent);
    color: var(--color-accent);
    --cbui-alert-accent: var(--color-accent);
  }
  .cbui-alert-outline.cbui-alert-tone-info {
    border-color: var(--color-info);
    color: var(--color-info);
    --cbui-alert-accent: var(--color-info);
  }
  .cbui-alert-outline.cbui-alert-tone-success {
    border-color: var(--color-success);
    color: var(--color-success);
    --cbui-alert-accent: var(--color-success);
  }
  .cbui-alert-outline.cbui-alert-tone-warning {
    border-color: var(--color-warning);
    color: var(--color-warning);
    --cbui-alert-accent: var(--color-warning);
  }
  .cbui-alert-outline.cbui-alert-tone-danger {
    border-color: var(--color-danger);
    color: var(--color-danger);
    --cbui-alert-accent: var(--color-danger);
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  GHOST variant — transparent, no border, tone-coloured text         */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-ghost {
    background-color: transparent;
    border: none;
  }
  .cbui-alert-ghost.cbui-alert-tone-neutral {
    color: var(--color-text-secondary);
    --cbui-alert-accent: var(--color-border-strong);
  }
  .cbui-alert-ghost.cbui-alert-tone-primary {
    color: var(--color-primary);
    --cbui-alert-accent: var(--color-primary);
  }
  .cbui-alert-ghost.cbui-alert-tone-accent {
    color: var(--color-accent);
    --cbui-alert-accent: var(--color-accent);
  }
  .cbui-alert-ghost.cbui-alert-tone-info {
    color: var(--color-info);
    --cbui-alert-accent: var(--color-info);
  }
  .cbui-alert-ghost.cbui-alert-tone-success {
    color: var(--color-success);
    --cbui-alert-accent: var(--color-success);
  }
  .cbui-alert-ghost.cbui-alert-tone-warning {
    color: var(--color-warning);
    --cbui-alert-accent: var(--color-warning);
  }
  .cbui-alert-ghost.cbui-alert-tone-danger {
    color: var(--color-danger);
    --cbui-alert-accent: var(--color-danger);
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  border-pale modifier — de-saturates the box border (outline only)  */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-outline.cbui-alert-mod-border-pale.cbui-alert-tone-neutral  { border-color: color-mix(in srgb, var(--color-border-strong) 60%, transparent); }
  .cbui-alert-outline.cbui-alert-mod-border-pale.cbui-alert-tone-primary  { border-color: color-mix(in srgb, var(--color-primary) 45%, transparent); }
  .cbui-alert-outline.cbui-alert-mod-border-pale.cbui-alert-tone-accent   { border-color: color-mix(in srgb, var(--color-accent)  45%, transparent); }
  .cbui-alert-outline.cbui-alert-mod-border-pale.cbui-alert-tone-info     { border-color: color-mix(in srgb, var(--color-info)    45%, transparent); }
  .cbui-alert-outline.cbui-alert-mod-border-pale.cbui-alert-tone-success  { border-color: color-mix(in srgb, var(--color-success) 45%, transparent); }
  .cbui-alert-outline.cbui-alert-mod-border-pale.cbui-alert-tone-warning  { border-color: color-mix(in srgb, var(--color-warning) 45%, transparent); }
  .cbui-alert-outline.cbui-alert-mod-border-pale.cbui-alert-tone-danger   { border-color: color-mix(in srgb, var(--color-danger)  45%, transparent); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  bg-fill modifier — adds soft tinted background (outline / ghost)   */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-mod-bg-fill.cbui-alert-tone-neutral  { background-color: var(--color-bg-subtle); }
  .cbui-alert-mod-bg-fill.cbui-alert-tone-primary  { background-color: color-mix(in srgb, var(--color-primary) 10%, transparent); }
  .cbui-alert-mod-bg-fill.cbui-alert-tone-accent   { background-color: color-mix(in srgb, var(--color-accent)  10%, transparent); }
  .cbui-alert-mod-bg-fill.cbui-alert-tone-info     { background-color: color-mix(in srgb, var(--color-info)    10%, transparent); }
  .cbui-alert-mod-bg-fill.cbui-alert-tone-success  { background-color: color-mix(in srgb, var(--color-success) 10%, transparent); }
  .cbui-alert-mod-bg-fill.cbui-alert-tone-warning  { background-color: color-mix(in srgb, var(--color-warning) 10%, transparent); }
  .cbui-alert-mod-bg-fill.cbui-alert-tone-danger   { background-color: color-mix(in srgb, var(--color-danger)  10%, transparent); }

  /* Dark mode boosts */
  .dark .cbui-alert-mod-bg-fill.cbui-alert-tone-primary,  [data-scheme="dark"] .cbui-alert-mod-bg-fill.cbui-alert-tone-primary  { background-color: color-mix(in srgb, var(--color-primary) 16%, transparent); }
  .dark .cbui-alert-mod-bg-fill.cbui-alert-tone-accent,   [data-scheme="dark"] .cbui-alert-mod-bg-fill.cbui-alert-tone-accent   { background-color: color-mix(in srgb, var(--color-accent)  16%, transparent); }
  .dark .cbui-alert-mod-bg-fill.cbui-alert-tone-info,     [data-scheme="dark"] .cbui-alert-mod-bg-fill.cbui-alert-tone-info     { background-color: color-mix(in srgb, var(--color-info)    16%, transparent); }
  .dark .cbui-alert-mod-bg-fill.cbui-alert-tone-success,  [data-scheme="dark"] .cbui-alert-mod-bg-fill.cbui-alert-tone-success  { background-color: color-mix(in srgb, var(--color-success) 16%, transparent); }
  .dark .cbui-alert-mod-bg-fill.cbui-alert-tone-warning,  [data-scheme="dark"] .cbui-alert-mod-bg-fill.cbui-alert-tone-warning  { background-color: color-mix(in srgb, var(--color-warning) 16%, transparent); }
  .dark .cbui-alert-mod-bg-fill.cbui-alert-tone-danger,   [data-scheme="dark"] .cbui-alert-mod-bg-fill.cbui-alert-tone-danger   { background-color: color-mix(in srgb, var(--color-danger)  16%, transparent); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  ACCENT BAR modifiers — tone-coloured edge bar                      */
  /*                                                                     */
  /*  Uses --cbui-alert-accent, which every variant+tone rule sets.      */
  /*                                                                     */
  /*  Two variants:                                                       */
  /*    border-accent-*         — bar only, corner radius preserved       */
  /*    border-accent-*-sharp   — bar + zero radius on the bar side       */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-mod-border-accent-start {
    border-inline-start: var(--alert-accent-width, 3px) solid var(--cbui-alert-accent, currentColor);
  }
  .cbui-alert-mod-border-accent-end {
    border-inline-end: var(--alert-accent-width, 3px) solid var(--cbui-alert-accent, currentColor);
  }
  .cbui-alert-mod-border-accent-top {
    border-block-start: var(--alert-accent-width, 3px) solid var(--cbui-alert-accent, currentColor);
  }
  .cbui-alert-mod-border-accent-bottom {
    border-block-end: var(--alert-accent-width, 3px) solid var(--cbui-alert-accent, currentColor);
  }

  /* Sharp variants — also zero the corner radius on the bar side */
  .cbui-alert-mod-border-accent-start-sharp {
    border-inline-start: var(--alert-accent-width, 3px) solid var(--cbui-alert-accent, currentColor);
    border-start-start-radius: 0;
    border-end-start-radius: 0;
  }
  .cbui-alert-mod-border-accent-end-sharp {
    border-inline-end: var(--alert-accent-width, 3px) solid var(--cbui-alert-accent, currentColor);
    border-start-end-radius: 0;
    border-end-end-radius: 0;
  }
  .cbui-alert-mod-border-accent-top-sharp {
    border-block-start: var(--alert-accent-width, 3px) solid var(--cbui-alert-accent, currentColor);
    border-start-start-radius: 0;
    border-start-end-radius: 0;
  }
  .cbui-alert-mod-border-accent-bottom-sharp {
    border-block-end: var(--alert-accent-width, 3px) solid var(--cbui-alert-accent, currentColor);
    border-end-start-radius: 0;
    border-end-end-radius: 0;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  bg-gradient-horizontal modifier                                    */
  /*                                                                     */
  /*  Left→right gradient from a subtle tint to transparent.            */
  /*  Works well combined with +border-accent-start for a             */
  /*  "fade from the bar" look.                                          */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-alert-mod-bg-gradient-horizontal {
    background-color: transparent;
    background-image: linear-gradient(
      to right,
      color-mix(in srgb, var(--cbui-alert-accent, currentColor) 22%, transparent) 0%,
      color-mix(in srgb, var(--cbui-alert-accent, currentColor) 7%, transparent) 100%
    );
  }
  /* On outline, only keep the inline-start border — the rest clashes with the gradient fade. */
  .cbui-alert-outline.cbui-alert-mod-bg-gradient-horizontal {
    border-block-color: transparent;
    border-inline-end-color: transparent;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  FLAVOR OVERRIDES — auto variant                                     */
  /*                                                                      */
  /*  Each flavor can change the default style for .cbui-alert-auto by    */
  /*  overriding the structural border properties here.                   */
  /*  Per-tone colours from the base rules still apply unless overridden. */
  /* ─────────────────────────────────────────────────────────────────── */

  /* Aero: outline+bg-fill+border-accent-start */
  [data-flavor="aero"] .cbui-alert-auto {
    border-style: solid;
    border-width: var(--alert-border-width);
    border-inline-start-width: var(--alert-accent-width, 3px);
  }

  /* Flush in Aero: a flush alert is a full-bleed band, not a floating object,
     so the Aero auto box border (block + inline-end edges) reads as a broken
     frame hugging the squared-off, edge-to-edge band. Drop those edges and
     keep only the inline-start accent bar — same clean full-bleed band as the
     Clean flavor. The non-flush Aero auto plastik (4-sided border) is untouched. */
  [data-flavor="aero"] .cbui-alert-auto.cbui-alert-flush {
    border-block-width: 0;
    border-inline-end-width: 0;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  HIGH CONTRAST (a11y-high-contrast)                                  */
  /* ─────────────────────────────────────────────────────────────────── */

  .a11y-high-contrast .cbui-alert-auto,
  .a11y-high-contrast .cbui-alert-ghost {
    border: 2px solid currentColor;
    border-inline-start-width: var(--alert-accent-width, 3px);
    background-color: var(--color-bg-base);
  }
  .a11y-high-contrast .cbui-alert-outline {
    border-width: 2px;
  }
}

/* cbui/components/badge */
/* cbui-badge — small label for status, categories, counts and tags.
 *
 * Two-axis variant model (shared with Button):
 *   variant = visual treatment: solid | outline | ghost
 *   tone    = semantic colour:  neutral | primary | accent | success | danger
 *                               | warning | info | muted
 *           + categorical (16 hues around the wheel):
 *             orange | amber | yellow | lime | green | emerald | teal | cyan
 *             | sky | blue | indigo | violet | purple | fuchsia | pink | rose
 *
 * Composing the variant string:
 *   '+'  joins variant + modifier(s)   → "outline+border-pale", "ghost+bg-fill"
 *   ','  separates variant from tone   → "outline,success", "ghost+bg-fill,danger"
 *
 * Modifiers:
 *   border-pale  — de-saturate the border (works on outline)
 *   bg-fill      — add a soft tinted background (works on outline, ghost)
 *
 * Modifier composition produces:
 *   outline                        → full tone border, no fill
 *   outline+border-pale            → de-saturated tone border, no fill
 *   outline+bg-fill                → full tone border + soft tinted fill
 *   outline+border-pale+bg-fill    → de-saturated border + soft tinted fill (chip look)
 *   ghost+bg-fill                  → tinted fill, no border (formerly "soft")
 *
 * Sizes:
 *   xs | sm | md
 *
 * Other modifiers (independent props):
 *   icon  — leading SVG icon
 *   dot   — leading status circle
 *   ring  — outer 2px ring in the page background colour, for badges anchored
 *           to other elements (notification counters)
 *   interactive (auto, when rendered as <a>) — cursor + hover lift
 *
 * Accessibility:
 *   .a11y-high-contrast → solid backgrounds, opaque borders, no transparent overlays
 */

@layer components {
  /* Universal shade overlay token — used for the interactive hover state.
     Matches Button's --cbui-shade-soft so the two components share the same
     visual rhythm. Light mode darkens with black; dark mode lightens with
     white. */
  :where(.cbui-badge) {
    --cbui-shade-soft: rgb(0 0 0 / 0.08);

    /* Icon-only-at-width capability (S0.5 Batch B). A host with its own
       container query flips ONE custom property to collapse every badge in
       a row to icon-only — instead of 23 hand-rolled `.badge-text` wrapper
       classes across three bundles. `labels="never"` (the static case) sets
       these via .cbui-badge-labels-never below; a host's own @container
       rule sets them directly on an ancestor, e.g.:
         @container my-listpane (max-width: 22rem) {
           .my-tags { --cbui-badge-labels: none; --cbui-badge-pad-x-current: 0.375rem; }
         }
       `:where()` keeps this at zero specificity so either override wins. */
    --cbui-badge-labels: inline;
  }
  .cbui-badge > .cbui-badge-label {
    display: var(--cbui-badge-labels);
  }
  .cbui-badge-labels-never {
    --cbui-badge-labels: none;
    --cbui-badge-pad-x-current: 0.375rem;
  }
  .dark :where(.cbui-badge),
  [data-scheme="dark"] :where(.cbui-badge) {
    --cbui-shade-soft: rgb(255 255 255 / 0.12);
  }

  .cbui-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    border-radius: var(--badge-radius);
    padding-inline: var(--cbui-badge-pad-x-current, var(--badge-padding-x, 0.625rem));
    padding-block: var(--badge-padding-y, 0.125rem);
    font-size: var(--badge-font-size, var(--font-size-md, 1rem));
    font-weight: 500;
    /* line-height 1.2 (instead of 1) gives ascender breathing room above the
       glyph that matches the descender space below — without it, numerals
       and capitals visually sit too low in the pill. */
    line-height: 1.2;
    /* A badge never inherits an upper-case heading (CB-1683); the deliberate
       `upper` modifier below sets it itself. */
    text-transform: none;
    white-space: nowrap;
    border: 1px solid transparent;
    text-decoration: none;
    /* solid background goes on `background-color`; the interactive hover overlay
       lives on `background-image` — same trick Button uses so the overlay
       doesn't fight the variant background. */
    background-image: linear-gradient(transparent, transparent);
    transition:
      background-color 150ms ease,
      background-image 150ms ease,
      color 150ms ease,
      border-color 150ms ease,
      box-shadow 150ms ease;

    &:focus-visible {
      outline: none;
      box-shadow: 0 0 0 var(--focus-ring-width) var(--color-focus-ring, var(--color-primary));
      outline-offset: var(--focus-ring-offset);
    }
  }

  /* ── Sizes ──────────────────────────────────────────────────────────
     Type never renders below --font-size-md; size steers padding and line height only (Matthias 2026-10-17). */

  .cbui-badge-xs {
    padding-inline: 0.375rem;
    padding-block: 0;
    font-size: var(--font-size-md, 1rem);
    line-height: 1;
    gap: 0.25rem;
  }

  .cbui-badge-sm {
    padding-inline: 0.5rem;
    padding-block: 0;
    font-size: var(--font-size-md, 1rem);
    line-height: 1.125;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  SOLID variant — filled, contrast text                                */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-badge-solid.cbui-badge-tone-neutral { background-color: var(--color-text);    color: var(--color-bg-base);          }
  .cbui-badge-solid.cbui-badge-tone-primary { background-color: var(--color-primary); color: var(--color-text-on-primary); }
  .cbui-badge-solid.cbui-badge-tone-accent  { background-color: var(--color-accent);  color: var(--color-text-on-accent);  }
  .cbui-badge-solid.cbui-badge-tone-success { background-color: var(--color-success); color: var(--color-text-on-success); }
  .cbui-badge-solid.cbui-badge-tone-danger  { background-color: var(--color-danger);  color: var(--color-text-on-danger);  }
  .cbui-badge-solid.cbui-badge-tone-warning { background-color: var(--color-warning); color: var(--color-text-on-warning); }
  .cbui-badge-solid.cbui-badge-tone-info    { background-color: var(--color-info);    color: var(--color-text-on-info);    }
  .cbui-badge-solid.cbui-badge-tone-muted   { background-color: var(--color-muted);   color: var(--color-text-on-muted);   }
  /* Categorical (16 hues, ordered around the colour wheel) */
  .cbui-badge-solid.cbui-badge-tone-orange  { background-color: var(--color-orange);  color: var(--color-text-on-orange);  }
  .cbui-badge-solid.cbui-badge-tone-amber   { background-color: var(--color-amber);   color: var(--color-text-on-amber);   }
  .cbui-badge-solid.cbui-badge-tone-yellow  { background-color: var(--color-yellow);  color: var(--color-text-on-yellow);  }
  .cbui-badge-solid.cbui-badge-tone-lime    { background-color: var(--color-lime);    color: var(--color-text-on-lime);    }
  .cbui-badge-solid.cbui-badge-tone-green   { background-color: var(--color-green);   color: var(--color-text-on-green);   }
  .cbui-badge-solid.cbui-badge-tone-emerald { background-color: var(--color-emerald); color: var(--color-text-on-emerald); }
  .cbui-badge-solid.cbui-badge-tone-teal    { background-color: var(--color-teal);    color: var(--color-text-on-teal);    }
  .cbui-badge-solid.cbui-badge-tone-cyan    { background-color: var(--color-cyan);    color: var(--color-text-on-cyan);    }
  .cbui-badge-solid.cbui-badge-tone-sky     { background-color: var(--color-sky);     color: var(--color-text-on-sky);     }
  .cbui-badge-solid.cbui-badge-tone-blue    { background-color: var(--color-blue);    color: var(--color-text-on-blue);    }
  .cbui-badge-solid.cbui-badge-tone-indigo  { background-color: var(--color-indigo);  color: var(--color-text-on-indigo);  }
  .cbui-badge-solid.cbui-badge-tone-violet  { background-color: var(--color-violet);  color: var(--color-text-on-violet);  }
  .cbui-badge-solid.cbui-badge-tone-purple  { background-color: var(--color-purple);  color: var(--color-text-on-purple);  }
  .cbui-badge-solid.cbui-badge-tone-fuchsia { background-color: var(--color-fuchsia); color: var(--color-text-on-fuchsia); }
  .cbui-badge-solid.cbui-badge-tone-pink    { background-color: var(--color-pink);    color: var(--color-text-on-pink);    }
  .cbui-badge-solid.cbui-badge-tone-rose    { background-color: var(--color-rose);    color: var(--color-text-on-rose);    }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  OUTLINE variant — transparent + tone-coloured border + text         */
  /*                                                                      */
  /*  Modifiers:                                                          */
  /*    +border-pale → de-saturate the border (45% mix with surface)    */
  /*    +bg-fill     → add a soft tinted background behind the border   */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-badge-outline { background-color: transparent; }
  .cbui-badge-outline.cbui-badge-tone-neutral { color: var(--color-text-secondary); border-color: var(--color-border-strong); }
  .cbui-badge-outline.cbui-badge-tone-primary { color: var(--color-primary); border-color: var(--color-primary); }
  .cbui-badge-outline.cbui-badge-tone-accent  { color: var(--color-accent);  border-color: var(--color-accent);  }
  .cbui-badge-outline.cbui-badge-tone-success { color: var(--color-success); border-color: var(--color-success); }
  .cbui-badge-outline.cbui-badge-tone-danger  { color: var(--color-danger);  border-color: var(--color-danger);  }
  .cbui-badge-outline.cbui-badge-tone-warning { color: var(--color-warning); border-color: var(--color-warning); }
  .cbui-badge-outline.cbui-badge-tone-info    { color: var(--color-info);    border-color: var(--color-info);    }
  .cbui-badge-outline.cbui-badge-tone-muted   { color: var(--color-muted);   border-color: var(--color-muted);   }
  /* Categorical */
  .cbui-badge-outline.cbui-badge-tone-orange  { color: var(--color-orange);  border-color: var(--color-orange);  }
  .cbui-badge-outline.cbui-badge-tone-amber   { color: var(--color-amber);   border-color: var(--color-amber);   }
  .cbui-badge-outline.cbui-badge-tone-yellow  { color: var(--color-yellow);  border-color: var(--color-yellow);  }
  .cbui-badge-outline.cbui-badge-tone-lime    { color: var(--color-lime);    border-color: var(--color-lime);    }
  .cbui-badge-outline.cbui-badge-tone-green   { color: var(--color-green);   border-color: var(--color-green);   }
  .cbui-badge-outline.cbui-badge-tone-emerald { color: var(--color-emerald); border-color: var(--color-emerald); }
  .cbui-badge-outline.cbui-badge-tone-teal    { color: var(--color-teal);    border-color: var(--color-teal);    }
  .cbui-badge-outline.cbui-badge-tone-cyan    { color: var(--color-cyan);    border-color: var(--color-cyan);    }
  .cbui-badge-outline.cbui-badge-tone-sky     { color: var(--color-sky);     border-color: var(--color-sky);     }
  .cbui-badge-outline.cbui-badge-tone-blue    { color: var(--color-blue);    border-color: var(--color-blue);    }
  .cbui-badge-outline.cbui-badge-tone-indigo  { color: var(--color-indigo);  border-color: var(--color-indigo);  }
  .cbui-badge-outline.cbui-badge-tone-violet  { color: var(--color-violet);  border-color: var(--color-violet);  }
  .cbui-badge-outline.cbui-badge-tone-purple  { color: var(--color-purple);  border-color: var(--color-purple);  }
  .cbui-badge-outline.cbui-badge-tone-fuchsia { color: var(--color-fuchsia); border-color: var(--color-fuchsia); }
  .cbui-badge-outline.cbui-badge-tone-pink    { color: var(--color-pink);    border-color: var(--color-pink);    }
  .cbui-badge-outline.cbui-badge-tone-rose    { color: var(--color-rose);    border-color: var(--color-rose);    }

  /* Modifier: border-pale — de-saturated border (works on outline) */
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-neutral { border-color: color-mix(in srgb, var(--color-border-strong) 60%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-primary { border-color: color-mix(in srgb, var(--color-primary) 45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-accent  { border-color: color-mix(in srgb, var(--color-accent)  45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-success { border-color: color-mix(in srgb, var(--color-success) 45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-danger  { border-color: color-mix(in srgb, var(--color-danger)  45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-warning { border-color: color-mix(in srgb, var(--color-warning) 45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-info    { border-color: color-mix(in srgb, var(--color-info)    45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-muted   { border-color: color-mix(in srgb, var(--color-muted)   45%, transparent); }
  /* Categorical */
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-orange  { border-color: color-mix(in srgb, var(--color-orange)  45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-amber   { border-color: color-mix(in srgb, var(--color-amber)   45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-yellow  { border-color: color-mix(in srgb, var(--color-yellow)  45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-lime    { border-color: color-mix(in srgb, var(--color-lime)    45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-green   { border-color: color-mix(in srgb, var(--color-green)   45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-emerald { border-color: color-mix(in srgb, var(--color-emerald) 45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-teal    { border-color: color-mix(in srgb, var(--color-teal)    45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-cyan    { border-color: color-mix(in srgb, var(--color-cyan)    45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-sky     { border-color: color-mix(in srgb, var(--color-sky)     45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-blue    { border-color: color-mix(in srgb, var(--color-blue)    45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-indigo  { border-color: color-mix(in srgb, var(--color-indigo)  45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-violet  { border-color: color-mix(in srgb, var(--color-violet)  45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-purple  { border-color: color-mix(in srgb, var(--color-purple)  45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-fuchsia { border-color: color-mix(in srgb, var(--color-fuchsia) 45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-pink    { border-color: color-mix(in srgb, var(--color-pink)    45%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-rose    { border-color: color-mix(in srgb, var(--color-rose)    45%, transparent); }

  /* In dark mode, the de-saturated border drops out — boost from 45% → 55% */
  .dark .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-primary, [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-primary { border-color: color-mix(in srgb, var(--color-primary) 55%, transparent); color: color-mix(in srgb, var(--color-primary) 80%, white); }
  .dark .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-accent,  [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-accent  { border-color: color-mix(in srgb, var(--color-accent)  55%, transparent); color: color-mix(in srgb, var(--color-accent)  80%, white); }
  .dark .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-success, [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-success { border-color: color-mix(in srgb, var(--color-success) 55%, transparent); color: color-mix(in srgb, var(--color-success) 80%, white); }
  .dark .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-danger,  [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-danger  { border-color: color-mix(in srgb, var(--color-danger)  55%, transparent); color: color-mix(in srgb, var(--color-danger)  80%, white); }
  .dark .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-warning, [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-warning { border-color: color-mix(in srgb, var(--color-warning) 55%, transparent); color: color-mix(in srgb, var(--color-warning) 80%, white); }
  .dark .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-info,    [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-info    { border-color: color-mix(in srgb, var(--color-info)    55%, transparent); color: color-mix(in srgb, var(--color-info)    80%, white); }
  .dark .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-muted,   [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-border-pale.cbui-badge-tone-muted   { border-color: color-mix(in srgb, var(--color-muted)   55%, transparent); color: color-mix(in srgb, var(--color-muted)   80%, white); }

  /* Modifier: bg-fill — soft tinted background (works on outline and ghost) */
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-neutral { background-color: var(--color-bg-subtle); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-primary { background-color: color-mix(in srgb, var(--color-primary) 14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-accent  { background-color: color-mix(in srgb, var(--color-accent)  14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-success { background-color: color-mix(in srgb, var(--color-success) 14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-danger  { background-color: color-mix(in srgb, var(--color-danger)  14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-warning { background-color: color-mix(in srgb, var(--color-warning) 14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-info    { background-color: color-mix(in srgb, var(--color-info)    14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-muted   { background-color: color-mix(in srgb, var(--color-muted)   14%, transparent); }
  /* Categorical */
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-orange  { background-color: color-mix(in srgb, var(--color-orange)  14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-amber   { background-color: color-mix(in srgb, var(--color-amber)   14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-yellow  { background-color: color-mix(in srgb, var(--color-yellow)  14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-lime    { background-color: color-mix(in srgb, var(--color-lime)    14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-green   { background-color: color-mix(in srgb, var(--color-green)   14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-emerald { background-color: color-mix(in srgb, var(--color-emerald) 14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-teal    { background-color: color-mix(in srgb, var(--color-teal)    14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-cyan    { background-color: color-mix(in srgb, var(--color-cyan)    14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-sky     { background-color: color-mix(in srgb, var(--color-sky)     14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-blue    { background-color: color-mix(in srgb, var(--color-blue)    14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-indigo  { background-color: color-mix(in srgb, var(--color-indigo)  14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-violet  { background-color: color-mix(in srgb, var(--color-violet)  14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-purple  { background-color: color-mix(in srgb, var(--color-purple)  14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-fuchsia { background-color: color-mix(in srgb, var(--color-fuchsia) 14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-pink    { background-color: color-mix(in srgb, var(--color-pink)    14%, transparent); }
  .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-rose    { background-color: color-mix(in srgb, var(--color-rose)    14%, transparent); }

  /* Dark-mode boost for fill modifier */
  .dark .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-primary, [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-primary { background-color: color-mix(in srgb, var(--color-primary) 22%, transparent); }
  .dark .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-accent,  [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-accent  { background-color: color-mix(in srgb, var(--color-accent)  22%, transparent); }
  .dark .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-success, [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-success { background-color: color-mix(in srgb, var(--color-success) 22%, transparent); }
  .dark .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-danger,  [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-danger  { background-color: color-mix(in srgb, var(--color-danger)  22%, transparent); }
  .dark .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-warning, [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-warning { background-color: color-mix(in srgb, var(--color-warning) 22%, transparent); }
  .dark .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-info,    [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-info    { background-color: color-mix(in srgb, var(--color-info)    22%, transparent); }
  .dark .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-muted,   [data-scheme="dark"] .cbui-badge-outline.cbui-badge-mod-bg-fill.cbui-badge-tone-muted   { background-color: color-mix(in srgb, var(--color-muted)   22%, transparent); }

  /* ghost+bg-fill — tinted fill, no border (replaces soft variant).
     Applies the same 14% tint as outline+bg-fill, plus uses normal badge padding.
     Tone-coloured text comes from the ghost variant rules above. */
  .cbui-badge-ghost.cbui-badge-mod-bg-fill { padding-inline: var(--cbui-badge-pad-x-current, var(--badge-padding-x, 0.625rem)); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-neutral { background-color: var(--color-bg-subtle); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-primary { background-color: color-mix(in srgb, var(--color-primary) 14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-accent  { background-color: color-mix(in srgb, var(--color-accent)  14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-success { background-color: color-mix(in srgb, var(--color-success) 14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-danger  { background-color: color-mix(in srgb, var(--color-danger)  14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-warning { background-color: color-mix(in srgb, var(--color-warning) 14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-info    { background-color: color-mix(in srgb, var(--color-info)    14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-muted   { background-color: color-mix(in srgb, var(--color-muted)   14%, transparent); }
  /* Categorical */
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-orange  { background-color: color-mix(in srgb, var(--color-orange)  14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-amber   { background-color: color-mix(in srgb, var(--color-amber)   14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-yellow  { background-color: color-mix(in srgb, var(--color-yellow)  14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-lime    { background-color: color-mix(in srgb, var(--color-lime)    14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-green   { background-color: color-mix(in srgb, var(--color-green)   14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-emerald { background-color: color-mix(in srgb, var(--color-emerald) 14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-teal    { background-color: color-mix(in srgb, var(--color-teal)    14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-cyan    { background-color: color-mix(in srgb, var(--color-cyan)    14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-sky     { background-color: color-mix(in srgb, var(--color-sky)     14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-blue    { background-color: color-mix(in srgb, var(--color-blue)    14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-indigo  { background-color: color-mix(in srgb, var(--color-indigo)  14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-violet  { background-color: color-mix(in srgb, var(--color-violet)  14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-purple  { background-color: color-mix(in srgb, var(--color-purple)  14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-fuchsia { background-color: color-mix(in srgb, var(--color-fuchsia) 14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-pink    { background-color: color-mix(in srgb, var(--color-pink)    14%, transparent); }
  .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-rose    { background-color: color-mix(in srgb, var(--color-rose)    14%, transparent); }
  /* Dark mode boost */
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-primary, [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-primary { background-color: color-mix(in srgb, var(--color-primary) 22%, transparent); color: color-mix(in srgb, var(--color-primary) 80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-accent,  [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-accent  { background-color: color-mix(in srgb, var(--color-accent)  22%, transparent); color: color-mix(in srgb, var(--color-accent)  80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-success, [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-success { background-color: color-mix(in srgb, var(--color-success) 22%, transparent); color: color-mix(in srgb, var(--color-success) 80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-danger,  [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-danger  { background-color: color-mix(in srgb, var(--color-danger)  22%, transparent); color: color-mix(in srgb, var(--color-danger)  80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-warning, [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-warning { background-color: color-mix(in srgb, var(--color-warning) 22%, transparent); color: color-mix(in srgb, var(--color-warning) 80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-info,    [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-info    { background-color: color-mix(in srgb, var(--color-info)    22%, transparent); color: color-mix(in srgb, var(--color-info)    80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-muted,   [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-muted   { background-color: color-mix(in srgb, var(--color-muted)   22%, transparent); color: color-mix(in srgb, var(--color-muted)   80%, white); }
  /* Categorical dark mode boost */
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-orange,  [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-orange  { background-color: color-mix(in srgb, var(--color-orange)  22%, transparent); color: color-mix(in srgb, var(--color-orange)  80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-amber,   [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-amber   { background-color: color-mix(in srgb, var(--color-amber)   22%, transparent); color: color-mix(in srgb, var(--color-amber)   80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-yellow,  [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-yellow  { background-color: color-mix(in srgb, var(--color-yellow)  22%, transparent); color: color-mix(in srgb, var(--color-yellow) 80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-lime,    [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-lime    { background-color: color-mix(in srgb, var(--color-lime)    22%, transparent); color: color-mix(in srgb, var(--color-lime)    80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-green,   [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-green   { background-color: color-mix(in srgb, var(--color-green)   22%, transparent); color: color-mix(in srgb, var(--color-green)   80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-emerald, [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-emerald { background-color: color-mix(in srgb, var(--color-emerald) 22%, transparent); color: color-mix(in srgb, var(--color-emerald) 80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-teal,    [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-teal    { background-color: color-mix(in srgb, var(--color-teal)    22%, transparent); color: color-mix(in srgb, var(--color-teal)    80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-cyan,    [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-cyan    { background-color: color-mix(in srgb, var(--color-cyan)    22%, transparent); color: color-mix(in srgb, var(--color-cyan)    80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-sky,     [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-sky     { background-color: color-mix(in srgb, var(--color-sky)     22%, transparent); color: color-mix(in srgb, var(--color-sky)     80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-blue,    [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-blue    { background-color: color-mix(in srgb, var(--color-blue)    22%, transparent); color: color-mix(in srgb, var(--color-blue)    80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-indigo,  [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-indigo  { background-color: color-mix(in srgb, var(--color-indigo)  22%, transparent); color: color-mix(in srgb, var(--color-indigo)  80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-violet,  [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-violet  { background-color: color-mix(in srgb, var(--color-violet)  22%, transparent); color: color-mix(in srgb, var(--color-violet)  80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-purple,  [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-purple  { background-color: color-mix(in srgb, var(--color-purple)  22%, transparent); color: color-mix(in srgb, var(--color-purple)  80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-fuchsia, [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-fuchsia { background-color: color-mix(in srgb, var(--color-fuchsia) 22%, transparent); color: color-mix(in srgb, var(--color-fuchsia) 80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-pink,    [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-pink    { background-color: color-mix(in srgb, var(--color-pink)    22%, transparent); color: color-mix(in srgb, var(--color-pink)    80%, white); }
  .dark .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-rose,    [data-scheme="dark"] .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-rose    { background-color: color-mix(in srgb, var(--color-rose)    22%, transparent); color: color-mix(in srgb, var(--color-rose)    80%, white); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  GHOST variant — transparent + tone-coloured text, no border         */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-badge-ghost {
    background-color: transparent;
    border-color: transparent;
    padding-inline: 0.25rem;
  }
  .cbui-badge-ghost.cbui-badge-tone-neutral { color: var(--color-text-secondary); }
  .cbui-badge-ghost.cbui-badge-tone-primary { color: var(--color-primary); }
  .cbui-badge-ghost.cbui-badge-tone-accent  { color: var(--color-accent);  }
  .cbui-badge-ghost.cbui-badge-tone-success { color: var(--color-success); }
  .cbui-badge-ghost.cbui-badge-tone-danger  { color: var(--color-danger);  }
  .cbui-badge-ghost.cbui-badge-tone-warning { color: var(--color-warning); }
  .cbui-badge-ghost.cbui-badge-tone-info    { color: var(--color-info);    }
  .cbui-badge-ghost.cbui-badge-tone-muted   { color: var(--color-muted);   }
  /* Categorical */
  .cbui-badge-ghost.cbui-badge-tone-orange  { color: var(--color-orange);  }
  .cbui-badge-ghost.cbui-badge-tone-amber   { color: var(--color-amber);   }
  .cbui-badge-ghost.cbui-badge-tone-yellow  { color: var(--color-yellow);  }
  .cbui-badge-ghost.cbui-badge-tone-lime    { color: var(--color-lime);    }
  .cbui-badge-ghost.cbui-badge-tone-green   { color: var(--color-green);   }
  .cbui-badge-ghost.cbui-badge-tone-emerald { color: var(--color-emerald); }
  .cbui-badge-ghost.cbui-badge-tone-teal    { color: var(--color-teal);    }
  .cbui-badge-ghost.cbui-badge-tone-cyan    { color: var(--color-cyan);    }
  .cbui-badge-ghost.cbui-badge-tone-sky     { color: var(--color-sky);     }
  .cbui-badge-ghost.cbui-badge-tone-blue    { color: var(--color-blue);    }
  .cbui-badge-ghost.cbui-badge-tone-indigo  { color: var(--color-indigo);  }
  .cbui-badge-ghost.cbui-badge-tone-violet  { color: var(--color-violet);  }
  .cbui-badge-ghost.cbui-badge-tone-purple  { color: var(--color-purple);  }
  .cbui-badge-ghost.cbui-badge-tone-fuchsia { color: var(--color-fuchsia); }
  .cbui-badge-ghost.cbui-badge-tone-pink    { color: var(--color-pink);    }
  .cbui-badge-ghost.cbui-badge-tone-rose    { color: var(--color-rose);    }

  /* ── Interactive (anchor) — hover only fires when rendered as <a> ─── */

  .cbui-badge-interactive {
    cursor: pointer;
    user-select: none;
  }

  /* Universal shade overlay — works on every variant that has a fill (solid,
     soft, outline+fill, outline+pale+fill) by sitting on background-image
     instead of background-color. Same darken/lighten rhythm as Button. */
  .cbui-badge-interactive:hover {
    background-image: linear-gradient(var(--cbui-shade-soft), var(--cbui-shade-soft));
  }
  /* Outline (without fill) and ghost have no background to overlay — introduce
     a currentColor tint as the visible hover affordance. */
  .cbui-badge-outline:not(.cbui-badge-mod-bg-fill).cbui-badge-interactive:hover,
  .cbui-badge-ghost.cbui-badge-interactive:hover {
    background-color: color-mix(in srgb, currentColor 10%, transparent);
    background-image: none;
  }

  /* ── Ring modifier — punches the badge off any background ────────── */

  /* ── Shape overrides — per-instance corner-radius control ───────────
     The flavor sets the global default via --badge-radius (its component
     token). These classes let individual badges opt out of that default.
     Values map to the global radius scale so the result always feels
     "in-system" with the current flavor:
       pill    — always a full pill regardless of flavor
       rounded — radius-md in the current flavor (medium rounding)
       square  — radius-none = 0 (sharp corners)
  ───────────────────────────────────────────────────────────────────── */
  .cbui-badge-shape-pill    { --badge-radius: var(--radius-full); }
  .cbui-badge-shape-rounded { --badge-radius: var(--radius-lg); }
  .cbui-badge-shape-square  { --badge-radius: var(--radius-none); }

  /* ── block modifier — fills available width, content centred ────── */
  .cbui-badge-mod-block {
    display: flex;
    width: 100%;
    justify-content: center;
  }

  /* ── upper modifier — uppercase text with tracked letter-spacing ── */
  .cbui-badge-mod-upper {
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }

  /* Implementation: a 2px box-shadow in the page surface colour. We use
     box-shadow rather than border to keep the badge layout (padding,
     dimensions) identical to non-ringed badges — the ring sits *outside*
     the box. The shadow uses the canvas/page colour so the badge looks
     "lifted" off whatever is underneath: a navbar, an avatar, a button. */
  .cbui-badge-ring {
    box-shadow: 0 0 0 2px var(--cbui-badge-ring-color, var(--color-bg-base));
  }

  /* ── Dot indicator ────────────────────────────────────────────────── */

  .cbui-badge-dot {
    display: inline-block;
    width: 0.5em;
    height: 0.5em;
    border-radius: 50%;
    background-color: currentColor;
    flex: none;
  }
  /* Solid badges: dot needs to render against the filled background */
  .cbui-badge-solid .cbui-badge-dot {
    background-color: currentColor;
    opacity: 0.9;
  }

  /* When a badge is *only* a dot (no text), make it a tight pill */
  .cbui-badge-with-dot:empty,
  .cbui-badge-with-dot:has(> .cbui-badge-dot:only-child) {
    padding-inline: 0.375rem;
  }

  /* ── Icon glyph ───────────────────────────────────────────────────── */

  .cbui-badge-glyph {
    display: inline-flex;
    flex: none;
    width: 1em;
    height: 1em;
  }
  .cbui-badge-glyph-xs { width: 0.875em; height: 0.875em; }
  .cbui-badge-glyph-sm { width: 1em;     height: 1em; }

  /* ── High Contrast ────────────────────────────────────────────────── */

  .a11y-high-contrast .cbui-badge {
    border-width: 2px;
    transition: none;
  }
  /* Solid: keep colour, ensure opaque border for definition */
  .a11y-high-contrast .cbui-badge-solid {
    border-color: currentColor;
  }
  /* ghost+bg-fill in HC: becomes solid for stronger contrast */
  .a11y-high-contrast .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-neutral { background-color: var(--color-text);    color: var(--color-bg-base);         border-color: var(--color-text);    border-width: 2px; }
  .a11y-high-contrast .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-primary { background-color: var(--color-primary); color: var(--color-text-on-primary); border-color: var(--color-primary); border-width: 2px; }
  .a11y-high-contrast .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-accent  { background-color: var(--color-accent);  color: var(--color-text-on-accent);  border-color: var(--color-accent);  border-width: 2px; }
  .a11y-high-contrast .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-success { background-color: var(--color-success); color: var(--color-text-on-success); border-color: var(--color-success); border-width: 2px; }
  .a11y-high-contrast .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-danger  { background-color: var(--color-danger);  color: var(--color-text-on-danger);  border-color: var(--color-danger);  border-width: 2px; }
  .a11y-high-contrast .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-warning { background-color: var(--color-warning); color: var(--color-text-on-warning); border-color: var(--color-warning); border-width: 2px; }
  .a11y-high-contrast .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-info    { background-color: var(--color-info);    color: var(--color-text-on-info);    border-color: var(--color-info);    border-width: 2px; }
  .a11y-high-contrast .cbui-badge-ghost.cbui-badge-mod-bg-fill.cbui-badge-tone-muted   { background-color: var(--color-muted);   color: var(--color-text-on-muted);   border-color: var(--color-muted);   border-width: 2px; }

  /* Outline already has visible borders — just bump the weight, recover
     full opacity on the border-pale modifier */
  .a11y-high-contrast .cbui-badge-outline {
    border-color: currentColor;
  }
  .a11y-high-contrast .cbui-badge-outline.cbui-badge-mod-border-pale {
    border-color: currentColor;
  }
  /* Ghost in HC: gain a visible border so it doesn't fade away */
  .a11y-high-contrast .cbui-badge-ghost {
    border-color: currentColor;
    padding-inline: 0.5rem;
  }

  /* ── Badge Group ─────────────────────────────────────────────────────
     Connects 2–3 badges into one visual unit. Outer corners keep the
     normal --badge-radius; inner junctions become straight edges with
     a single 1px separator line.

     Usage:
       <twig:UiComponent:BadgeGroup>
         <twig:UiComponent:Badge variant="soft">Label</twig:UiComponent:Badge>
         <twig:UiComponent:Badge variant="soft,success">Value</twig:UiComponent:Badge>
       </twig:UiComponent:BadgeGroup>

     The separator colour defaults to --color-border-strong and can be
     overridden with --cbui-badge-group-sep on any ancestor or the group
     element itself.
  ───────────────────────────────────────────────────────────────────── */

  /* The group is the vessel — it owns the outer border and pill rounding.
     Child badges have no border and no radius of their own; only the
     first/last get inner radii to keep their background from overflowing
     the rounded corners without needing overflow:hidden (which would clip
     focus-visible rings). */
  .cbui-badge-group {
    display: inline-flex;
    align-items: stretch;
    border-radius: var(--badge-radius);
    border: 1px solid var(--cbui-badge-group-sep, var(--color-border-strong));
  }

  /* fill: full container width, entries share it equally (flex 1 1 0 + min-width 0
     makes the split independent of the content; the badge's own justify-content
     keeps the text centred). */
  .cbui-badge-group.cbui-badge-group-fill {
    display: flex;
    width: 100%;
  }

  .cbui-badge-group-fill > .cbui-badge {
    flex: 1 1 0;
    min-width: 0;
    justify-content: center;
  }

  /* The separator is an inset shadow here, not a border: a border on every entry but
     the first would make those entries one pixel wider than the first. */
  .cbui-badge-group.cbui-badge-group-fill > .cbui-badge + .cbui-badge {
    border-inline-start: none;
    box-shadow: inset 1px 0 0 var(--cbui-badge-group-sep, var(--color-border-strong));
  }

  .cbui-badge-group > .cbui-badge {
    border-radius: 0;
    border: none;
  }

  .cbui-badge-group > .cbui-badge:first-child {
    border-start-start-radius: calc(var(--badge-radius) - 1px);
    border-end-start-radius: calc(var(--badge-radius) - 1px);
  }

  .cbui-badge-group > .cbui-badge:last-child {
    border-start-end-radius: calc(var(--badge-radius) - 1px);
    border-end-end-radius: calc(var(--badge-radius) - 1px);
  }

  /* Internal separator line between adjacent badges */
  .cbui-badge-group > .cbui-badge + .cbui-badge {
    border-inline-start: 1px solid var(--cbui-badge-group-sep, var(--color-border-strong));
  }
}

/* ── Positioning helper ─────────────────────────────────────────────
   For the "notification badge in the corner of an element" pattern.
   Wrap the parent in .cbui-badge-anchor and place a ringed Badge inside
   marked with .cbui-badge-anchored-tr (or -tl, -br, -bl).

   <span class="cbui-badge-anchor">
     <Button icon="ph:bell" size="icon" variant="ghost" label="Inbox" />
     <Badge size="xs" variant="solid,danger" ring class="cbui-badge-anchored-tr">3</Badge>
   </span>
─────────────────────────────────────────────────────────────────────── */

@layer components {
  .cbui-badge-anchor {
    position: relative;
    display: inline-flex;
    flex: none;
  }
  .cbui-badge-anchored-tr,
  .cbui-badge-anchored-tl,
  .cbui-badge-anchored-br,
  .cbui-badge-anchored-bl {
    position: absolute;
    transform: translate(50%, -50%);
    pointer-events: none;
    z-index: 1;
  }
  .cbui-badge-anchored-tr { top: 0;    right: 0;  }
  .cbui-badge-anchored-tl { top: 0;    left: 0;   transform: translate(-50%, -50%); }
  .cbui-badge-anchored-br { bottom: 0; right: 0;  transform: translate(50%, 50%); }
  .cbui-badge-anchored-bl { bottom: 0; left: 0;   transform: translate(-50%, 50%); }
}

/* ── A badge anchored to the top right of a button (the unread count on a bell) ─────────────────────────────────────────
 * One rule for every bar: the wrapper `.cbui-badge-anchor` is the positioning context, the Badge inside it carries
 * `.cbui-badge-anchored` and sits over the corner of the button before it. `pointer-events: none` keeps the whole button
 * clickable; a hidden badge takes no room. The workspace bell, the live status and the Console bell share it. */
.cbui-badge-anchor {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.cbui-badge-anchor > .cbui-badge-anchored {
    position: absolute;
    top: -0.1875rem;
    /* Starts right of the centre, so a wide count grows outwards and leaves the icon of the button visible. */
    inset-inline-start: calc(50% + 0.375rem);
    font-size: var(--font-size-md, 1rem);
    pointer-events: none;
}
.cbui-badge-anchor > .cbui-badge-anchored[hidden] {
    display: none;
}

/* cbui/components/button */
/* cbui-button — text + icon-only buttons.
 *
 * Two-axis variant model (shared with Badge):
 *   variant = visual treatment: solid | soft | outline | ghost | link
 *   tone    = semantic colour:  neutral | primary | success | danger | warning | info
 *
 * Every variant accepts every tone. Compound shorthand variant="solid-success"
 * is parsed in the template into variant="solid" tone="success".
 *
 * Hover/active are rendered with a *predictable* shading overlay
 * (--cbui-shade-*) instead of theme-specific hover/active colour tokens.
 * Solid variants darken (light mode) / lighten (dark mode) by the same amount
 * regardless of base colour. Soft/outline/ghost use color-mix() on the tone
 * for hover.
 *
 * Sizes:
 *   xs | sm | md | lg | icon | icon-xs | icon-sm | icon-lg
 *
 * Accessibility:
 *   .a11y-reduced-motion       → no transitions, no transforms
 *   .a11y-reduced-transparency → flavors strip backdrop-blur
 *   .a11y-high-contrast        → 2px borders, solid backgrounds, sharper hover
 */

@layer components {
  /* ── Universal shading overlay (light mode default) ─────────────── */
  :where(.cbui-button) {
    --cbui-shade-soft: rgb(0 0 0 / 0.08);
    --cbui-shade-strong: rgb(0 0 0 / 0.16);

    /* Subtle anchor edge for dark mode — without it the soft drop shadow
       on solid buttons looks like fog rather than a lift. Light mode gets
       transparent so the white surface around the button is the edge. */
    --cbui-button-edge: transparent;
  }
  .dark :where(.cbui-button),
  [data-scheme="dark"] :where(.cbui-button) {
    --cbui-shade-soft: rgb(255 255 255 / 0.12);
    --cbui-shade-strong: rgb(255 255 255 / 0.22);
    --cbui-button-edge: rgb(0 0 0 / 0.40);
  }

  .cbui-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    font-weight: var(--button-font-weight, 500);
    line-height: 1;
    border-radius: var(--button-radius);
    padding-inline: var(--button-padding-x);
    padding-block: var(--button-padding-y);
    border: 1px solid transparent;
    cursor: pointer;
    user-select: none;
    text-decoration: none;
    /* solid background goes on `background-color`; overlay sits on `background-image` */
    background-image: linear-gradient(transparent, transparent);
    transition:
      background-color 150ms ease,
      background-image 150ms ease,
      color 150ms ease,
      border-color 150ms ease,
      box-shadow 150ms ease,
      transform 120ms ease;

    /* Focus ring — a coloured ring with a clean gap to the button.
       Implemented as a two-layer box-shadow: the inner shadow paints the
       gap in the page background colour, the outer shadow is the ring.
       Composes safely with Aero's box-shadow (Aero overrides this rule on
       :focus-visible to preserve its emboss). */
    &:focus-visible {
      outline: none;
      box-shadow:
        0 0 0 var(--focus-ring-offset, 2px) var(--color-bg-base),
        0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring, var(--color-primary));
    }
  }

  /* The base rule sets an explicit `display: inline-flex`, so a caller
     toggling `hidden`/`.hidden = true` (e.g. a wizard footer button) loses:
     `[hidden] { display: none }` is a User-Agent rule, and any author rule
     targeting `display` beats it regardless of specificity (same pattern as
     `.cbui-local-modal-host[hidden]` in local-modal.css — CB-1503 browser
     defect A, a Button toggled hidden by JS stayed visible in the DOM AND
     the accessibility tree). */
  .cbui-button[hidden] {
    display: none;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  SOLID variant — filled background, contrast-colour text             */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-button-solid {
    border-color: var(--cbui-button-edge);
    &:hover  { background-image: linear-gradient(var(--cbui-shade-soft),   var(--cbui-shade-soft)); }
    &:active { background-image: linear-gradient(var(--cbui-shade-strong), var(--cbui-shade-strong)); }
  }
  .cbui-button-solid.cbui-button-tone-neutral {
    background-color: var(--color-text);
    color: var(--color-bg-base);
  }
  .cbui-button-solid.cbui-button-tone-primary {
    background-color: var(--color-primary);
    color: var(--color-text-on-primary);
  }
  .cbui-button-solid.cbui-button-tone-success {
    background-color: var(--color-success);
    color: var(--color-text-on-success);
  }
  .cbui-button-solid.cbui-button-tone-danger {
    background-color: var(--color-danger);
    color: var(--color-text-on-danger);
  }
  .cbui-button-solid.cbui-button-tone-warning {
    background-color: var(--color-warning);
    color: var(--color-text-on-warning);
  }
  .cbui-button-solid.cbui-button-tone-info {
    background-color: var(--color-info);
    color: var(--color-text-on-info);
  }
  .cbui-button-solid.cbui-button-tone-accent {
    background-color: var(--color-accent);
    color: var(--color-text-on-accent);
  }
  .cbui-button-solid.cbui-button-tone-muted {
    background-color: var(--color-muted);
    color: var(--color-text-on-muted);
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  FILL modifier — adds soft tinted background to any base variant     */
  /*                                                                      */
  /*  Works with any base:                                                */
  /*    outline+fill → bordered + tinted ("chip" look)                   */
  /*    ghost+fill   → tinted, no border — equivalent to the old 'soft'  */
  /*                                                                      */
  /*  Stacks with +glow: outline+fill+glow, ghost+fill+glow              */
  /*  ghost+fill has no border — tinted background only.                  */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-button-mod-bg-fill.cbui-button-tone-neutral {
    background-color: var(--color-bg-subtle);
    &:hover  { background-image: linear-gradient(var(--cbui-shade-soft), var(--cbui-shade-soft)); }
    &:active { background-image: linear-gradient(var(--cbui-shade-strong), var(--cbui-shade-strong)); }
  }
.cbui-button-mod-bg-fill.cbui-button-tone-primary {
    background-color: color-mix(in srgb, var(--color-primary) 14%, transparent);
    &:hover  { background-color: color-mix(in srgb, var(--color-primary) 22%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-primary) 30%, transparent); }
  }
  .cbui-button-mod-bg-fill.cbui-button-tone-success {
    background-color: color-mix(in srgb, var(--color-success) 14%, transparent);
    &:hover  { background-color: color-mix(in srgb, var(--color-success) 22%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-success) 30%, transparent); }
  }
  .cbui-button-mod-bg-fill.cbui-button-tone-danger {
    background-color: color-mix(in srgb, var(--color-danger) 14%, transparent);
    &:hover  { background-color: color-mix(in srgb, var(--color-danger) 22%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-danger) 30%, transparent); }
  }
  .cbui-button-mod-bg-fill.cbui-button-tone-warning {
    background-color: color-mix(in srgb, var(--color-warning) 14%, transparent);
    &:hover  { background-color: color-mix(in srgb, var(--color-warning) 22%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-warning) 30%, transparent); }
  }
  .cbui-button-mod-bg-fill.cbui-button-tone-info {
    background-color: color-mix(in srgb, var(--color-info) 14%, transparent);
    &:hover  { background-color: color-mix(in srgb, var(--color-info) 22%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-info) 30%, transparent); }
  }
  .cbui-button-mod-bg-fill.cbui-button-tone-accent {
    background-color: color-mix(in srgb, var(--color-accent) 14%, transparent);
    &:hover  { background-color: color-mix(in srgb, var(--color-accent) 22%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-accent) 30%, transparent); }
  }
  .cbui-button-mod-bg-fill.cbui-button-tone-muted {
    background-color: color-mix(in srgb, var(--color-muted) 14%, transparent);
    &:hover  { background-color: color-mix(in srgb, var(--color-muted) 22%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-muted) 30%, transparent); }
  }

  /* Dark mode: tints need more presence on dark backgrounds.
     neutral is the one tone whose bg-fill is an opaque token (--color-bg-subtle)
     rather than a translucent overlay — in dark that token equals the surface,
     so the fill vanishes. Give it the same surface-relative currentColor overlay
     the other tones get (neutral's "tone colour" is currentColor). Light keeps
     its opaque --color-bg-subtle fill (defined above) unchanged. */
  .dark .cbui-button-mod-bg-fill.cbui-button-tone-neutral,  [data-scheme="dark"] .cbui-button-mod-bg-fill.cbui-button-tone-neutral { background-color: color-mix(in srgb, currentColor 10%, transparent); }
  .dark .cbui-button-mod-bg-fill.cbui-button-tone-primary,  [data-scheme="dark"] .cbui-button-mod-bg-fill.cbui-button-tone-primary { background-color: color-mix(in srgb, var(--color-primary) 22%, transparent); }
  .dark .cbui-button-mod-bg-fill.cbui-button-tone-success,  [data-scheme="dark"] .cbui-button-mod-bg-fill.cbui-button-tone-success { background-color: color-mix(in srgb, var(--color-success) 22%, transparent); }
  .dark .cbui-button-mod-bg-fill.cbui-button-tone-danger,   [data-scheme="dark"] .cbui-button-mod-bg-fill.cbui-button-tone-danger  { background-color: color-mix(in srgb, var(--color-danger)  22%, transparent); }
  .dark .cbui-button-mod-bg-fill.cbui-button-tone-warning,  [data-scheme="dark"] .cbui-button-mod-bg-fill.cbui-button-tone-warning { background-color: color-mix(in srgb, var(--color-warning) 22%, transparent); }
  .dark .cbui-button-mod-bg-fill.cbui-button-tone-info,     [data-scheme="dark"] .cbui-button-mod-bg-fill.cbui-button-tone-info    { background-color: color-mix(in srgb, var(--color-info)    22%, transparent); }
  .dark .cbui-button-mod-bg-fill.cbui-button-tone-accent,   [data-scheme="dark"] .cbui-button-mod-bg-fill.cbui-button-tone-accent  { background-color: color-mix(in srgb, var(--color-accent)  22%, transparent); }
  .dark .cbui-button-mod-bg-fill.cbui-button-tone-muted,    [data-scheme="dark"] .cbui-button-mod-bg-fill.cbui-button-tone-muted   { background-color: color-mix(in srgb, var(--color-muted)   22%, transparent); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  OUTLINE variant — transparent + tone-coloured border + text         */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-button-outline {
    background-color: transparent;
  }
  .cbui-button-outline.cbui-button-tone-neutral {
    /* Neutral = "chrome colour, no own accent" — inherit text colour from the
       parent so the button adapts to tone-coloured Navbars / Cards / Modals
       (where color: var(--cbui-navbar-text) etc. is set). On a normal page
       this still resolves to --color-text via body. Border and hover overlays
       derive from currentColor so they stay readable in any context: halftone
       text for the resting border, full text on hover. */
    color: inherit;
    border-color: color-mix(in srgb, currentColor 45%, transparent);
    &:hover  { background-color: color-mix(in srgb, currentColor 10%, transparent); border-color: currentColor; }
    &:active { background-color: color-mix(in srgb, currentColor 18%, transparent); }
  }
  .cbui-button-outline.cbui-button-tone-primary {
    color: var(--color-primary);
    border-color: var(--color-primary);
    &:hover  { background-color: color-mix(in srgb, var(--color-primary) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-primary) 18%, transparent); }
  }
  .cbui-button-outline.cbui-button-tone-success {
    color: var(--color-success);
    border-color: var(--color-success);
    &:hover  { background-color: color-mix(in srgb, var(--color-success) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-success) 18%, transparent); }
  }
  .cbui-button-outline.cbui-button-tone-danger {
    color: var(--color-danger);
    border-color: var(--color-danger);
    &:hover  { background-color: color-mix(in srgb, var(--color-danger) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-danger) 18%, transparent); }
  }
  .cbui-button-outline.cbui-button-tone-warning {
    color: var(--color-warning);
    border-color: var(--color-warning);
    &:hover  { background-color: color-mix(in srgb, var(--color-warning) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-warning) 18%, transparent); }
  }
  .cbui-button-outline.cbui-button-tone-info {
    color: var(--color-info);
    border-color: var(--color-info);
    &:hover  { background-color: color-mix(in srgb, var(--color-info) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-info) 18%, transparent); }
  }
  .cbui-button-outline.cbui-button-tone-accent {
    color: var(--color-accent);
    border-color: var(--color-accent);
    &:hover  { background-color: color-mix(in srgb, var(--color-accent) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-accent) 18%, transparent); }
  }
  .cbui-button-outline.cbui-button-tone-muted {
    color: var(--color-muted);
    border-color: var(--color-muted);
    &:hover  { background-color: color-mix(in srgb, var(--color-muted) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-muted) 18%, transparent); }
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  PALE modifier — de-saturates the border (works on outline)          */
  /*                                                                      */
  /*  outline+pale          → muted border, no fill                       */
  /*  outline+pale+fill     → muted border + soft tinted fill (chip look) */
  /*  outline+pale+fill+glow → all three stacked                          */
  /*                                                                      */
  /*  Placed after the outline block so source order keeps the pale       */
  /*  border-color at equal specificity even on :hover.                   */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-button-outline.cbui-button-mod-border-pale.cbui-button-tone-neutral { border-color: color-mix(in srgb, var(--color-border-strong) 60%, transparent); }
  .cbui-button-outline.cbui-button-mod-border-pale.cbui-button-tone-primary { border-color: color-mix(in srgb, var(--color-primary) 45%, transparent); }
  .cbui-button-outline.cbui-button-mod-border-pale.cbui-button-tone-accent  { border-color: color-mix(in srgb, var(--color-accent)  45%, transparent); }
  .cbui-button-outline.cbui-button-mod-border-pale.cbui-button-tone-success { border-color: color-mix(in srgb, var(--color-success) 45%, transparent); }
  .cbui-button-outline.cbui-button-mod-border-pale.cbui-button-tone-danger  { border-color: color-mix(in srgb, var(--color-danger)  45%, transparent); }
  .cbui-button-outline.cbui-button-mod-border-pale.cbui-button-tone-warning { border-color: color-mix(in srgb, var(--color-warning) 45%, transparent); }
  .cbui-button-outline.cbui-button-mod-border-pale.cbui-button-tone-info    { border-color: color-mix(in srgb, var(--color-info)    45%, transparent); }
  .cbui-button-outline.cbui-button-mod-border-pale.cbui-button-tone-muted   { border-color: color-mix(in srgb, var(--color-muted)   45%, transparent); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  BORDER-DASHED modifier — gestrichelter Border. Works on outline,    */
  /*  ghost (synthesises a 1px transparent border so dashing is visible). */
  /*  Used for "Add" placeholders (`+ Filter`, `+ Bedingung`, `+ Gruppe`),*/
  /*  drop targets and empty-state CTAs — anywhere the border carries an  */
  /*  affordance of "not yet committed". Composes with border-pale freely.*/
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-button-mod-border-dashed { border-style: dashed; }
  /* Ghost has no border by default; synthesise one so the dashed line     */
  /* renders. Tone-coloured if a tone is set, else uses the muted border.  */
  .cbui-button-ghost.cbui-button-mod-border-dashed {
    border: 1px dashed var(--color-border);
  }
  .cbui-button-ghost.cbui-button-mod-border-dashed.cbui-button-tone-primary { border-color: color-mix(in srgb, var(--color-primary) 45%, transparent); }
  .cbui-button-ghost.cbui-button-mod-border-dashed.cbui-button-tone-accent  { border-color: color-mix(in srgb, var(--color-accent)  45%, transparent); }
  .cbui-button-ghost.cbui-button-mod-border-dashed.cbui-button-tone-success { border-color: color-mix(in srgb, var(--color-success) 45%, transparent); }
  .cbui-button-ghost.cbui-button-mod-border-dashed.cbui-button-tone-danger  { border-color: color-mix(in srgb, var(--color-danger)  45%, transparent); }
  .cbui-button-ghost.cbui-button-mod-border-dashed.cbui-button-tone-warning { border-color: color-mix(in srgb, var(--color-warning) 45%, transparent); }
  .cbui-button-ghost.cbui-button-mod-border-dashed.cbui-button-tone-info    { border-color: color-mix(in srgb, var(--color-info)    45%, transparent); }
  .cbui-button-ghost.cbui-button-mod-border-dashed.cbui-button-tone-muted   { border-color: color-mix(in srgb, var(--color-muted)   45%, transparent); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  GHOST variant — transparent, tone-coloured text, hover tint         */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-button-ghost {
    background-color: transparent;
  }
  .cbui-button-ghost.cbui-button-tone-neutral {
    /* Same context-adaptive rule as outline,neutral — see above. */
    color: inherit;
    &:hover  { background-color: color-mix(in srgb, currentColor 10%, transparent); }
    &:active { background-color: color-mix(in srgb, currentColor 18%, transparent); }
  }
  .cbui-button-ghost.cbui-button-tone-primary {
    color: var(--color-primary);
    &:hover  { background-color: color-mix(in srgb, var(--color-primary) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-primary) 18%, transparent); }
  }
  .cbui-button-ghost.cbui-button-tone-success {
    color: var(--color-success);
    &:hover  { background-color: color-mix(in srgb, var(--color-success) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-success) 18%, transparent); }
  }
  .cbui-button-ghost.cbui-button-tone-danger {
    color: var(--color-danger);
    &:hover  { background-color: color-mix(in srgb, var(--color-danger) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-danger) 18%, transparent); }
  }
  .cbui-button-ghost.cbui-button-tone-warning {
    color: var(--color-warning);
    &:hover  { background-color: color-mix(in srgb, var(--color-warning) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-warning) 18%, transparent); }
  }
  .cbui-button-ghost.cbui-button-tone-info {
    color: var(--color-info);
    &:hover  { background-color: color-mix(in srgb, var(--color-info) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-info) 18%, transparent); }
  }
  .cbui-button-ghost.cbui-button-tone-accent {
    color: var(--color-accent);
    &:hover  { background-color: color-mix(in srgb, var(--color-accent) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-accent) 18%, transparent); }
  }
  .cbui-button-ghost.cbui-button-tone-muted {
    color: var(--color-muted);
    &:hover  { background-color: color-mix(in srgb, var(--color-muted) 10%, transparent); }
    &:active { background-color: color-mix(in srgb, var(--color-muted) 18%, transparent); }
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  LINK variant — chrome-less inline anchor                            */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-button-link {
    background-color: transparent;
    padding-inline: 0;
    padding-block: 0;
    border-color: transparent;
    border-radius: 0;
    text-decoration: underline;
    text-underline-offset: 2px;
    height: auto;
    /* An inline link label stays on one line and keeps its width in a flex row (CB-2288): the text beside it truncates instead. */
    flex: none;
    white-space: nowrap;
    &:active { opacity: 0.7; }
  }
  .cbui-button-link.cbui-button-tone-neutral { color: inherit; }
  .cbui-button-link.cbui-button-tone-primary { color: var(--color-primary); }
  .cbui-button-link.cbui-button-tone-success { color: var(--color-success); }
  .cbui-button-link.cbui-button-tone-danger  { color: var(--color-danger); }
  .cbui-button-link.cbui-button-tone-warning { color: var(--color-warning); }
  .cbui-button-link.cbui-button-tone-info    { color: var(--color-info); }
  .cbui-button-link.cbui-button-tone-accent  { color: var(--color-accent); }
  .cbui-button-link.cbui-button-tone-muted   { color: var(--color-muted); }
  .cbui-button-link:hover { text-decoration-thickness: 2px; }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  LIFT / SHEEN modifiers — vertical depth gradient                    */
  /*                                                                      */
  /*  +lift  — white gradient bottom→top  (light rising from the bottom)  */
  /*  +sheen — white gradient top→bottom  (light falling from the top)    */
  /*                                                                      */
  /*  Work on any filled background (solid, fill). On hover/active the    */
  /*  shade overlay is stacked above the depth gradient so both show.     */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-button-mod-bg-lift {
    background-image: linear-gradient(to top, color-mix(in srgb, white 12%, transparent), transparent);
  }
  .cbui-button-solid.cbui-button-mod-bg-lift:hover,
  .cbui-button-mod-bg-fill.cbui-button-mod-bg-lift:hover {
    background-image:
      linear-gradient(var(--cbui-shade-soft), var(--cbui-shade-soft)),
      linear-gradient(to top, color-mix(in srgb, white 12%, transparent), transparent);
  }
  .cbui-button-solid.cbui-button-mod-bg-lift:active,
  .cbui-button-mod-bg-fill.cbui-button-mod-bg-lift:active {
    background-image:
      linear-gradient(var(--cbui-shade-strong), var(--cbui-shade-strong)),
      linear-gradient(to top, color-mix(in srgb, white 12%, transparent), transparent);
  }

  .cbui-button-mod-bg-sheen {
    background-image: linear-gradient(to bottom, color-mix(in srgb, white 12%, transparent), transparent);
  }
  .cbui-button-solid.cbui-button-mod-bg-sheen:hover,
  .cbui-button-mod-bg-fill.cbui-button-mod-bg-sheen:hover {
    background-image:
      linear-gradient(var(--cbui-shade-soft), var(--cbui-shade-soft)),
      linear-gradient(to bottom, color-mix(in srgb, white 12%, transparent), transparent);
  }
  .cbui-button-solid.cbui-button-mod-bg-sheen:active,
  .cbui-button-mod-bg-fill.cbui-button-mod-bg-sheen:active {
    background-image:
      linear-gradient(var(--cbui-shade-strong), var(--cbui-shade-strong)),
      linear-gradient(to bottom, color-mix(in srgb, white 12%, transparent), transparent);
  }

  /* HC: strip depth gradients */
  .a11y-high-contrast .cbui-button-mod-bg-lift,
  .a11y-high-contrast .cbui-button-mod-bg-sheen { background-image: none; }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  GLOW modifier — gloss sheen + tone-coloured outer halo              */
  /*                                                                      */
  /*  Composes with any base variant and modifier via the + separator:    */
  /*    solid+glow        → filled + gloss sheen + outer halo             */
  /*    outline+glow      → outlined + outer halo (no gloss)              */
  /*    outline+fill+glow → bordered + tinted + outer halo + gloss        */
  /*    ghost+fill+glow   → tinted (=soft) + outer halo + gloss           */
  /*    glow              → alias for solid+glow (normalized in template)  */
  /*                                                                      */
  /*  Interaction: hover lifts 1 px and deepens the halo.                 */
  /*  HC mode strips all effects. Reduced-motion strips the transform.    */
  /* ─────────────────────────────────────────────────────────────────── */

  /* ── Glow colour per tone ─────────────────────────────────────────── */
  .cbui-button-mod-shadow-glow.cbui-button-tone-neutral { --cbui-glow-color: var(--color-text); }
  .cbui-button-mod-shadow-glow.cbui-button-tone-primary { --cbui-glow-color: var(--color-primary); }
  .cbui-button-mod-shadow-glow.cbui-button-tone-accent  { --cbui-glow-color: var(--color-accent); }
  .cbui-button-mod-shadow-glow.cbui-button-tone-success { --cbui-glow-color: var(--color-success); }
  .cbui-button-mod-shadow-glow.cbui-button-tone-danger  { --cbui-glow-color: var(--color-danger); }
  .cbui-button-mod-shadow-glow.cbui-button-tone-warning { --cbui-glow-color: var(--color-warning); }
  .cbui-button-mod-shadow-glow.cbui-button-tone-info    { --cbui-glow-color: var(--color-info); }
  .cbui-button-mod-shadow-glow.cbui-button-tone-muted   { --cbui-glow-color: var(--color-muted); }

  /* ── Universal halo (all variants) ───────────────────────────────── */
  .cbui-button-mod-shadow-glow {
    --cbui-glow-color: var(--color-primary); /* fallback if tone class missing */
    opacity: 0.9;
    box-shadow:
      0 0 0 1px  color-mix(in srgb, var(--cbui-glow-color) 15%, transparent),
      0 2px 8px  color-mix(in srgb, var(--cbui-glow-color) 14%, transparent),
      0 6px 20px color-mix(in srgb, var(--cbui-glow-color) 8%, transparent);
  }
  .cbui-button-mod-shadow-glow:hover {
    transform: translateY(-1px);
    opacity: 1;
    box-shadow:
      0 0 0 1px  color-mix(in srgb, var(--cbui-glow-color) 20%, transparent),
      0 4px 12px color-mix(in srgb, var(--cbui-glow-color) 17%, transparent),
      0 8px 28px color-mix(in srgb, var(--cbui-glow-color) 10%, transparent);
  }
  .cbui-button-mod-shadow-glow:active {
    transform: translateY(0);
    opacity: 0.9;
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--cbui-glow-color) 13%, transparent),
      0 1px 4px  color-mix(in srgb, var(--cbui-glow-color) 10%, transparent);
  }

  /* ── Focus ring — restore over the glow box-shadow ───────────────── */
  /* The glow modifier's box-shadow would otherwise override the focus
     ring (same specificity, later in source order). This rule comes
     last and wins when the button is focused. */
  .cbui-button-mod-shadow-glow:focus-visible {
    box-shadow:
      0 0 0 var(--focus-ring-offset, 2px) var(--color-bg-base),
      0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring, var(--color-primary));
  }

  /* ── High Contrast — strip all glow effects ──────────────────────── */
  .a11y-high-contrast .cbui-button-mod-shadow-glow,
  .a11y-high-contrast .cbui-button-mod-shadow-glow:hover,
  .a11y-high-contrast .cbui-button-mod-shadow-glow:active {
    box-shadow: none;
    background-image: none;
    transform: none;
  }

  /* ── Reduced motion — no lift transform ──────────────────────────── */
  @media (prefers-reduced-motion: reduce) {
    .cbui-button-mod-shadow-glow:hover,
    .cbui-button-mod-shadow-glow:active { transform: none; }
  }
  .a11y-reduced-motion .cbui-button-mod-shadow-glow:hover,
  .a11y-reduced-motion .cbui-button-mod-shadow-glow:active { transform: none; }

  /* ── Sizes (text buttons) ───────────────────────────────────────────
     Type never renders below --font-size-md; size/density steer geometry only (Matthias 2026-10-17). */

  .cbui-button-xs { height: 1.5rem;  font-size: var(--font-size-md, 1rem);      padding-inline: 0.5rem;  gap: 0.25rem; }
  .cbui-button-sm { height: 2rem;    font-size: var(--font-size-md, 1rem);      padding-inline: 0.75rem; }
  .cbui-button-md { height: 2.5rem;  font-size: var(--font-size-md, 1rem);      padding-inline: 1rem; }
  .cbui-button-lg { height: 3rem;    font-size: var(--font-size-md, 1rem);      padding-inline: 1.5rem; }

  /* ── Density (CB-2914): lower height and side padding, text unchanged ── */

  .cbui-button-xs.cbui-button-density-compact { height: 1.25rem; }
  .cbui-button-xs.cbui-button-density-tight   { height: 1.125rem; }
  .cbui-button-sm.cbui-button-density-compact { height: 1.75rem; }
  .cbui-button-sm.cbui-button-density-tight   { height: 1.5rem; }
  .cbui-button-md.cbui-button-density-compact { height: 2rem; }
  .cbui-button-md.cbui-button-density-tight   { height: 1.75rem; }
  .cbui-button-lg.cbui-button-density-compact { height: 2.5rem; }
  .cbui-button-lg.cbui-button-density-tight   { height: 2.25rem; }

  .cbui-button-xs.cbui-button-density-compact:not(.cbui-button-icon-only) { padding-inline: 0.375rem; }
  .cbui-button-xs.cbui-button-density-tight:not(.cbui-button-icon-only)   { padding-inline: 0.25rem; }
  .cbui-button-sm.cbui-button-density-compact:not(.cbui-button-icon-only) { padding-inline: 0.5rem; }
  .cbui-button-sm.cbui-button-density-tight:not(.cbui-button-icon-only)   { padding-inline: 0.375rem; }
  .cbui-button-md.cbui-button-density-compact:not(.cbui-button-icon-only) { padding-inline: 0.75rem; }
  .cbui-button-md.cbui-button-density-tight:not(.cbui-button-icon-only)   { padding-inline: 0.5rem; }
  .cbui-button-lg.cbui-button-density-compact:not(.cbui-button-icon-only) { padding-inline: 1rem; }
  .cbui-button-lg.cbui-button-density-tight:not(.cbui-button-icon-only)   { padding-inline: 0.75rem; }

  /* ── Text size independent of size (CB-2914); xs and sm render md since CB-2918, the classes stay ── */

  .cbui-button.cbui-button-font-xs { font-size: var(--font-size-md, 1rem); }
  .cbui-button.cbui-button-font-sm { font-size: var(--font-size-md, 1rem); }
  .cbui-button.cbui-button-font-md { font-size: var(--font-size-md, 1rem); }
  .cbui-button.cbui-button-font-lg { font-size: var(--font-size-lg, 1.125rem); }

  /* ── Sizes (icon-only) — square aspect, no padding-inline ─────────── */

  .cbui-button-icon-only {
    padding-inline: 0;
    aspect-ratio: 1;
    gap: 0;
  }

  .cbui-button-icon-xs { height: 1.5rem; width: 1.5rem; padding-inline: 0; }
  .cbui-button-icon-sm { height: 2rem;   width: 2rem;   padding-inline: 0; }
  /* `--cbui-button-icon-size` exposes the DEFAULT icon-button's own square
   * dimension as a token — a consumer that needs to reserve layout space for
   * one of these buttons WITHOUT rendering it (e.g. `table.css`'s
   * `.cbui-table-actions-cell` min-width, CB-1722) reads this instead of
   * re-guessing the literal. */
  .cbui-button-icon    { height: 2.5rem; width: 2.5rem; padding-inline: 0; --cbui-button-icon-size: 2.5rem; }
  .cbui-button-icon-lg { height: 3rem;   width: 3rem;   padding-inline: 0; }

  /* ── Compacted by an overflow bar (CB-3525) — a text button with a leading icon shrinks to a square icon button ─
   * The overflow controller sets `data-overflow-compacted` on the button, or on the root of the Dropdown that holds it, and
   * mirrors the label into `title`. The label leaves the flow but stays in the accessibility tree; a trailing glyph goes. */
  .cbui-button[data-overflow-compacted]:has(.cbui-button-glyph-start),
  [data-overflow-compacted] .cbui-dropdown-trigger .cbui-button:has(.cbui-button-glyph-start) {
    padding-inline: 0;
    aspect-ratio: 1;
    gap: 0;
    justify-content: center;
  }

  .cbui-button[data-overflow-compacted]:has(.cbui-button-glyph-start) > .cbui-button-label,
  [data-overflow-compacted] .cbui-dropdown-trigger .cbui-button:has(.cbui-button-glyph-start) > .cbui-button-label {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .cbui-button[data-overflow-compacted] > .cbui-button-glyph-end,
  [data-overflow-compacted] .cbui-dropdown-trigger .cbui-button > .cbui-button-glyph-end {
    display: none;
  }

  /* ── Modifiers ────────────────────────────────────────────────────── */

  .cbui-button-block {
    display: flex;
    width: 100%;
  }

  .cbui-button-disabled,
  .cbui-button[disabled],
  .cbui-button[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
  }

  /* Toggle "on" state — works on any base variant via attribute selector. */
  .cbui-button[aria-pressed="true"] {
    background-color: color-mix(in srgb, var(--color-primary) 18%, transparent);
    color: var(--color-primary);
    border-color: color-mix(in srgb, var(--color-primary) 35%, transparent);
  }
  .cbui-button[aria-pressed="true"]:hover {
    background-color: color-mix(in srgb, var(--color-primary) 26%, transparent);
  }

  /* ── Loading ──────────────────────────────────────────────────────── */

  .cbui-button-loading {
    cursor: wait;
    pointer-events: none;
  }
  .cbui-button-loading .cbui-button-label {
    opacity: 0.7;
  }

  /* CB-1491: the spinner inserted by ui-component--submit-busy takes the leading-icon slot
     (the server-rendered loading state simply does not render the glyph). */
  .cbui-button-loading > [data-ui-component--submit-busy-spinner] ~ .cbui-button-glyph-start,
  .cbui-button-loading.cbui-button-icon-only > [data-ui-component--submit-busy-spinner] ~ .cbui-button-glyph {
    display: none;
  }

  .cbui-button-spinner {
    width: 1em;
    height: 1em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: cbui-button-spin 0.7s linear infinite;
    flex: none;
  }
  @keyframes cbui-button-spin { to { transform: rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) {
    .cbui-button-spinner { animation-duration: 1.6s; }
    .cbui-button { transition: none; }
  }
  /* Scope-level reduced-motion (set by Storybook a11y dropdown) */
  .a11y-reduced-motion .cbui-button {
    transition: none;
  }
  .a11y-reduced-motion .cbui-button-spinner {
    animation-duration: 1.6s;
  }

  /* ── High Contrast ────────────────────────────────────────────────── */

  /* Goal: clearly visible borders and snap state changes. We bump border
     widths, replace soft tints with solid backgrounds, and remove the
     transparent shading overlay so colours are predictable for users with
     low vision. */
  .a11y-high-contrast .cbui-button {
    border-width: 2px;
    transition: none;
  }
  /* Solid: keep the colour but ensure the border is opaque. */
  .a11y-high-contrast .cbui-button-solid {
    border-color: currentColor;
  }
  /* Hover/active in HC: do not soften (overlay), instead invert text+bg
     swap for the strongest contrast shift. */
  .a11y-high-contrast .cbui-button-solid:hover {
    background-image: none;
    filter: brightness(1.15) contrast(1.1);
  }
  .a11y-high-contrast .cbui-button-solid:active {
    background-image: none;
    filter: brightness(0.85) contrast(1.2);
  }

  /* Outline / ghost: opaque border, solid hover */
  .a11y-high-contrast .cbui-button-outline,
  .a11y-high-contrast .cbui-button-ghost {
    border-color: currentColor;
  }
  .a11y-high-contrast .cbui-button-outline.cbui-button-tone-neutral:hover,
  .a11y-high-contrast .cbui-button-ghost.cbui-button-tone-neutral:hover {
    background-color: var(--color-text);
    color: var(--color-bg-base);
  }
  .a11y-high-contrast .cbui-button-outline.cbui-button-tone-primary:hover,
  .a11y-high-contrast .cbui-button-ghost.cbui-button-tone-primary:hover  { background-color: var(--color-primary); color: var(--color-text-on-primary); }
  .a11y-high-contrast .cbui-button-outline.cbui-button-tone-success:hover,
  .a11y-high-contrast .cbui-button-ghost.cbui-button-tone-success:hover  { background-color: var(--color-success); color: var(--color-text-on-success); }
  .a11y-high-contrast .cbui-button-outline.cbui-button-tone-danger:hover,
  .a11y-high-contrast .cbui-button-ghost.cbui-button-tone-danger:hover   { background-color: var(--color-danger);  color: var(--color-text-on-danger); }
  .a11y-high-contrast .cbui-button-outline.cbui-button-tone-warning:hover,
  .a11y-high-contrast .cbui-button-ghost.cbui-button-tone-warning:hover  { background-color: var(--color-warning); color: var(--color-text-on-warning); }
  .a11y-high-contrast .cbui-button-outline.cbui-button-tone-info:hover,
  .a11y-high-contrast .cbui-button-ghost.cbui-button-tone-info:hover     { background-color: var(--color-info);    color: var(--color-text-on-info); }
  .a11y-high-contrast .cbui-button-outline.cbui-button-tone-accent:hover,
  .a11y-high-contrast .cbui-button-ghost.cbui-button-tone-accent:hover   { background-color: var(--color-accent);  color: var(--color-text-on-accent); }
  .a11y-high-contrast .cbui-button-outline.cbui-button-tone-muted:hover,
  .a11y-high-contrast .cbui-button-ghost.cbui-button-tone-muted:hover    { background-color: var(--color-muted);   color: var(--color-text-on-muted); }

  /* Fill modifier in HC: become a solid filled button using the tone colour */
  .a11y-high-contrast .cbui-button-mod-bg-fill.cbui-button-tone-neutral { background-color: var(--color-text);    color: var(--color-bg-base);          border-color: var(--color-text); }
  .a11y-high-contrast .cbui-button-mod-bg-fill.cbui-button-tone-primary { background-color: var(--color-primary); color: var(--color-text-on-primary); border-color: var(--color-primary); }
  .a11y-high-contrast .cbui-button-mod-bg-fill.cbui-button-tone-success { background-color: var(--color-success); color: var(--color-text-on-success); border-color: var(--color-success); }
  .a11y-high-contrast .cbui-button-mod-bg-fill.cbui-button-tone-danger  { background-color: var(--color-danger);  color: var(--color-text-on-danger);  border-color: var(--color-danger); }
  .a11y-high-contrast .cbui-button-mod-bg-fill.cbui-button-tone-warning { background-color: var(--color-warning); color: var(--color-text-on-warning); border-color: var(--color-warning); }
  .a11y-high-contrast .cbui-button-mod-bg-fill.cbui-button-tone-info    { background-color: var(--color-info);    color: var(--color-text-on-info);    border-color: var(--color-info); }
  .a11y-high-contrast .cbui-button-mod-bg-fill.cbui-button-tone-accent  { background-color: var(--color-accent);  color: var(--color-text-on-accent);  border-color: var(--color-accent); }
  .a11y-high-contrast .cbui-button-mod-bg-fill.cbui-button-tone-muted   { background-color: var(--color-muted);   color: var(--color-text-on-muted);   border-color: var(--color-muted); }

  /* Toggle on in HC: swap to solid primary fill */
  .a11y-high-contrast .cbui-button[aria-pressed="true"] {
    background-color: var(--color-primary);
    color: var(--color-text-on-primary);
    border-color: var(--color-primary);
  }

  /* Disabled in HC: keep visible (don't drop opacity to 0.5) — show the
     state via reduced saturation + crosshatch border instead. */
  .a11y-high-contrast .cbui-button-disabled,
  .a11y-high-contrast .cbui-button[disabled],
  .a11y-high-contrast .cbui-button[aria-disabled="true"] {
    opacity: 0.75;
    border-style: dashed;
  }

  /* Link in HC: thicker underline, no opacity drop */
  .a11y-high-contrast .cbui-button-link {
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
  }

  /* ── Icon glyphs (the SVG inside the button) ──────────────────────── */

  .cbui-button-glyph {
    display: inline-flex;
    flex: none;
    width: 1em;
    height: 1em;
  }
  .cbui-button-glyph-xs { width: 0.875em; height: 0.875em; }
  .cbui-button-glyph-sm { width: 1em;     height: 1em; }
  .cbui-button-glyph-lg { width: 1.25em;  height: 1.25em; }
}

/* cbui/components/button-group */
/* cbui-button-group — connected or clustered row of Buttons.
 *
 * Architecture — TWO modes only:
 *
 *   1. `attached` (default):
 *        - The GROUP owns the chrome: one continuous border + one radius.
 *        - Children drop their own border and become tinted segments.
 *        - Hairline dividers between adjacent segments — same colour as
 *          the frame.
 *        - State (hover, pressed, focus) paints via BACKGROUND only.
 *          Border colour is locked — there is no per-button border to
 *          flicker, the group's border is one stable line.
 *        - The outer radius is the group's own segment-scale token
 *          `--button-group-radius` (radius-md; radius-sm in Sharp) — never
 *          the Button pill. An attached group is section chrome, so Aero
 *          and Soft keep it as calm as Clean (css-architecture.md,
 *          Radius discipline).
 *
 *   2. `cluster` (attached=false):
 *        - Independent buttons with a gap; each child keeps its own
 *          full chrome (border + radius). The right choice when the
 *          buttons are unrelated actions that just happen to be aligned
 *          as a row (Save | Cancel | Delete).
 *
 * The earlier `framed` opt-in is gone — `attached` IS framed by design.
 * "Bootstrap-style border-collapse" would force every consumer to be
 * aware of per-button hover-border-flicker. We instead pick ONE owner
 * (the group), and the result reads as a designed control rather than
 * a set of buttons that happen to touch.
 *
 * Quality contract — across every flavor:
 *   1. NO transform / translate / scale on hover, active or pressed
 *      inside the group. Aero's button :hover-lift is suppressed.
 *   2. Border colour is FIXED — does not change on hover, active or
 *      pressed. Only the background tint conveys state.
 *   3. Vertical groups follow EXACTLY the horizontal contract.
 *
 * Variants:
 *   .cbui-button-group                 base (horizontal, attached)
 *   .cbui-button-group-vertical        stacked column
 *   .cbui-button-group-cluster         spaced row (gap, independent buttons)
 *   .cbui-button-group-width-full      stretch to container width; children
 *                                       share width equally
 */

@layer components {

  .cbui-button-group {
    display: inline-flex;
    flex-direction: row;
    align-items: stretch;
  }

  .cbui-button-group-vertical {
    flex-direction: column;
  }

  .cbui-button-group-width-full {
    display: flex;
    width: 100%;
  }
  .cbui-button-group-width-full > .cbui-button {
    flex: 1 1 0;
    min-width: 0;
  }

  /* ── Cluster mode — independent buttons with gap ───────────────────────── */
  .cbui-button-group-cluster {
    gap: 0.375rem;
    align-items: center;
  }
  .cbui-button-group-cluster.cbui-button-group-vertical {
    align-items: stretch;
  }
  .cbui-button-group-size-sm.cbui-button-group-cluster { gap: 0.25rem; }
  .cbui-button-group-size-lg.cbui-button-group-cluster { gap: 0.5rem; }
  .cbui-button-group-density-compact.cbui-button-group-cluster,
  .cbui-button-group-density-tight.cbui-button-group-cluster { gap: 0.25rem; }

  /* ────────────────────────────────────────────────────────────────────────
   * Attached mode — the group owns ONE continuous frame.
   *
   * The group paints the border + radius once. Children drop their own
   * border / radius / shadow / background so the frame reads as a single
   * designed control. Hairline dividers separate adjacent segments —
   * same colour as the frame so the seam is continuous.
   * ────────────────────────────────────────────────────────────────────── */

  .cbui-button-group-attached {
    --cbui-bg-frame-color: var(--color-border);
    --cbui-bg-divider-color: var(--color-border);
    --cbui-bg-bg-color: transparent;

    border: 1px solid var(--cbui-bg-frame-color);
    border-radius: var(--button-group-radius, var(--radius-md, 0.5rem));
    background-color: var(--cbui-bg-bg-color);
    overflow: hidden;
  }

  /* Strip per-button chrome — the group owns everything. */
  .cbui-button-group-attached > .cbui-button {
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    background-color: transparent;
    background-image: none;
    margin: 0 !important;
    transform: none !important;
    /* Transitions: bg + colour + text effects may animate; transforms /
       layout properties must not. */
    transition:
      background-color 150ms ease,
      color 150ms ease;
  }

  /* Hairline dividers between adjacent segments (horizontal). */
  .cbui-button-group-attached:not(.cbui-button-group-vertical) > .cbui-button:not(:first-child) {
    border-inline-start: 1px solid var(--cbui-bg-divider-color) !important;
  }
  /* Vertical: dividers between rows. */
  .cbui-button-group-attached.cbui-button-group-vertical > .cbui-button:not(:first-child) {
    border-block-start: 1px solid var(--cbui-bg-divider-color) !important;
  }

  /* State painting — ONLY background. No border-color, no transform. */
  .cbui-button-group-attached > .cbui-button:hover {
    background-color: color-mix(in srgb, var(--color-text) 6%, transparent);
  }
  .cbui-button-group-attached > .cbui-button:active,
  .cbui-button-group-attached > .cbui-button[aria-pressed="true"] {
    background-color: color-mix(in srgb, var(--color-text) 12%, transparent);
  }

  /* Tone-aware pressed/hover backgrounds (when the button carries a tone). */
  .cbui-button-group-attached > .cbui-button-tone-primary[aria-pressed="true"] {
    background-color: color-mix(in srgb, var(--color-primary) 14%, transparent);
    color: var(--color-primary);
  }
  .cbui-button-group-attached > .cbui-button-tone-success[aria-pressed="true"] {
    background-color: color-mix(in srgb, var(--color-success) 14%, transparent);
    color: var(--color-success);
  }
  .cbui-button-group-attached > .cbui-button-tone-warning[aria-pressed="true"] {
    background-color: color-mix(in srgb, var(--color-warning) 14%, transparent);
    color: var(--color-warning);
  }
  .cbui-button-group-attached > .cbui-button-tone-danger[aria-pressed="true"] {
    background-color: color-mix(in srgb, var(--color-danger) 14%, transparent);
    color: var(--color-danger);
  }
  .cbui-button-group-attached > .cbui-button-tone-info[aria-pressed="true"] {
    background-color: color-mix(in srgb, var(--color-info) 14%, transparent);
    color: var(--color-info);
  }
  .cbui-button-group-attached > .cbui-button-tone-accent[aria-pressed="true"] {
    background-color: color-mix(in srgb, var(--color-accent) 14%, transparent);
    color: var(--color-accent);
  }

  /* Focus — inset ring so the frame's `overflow: hidden` doesn't clip it. */
  .cbui-button-group-attached > .cbui-button:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--color-focus-ring) !important;
    z-index: 1;
    position: relative;
  }

  /* High-contrast — bump the frame width. */
  .a11y-high-contrast .cbui-button-group-attached {
    border-width: 2px;
  }
  .a11y-high-contrast .cbui-button-group-attached > .cbui-button:not(:first-child) {
    border-inline-start-width: 2px !important;
  }
  .a11y-high-contrast .cbui-button-group-attached.cbui-button-group-vertical > .cbui-button:not(:first-child) {
    border-block-start-width: 2px !important;
    border-inline-start-width: 0 !important;
  }

  /* Reduced motion — drop animation. */
  .a11y-reduced-motion .cbui-button-group > .cbui-button {
    transition: none;
  }
}

/* cbui/components/card */
/* cbui-card
 *
 * Two-axis variant model (shared with Button/Badge/Alert):
 *   variant = visual treatment: flat | outlined | elevated
 *   tone    = semantic colour:  neutral | primary | accent | success | danger
 *                              | warning | info | muted
 *
 * Modifiers (composable via + in the variant string):
 *   bg-fill                    — soft tinted background tied to the tone
 *   border-accent-{start|end|top|bottom}        — 3px tone bar on one edge,
 *                                                  corner radius preserved
 *   border-accent-{...}-sharp                    — same bar + zero radius on
 *                                                  the bar side
 *
 * Tone defaults to 'neutral' when omitted. Each variant+tone combination sets
 * --cbui-card-accent so the accent-bar modifiers do not need per-tone rules.
 *
 * Anchor border:
 *   .cbui-card-elevated reads --cbui-card-edge for a 1px anchor border in
 *   dark mode — without it the soft drop-shadow on a dark background looks
 *   like fog rather than lift. Same trick as Button.
 *
 * Accessibility:
 *   .a11y-reduced-motion → no transitions
 *   .a11y-high-contrast  → 2px borders on outlined/elevated, solid surfaces,
 *                          stronger hover treatment on interactive cards
 */

@layer components {

  /* ─────────────────────────────────────────────────────────────────── */
  /*  BASE + SHARED CASCADE TOKENS                                         */
  /* ─────────────────────────────────────────────────────────────────── */

  :where(.cbui-card) {
    /* Hover/active overlays — the universal CBUI shading vars. Per-flavor
       and dark-mode rules retune these without each variant needing its
       own colour. */
    --cbui-shade-soft: rgb(0 0 0 / 0.04);
    --cbui-shade-strong: rgb(0 0 0 / 0.08);

    /* Anchor edge for elevated cards in dark mode (see button.css). */
    --cbui-card-edge: transparent;
  }
  .dark :where(.cbui-card),
  [data-scheme="dark"] :where(.cbui-card) {
    --cbui-shade-soft: rgb(255 255 255 / 0.05);
    --cbui-shade-strong: rgb(255 255 255 / 0.10);
    --cbui-card-edge: rgb(0 0 0 / 0.40);
  }

  .cbui-card {
    /* Flex column instead of plain block — flexbox specs whitespace-only
       text nodes between flex items as ignored, so the indentation
       newlines from the Twig template don't render as 24px-tall line
       boxes between Card and Header (the bug that produced the white
       strip above the Interactive button card header). */
    display: flex;
    flex-direction: column;
    /* Reset — native <button>/<a> bring their own padding/font/text-align
       chrome. Card body/header/footer drive the visible padding via the
       size cascade below. */
    padding: 0;
    margin: 0;
    border-radius: var(--card-radius);
    border: 1px solid transparent;
    background-color: transparent;
    color: inherit;
    text-decoration: none;
    text-align: inherit;
    font: inherit;
    /* Clip slot bands and accent-bar fills to the card curve so they hug
       the rounded outer shape automatically — no per-modifier corner
       maths needed. The drop shadow on elevated lives outside the box
       and is not clipped. */
    overflow: hidden;
    /* Resolved per size — surfaces below pull from these. */
    --cbui-card-pad-x: var(--card-padding-md);
    --cbui-card-pad-y: var(--card-padding-md);
    --cbui-card-header-pad-y: var(--card-header-padding-y-md);
    transition:
      background-color 150ms ease,
      background-image 150ms ease,
      border-color 150ms ease,
      box-shadow 150ms ease,
      transform 120ms ease;
  }

  /* Cards are flow containers, not flex parents — body content lays itself
     out. We just zero the natural margins on first/last children so the
     padding looks intentional. */
  .cbui-card-body > :first-child,
  .cbui-card-header > :first-child,
  .cbui-card-footer > :first-child { margin-block-start: 0; }
  .cbui-card-body > :last-child,
  .cbui-card-header > :last-child,
  .cbui-card-footer > :last-child  { margin-block-end: 0; }

  /* ── Header height scale ─────────────────────────────────────────────
   * Token-aligned min-height tier (shared --cbui-bar-h-* scale). Padding
   * stays on so the inner content keeps its breathing room. */
  .cbui-card-header-h-xs,
  .cbui-card-header-h-sm,
  .cbui-card-header-h-md,
  .cbui-card-header-h-lg,
  .cbui-card-header-h-xl { display: flex; align-items: center; }
  .cbui-card-header-h-xs { min-height: var(--cbui-bar-h-xs); }
  .cbui-card-header-h-sm { min-height: var(--cbui-bar-h-sm); }
  .cbui-card-header-h-md { min-height: var(--cbui-bar-h-md); }
  .cbui-card-header-h-lg { min-height: var(--cbui-bar-h-lg); }
  .cbui-card-header-h-xl { min-height: var(--cbui-bar-h-xl); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  SIZE — drives the cascade tokens                                    */
  /* ─────────────────────────────────────────────────────────────────── */

  /* xs is the densest tier — for tile-like cards in Kanban boards or
     dashboard grids where you fit lots of cards into a small area. The
     header band almost touches the body. */
  .cbui-card-size-xs {
    --cbui-card-pad-x: var(--card-padding-xs);
    --cbui-card-pad-y: var(--card-padding-xs);
    --cbui-card-header-pad-y: var(--card-header-padding-y-xs);
  }
  .cbui-card-size-sm {
    --cbui-card-pad-x: var(--card-padding-sm);
    --cbui-card-pad-y: var(--card-padding-sm);
    --cbui-card-header-pad-y: var(--card-header-padding-y-sm);
  }
  .cbui-card-size-md {
    --cbui-card-pad-x: var(--card-padding-md);
    --cbui-card-pad-y: var(--card-padding-md);
    --cbui-card-header-pad-y: var(--card-header-padding-y-md);
  }
  .cbui-card-size-lg {
    --cbui-card-pad-x: var(--card-padding-lg);
    --cbui-card-pad-y: var(--card-padding-lg);
    --cbui-card-header-pad-y: var(--card-header-padding-y-lg);
    font-size: var(--font-size-md, 1rem);
  }

  /* Slot padding — header/footer use a tighter block padding so they read as
     bands rather than mini-bodies. Inline padding matches the body. */
  .cbui-card-body {
    padding-inline: var(--cbui-card-pad-x);
    padding-block: var(--cbui-card-pad-y);
    /* Stretch to fill the card vertically when the card has an explicit
       height (e.g. inside a `grid-auto-rows: 1fr` Kanban column) — the
       footer then sticks to the bottom of every card so a column of
       cards lines up its action strips. With auto-height cards (the
       default) this is a no-op because flex-basis: auto preserves the
       intrinsic content height. */
    flex: 1 1 auto;
    min-height: 0;
  }
  .cbui-card-header,
  .cbui-card-footer {
    padding-inline: var(--cbui-card-pad-x);
    padding-block: var(--cbui-card-header-pad-y);
  }

  /* Flush variants — drop the slot's own padding so the caller can paint
     edge-to-edge. Useful for cover images, embedded tables, custom grids,
     full-width toolbars or progress bars. The slot still participates in
     the auto-divider logic, so dividers="header" still draws a line below
     a flushed header even with no padding. */
  .cbui-card-header-flush,
  .cbui-card-body-flush,
  .cbui-card-footer-flush {
    padding: 0;
  }

  /* Dividers — a 1px line painted with --cbui-card-divider, set per variant
     so it sits visually correct on any surface. Auto/explicit logic lives
     in the Twig template; we just react to the resulting class. */
  .cbui-card-divider-header .cbui-card-header {
    border-block-end: 1px solid var(--cbui-card-divider, var(--color-border));
  }
  .cbui-card-divider-footer .cbui-card-footer {
    border-block-start: 1px solid var(--cbui-card-divider, var(--color-border));
  }

  /* Edge-corner-radius rule: the first/last visible child of a rounded
     panel keeps the panel curve. With overflow:hidden on .cbui-card the
     band fills get clipped to the card curve automatically — no per-
     modifier corner math required (the accent-bar modifiers can change
     border-width on one side without breaking the slot band geometry). */

  /* ─────────────────────────────────────────────────────────────────── */
  /*  FLAT variant — surface fill, no border, no shadow                   */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-card-flat {
    background-color: var(--color-bg-subtle);
    border-color: transparent;
    --cbui-card-divider: var(--color-border);
  }
  .cbui-card-flat.cbui-card-tone-neutral { --cbui-card-accent: var(--color-border-strong); }
  .cbui-card-flat.cbui-card-tone-primary { --cbui-card-accent: var(--color-primary); }
  .cbui-card-flat.cbui-card-tone-accent  { --cbui-card-accent: var(--color-accent); }
  .cbui-card-flat.cbui-card-tone-success { --cbui-card-accent: var(--color-success); }
  .cbui-card-flat.cbui-card-tone-danger  { --cbui-card-accent: var(--color-danger); }
  .cbui-card-flat.cbui-card-tone-warning { --cbui-card-accent: var(--color-warning); }
  .cbui-card-flat.cbui-card-tone-info    { --cbui-card-accent: var(--color-info); }
  .cbui-card-flat.cbui-card-tone-muted   { --cbui-card-accent: var(--color-text-secondary); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  OUTLINED variant — border, transparent fill (default container)    */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-card-outlined {
    background-color: var(--color-bg-base);
    --cbui-card-divider: var(--color-border);
  }
  .cbui-card-outlined.cbui-card-tone-neutral { border-color: var(--color-border); --cbui-card-accent: var(--color-border-strong); }
  .cbui-card-outlined.cbui-card-tone-primary { border-color: color-mix(in srgb, var(--color-primary) 35%, var(--color-border)); --cbui-card-accent: var(--color-primary); }
  .cbui-card-outlined.cbui-card-tone-accent  { border-color: color-mix(in srgb, var(--color-accent)  35%, var(--color-border)); --cbui-card-accent: var(--color-accent); }
  .cbui-card-outlined.cbui-card-tone-success { border-color: color-mix(in srgb, var(--color-success) 35%, var(--color-border)); --cbui-card-accent: var(--color-success); }
  .cbui-card-outlined.cbui-card-tone-danger  { border-color: color-mix(in srgb, var(--color-danger)  35%, var(--color-border)); --cbui-card-accent: var(--color-danger); }
  .cbui-card-outlined.cbui-card-tone-warning { border-color: color-mix(in srgb, var(--color-warning) 35%, var(--color-border)); --cbui-card-accent: var(--color-warning); }
  .cbui-card-outlined.cbui-card-tone-info    { border-color: color-mix(in srgb, var(--color-info)    35%, var(--color-border)); --cbui-card-accent: var(--color-info); }
  .cbui-card-outlined.cbui-card-tone-muted   { border-color: var(--color-border); --cbui-card-accent: var(--color-text-secondary); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  ELEVATED variant — surface fill + soft shadow                       */
  /*                                                                      */
  /*  Anchor border in dark mode keeps the lift legible (--cbui-card-edge */
  /*  resolves to a translucent black 1px line; transparent in light).    */
  /* ─────────────────────────────────────────────────────────────────── */

  /* Elevated picks the surface that sits ABOVE the page (--color-bg-elevated
     in NeutralTheme: #fff in light, #262626 in dark). Not surface-elevated —
     that token is reserved for modals on a dimmed canvas and reads DARKER
     than bg-base in dark mode. */
  .cbui-card-elevated {
    background-color: var(--color-bg-elevated);
    border-color: var(--cbui-card-edge);
    box-shadow: var(--shadow-md);
    --cbui-card-divider: var(--color-border);
  }
  /* Dark-mode lift recipe: drop-shadow alone reads as fog on a dark page.
     Stack a top-edge sheen + inline-start-edge sheen (light comes from
     above-left, the universal CBUI light source) for the "raised tile"
     impression, plus a deeper drop and a 1px black anchor edge so the
     soft shadow has something to root against. */
  .dark .cbui-card-elevated,
  [data-scheme="dark"] .cbui-card-elevated {
    box-shadow:
      inset 0 1px 0 rgb(255 255 255 / 0.06),
      inset 1px 0 0 rgb(255 255 255 / 0.04),
      0 1px 2px rgb(0 0 0 / 0.6),
      0 6px 16px rgb(0 0 0 / 0.45);
  }
  .cbui-card-elevated.cbui-card-tone-neutral { --cbui-card-accent: var(--color-border-strong); }
  .cbui-card-elevated.cbui-card-tone-primary { --cbui-card-accent: var(--color-primary); }
  .cbui-card-elevated.cbui-card-tone-accent  { --cbui-card-accent: var(--color-accent); }
  .cbui-card-elevated.cbui-card-tone-success { --cbui-card-accent: var(--color-success); }
  .cbui-card-elevated.cbui-card-tone-danger  { --cbui-card-accent: var(--color-danger); }
  .cbui-card-elevated.cbui-card-tone-warning { --cbui-card-accent: var(--color-warning); }
  .cbui-card-elevated.cbui-card-tone-info    { --cbui-card-accent: var(--color-info); }
  .cbui-card-elevated.cbui-card-tone-muted   { --cbui-card-accent: var(--color-text-secondary); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  +ring modifier — visible hairline edge                             */
  /*                                                                      */
  /*  Repaints the card's existing 1px border (which is `transparent` on  */
  /*  flat / elevated) to a real --color-border hairline, with NO layout  */
  /*  shift — the box already reserves the 1px. Lets an `elevated` card    */
  /*  keep a defining edge in light mode where the drop-shadow alone is    */
  /*  too soft to read as a contained box (dark mode already carries the   */
  /*  --cbui-card-edge anchor). Composes with any variant/tone; specificity */
  /*  bumped (.cbui-card.cbui-card-mod-…) so it outranks the variant base.  */
  .cbui-card.cbui-card-mod-ring {
    border-color: var(--color-border);
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  bg-fill modifier — soft tinted background                           */
  /*                                                                      */
  /*  Layered as background-image so it composes with the variant's       */
  /*  background-color (elevated keeps its surface beneath the tint).     */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-card-mod-bg-fill.cbui-card-tone-neutral  { background-image: linear-gradient(var(--color-bg-subtle), var(--color-bg-subtle)); }
  .cbui-card-mod-bg-fill.cbui-card-tone-primary  { background-image: linear-gradient(color-mix(in srgb, var(--color-primary) 10%, transparent), color-mix(in srgb, var(--color-primary) 10%, transparent)); }
  .cbui-card-mod-bg-fill.cbui-card-tone-accent   { background-image: linear-gradient(color-mix(in srgb, var(--color-accent)  10%, transparent), color-mix(in srgb, var(--color-accent)  10%, transparent)); }
  .cbui-card-mod-bg-fill.cbui-card-tone-success  { background-image: linear-gradient(color-mix(in srgb, var(--color-success) 10%, transparent), color-mix(in srgb, var(--color-success) 10%, transparent)); }
  .cbui-card-mod-bg-fill.cbui-card-tone-danger   { background-image: linear-gradient(color-mix(in srgb, var(--color-danger)  10%, transparent), color-mix(in srgb, var(--color-danger)  10%, transparent)); }
  .cbui-card-mod-bg-fill.cbui-card-tone-warning  { background-image: linear-gradient(color-mix(in srgb, var(--color-warning) 10%, transparent), color-mix(in srgb, var(--color-warning) 10%, transparent)); }
  .cbui-card-mod-bg-fill.cbui-card-tone-info     { background-image: linear-gradient(color-mix(in srgb, var(--color-info)    10%, transparent), color-mix(in srgb, var(--color-info)    10%, transparent)); }

  /* Dark mode boosts — same magnitude as Alert. */
  .dark .cbui-card-mod-bg-fill.cbui-card-tone-primary,  [data-scheme="dark"] .cbui-card-mod-bg-fill.cbui-card-tone-primary  { background-image: linear-gradient(color-mix(in srgb, var(--color-primary) 16%, transparent), color-mix(in srgb, var(--color-primary) 16%, transparent)); }
  .dark .cbui-card-mod-bg-fill.cbui-card-tone-accent,   [data-scheme="dark"] .cbui-card-mod-bg-fill.cbui-card-tone-accent   { background-image: linear-gradient(color-mix(in srgb, var(--color-accent)  16%, transparent), color-mix(in srgb, var(--color-accent)  16%, transparent)); }
  .dark .cbui-card-mod-bg-fill.cbui-card-tone-info,     [data-scheme="dark"] .cbui-card-mod-bg-fill.cbui-card-tone-info     { background-image: linear-gradient(color-mix(in srgb, var(--color-info)    16%, transparent), color-mix(in srgb, var(--color-info)    16%, transparent)); }
  .dark .cbui-card-mod-bg-fill.cbui-card-tone-success,  [data-scheme="dark"] .cbui-card-mod-bg-fill.cbui-card-tone-success  { background-image: linear-gradient(color-mix(in srgb, var(--color-success) 16%, transparent), color-mix(in srgb, var(--color-success) 16%, transparent)); }
  .dark .cbui-card-mod-bg-fill.cbui-card-tone-warning,  [data-scheme="dark"] .cbui-card-mod-bg-fill.cbui-card-tone-warning  { background-image: linear-gradient(color-mix(in srgb, var(--color-warning) 16%, transparent), color-mix(in srgb, var(--color-warning) 16%, transparent)); }
  .dark .cbui-card-mod-bg-fill.cbui-card-tone-danger,   [data-scheme="dark"] .cbui-card-mod-bg-fill.cbui-card-tone-danger   { background-image: linear-gradient(color-mix(in srgb, var(--color-danger)  16%, transparent), color-mix(in srgb, var(--color-danger)  16%, transparent)); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  +header-fill / +footer-fill modifiers                              */
  /*                                                                      */
  /*  Tone-coloured slot bands without tinting the body. Useful as a      */
  /*  task/ticket-card pattern: status colour at the header strip, the    */
  /*  body stays neutral so content is fully readable.                    */
  /*                                                                      */
  /*  Specificity bumped (.cbui-card.cbui-card-mod-…) for the same        */
  /*  reason the accent-bar modifiers are bumped — a flavor-tinted        */
  /*  base band would otherwise outrank a 1-class modifier.               */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-card.cbui-card-mod-header-fill > .cbui-card-header,
  .cbui-card.cbui-card-mod-footer-fill > .cbui-card-footer {
    color: inherit;
  }
  .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-neutral > .cbui-card-header,
  .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-neutral > .cbui-card-footer { background-color: var(--color-bg-muted); }
  .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-primary > .cbui-card-header,
  .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-primary > .cbui-card-footer { background-color: color-mix(in srgb, var(--color-primary) 14%, transparent); }
  .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-accent > .cbui-card-header,
  .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-accent > .cbui-card-footer  { background-color: color-mix(in srgb, var(--color-accent)  14%, transparent); }
  .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-success > .cbui-card-header,
  .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-success > .cbui-card-footer { background-color: color-mix(in srgb, var(--color-success) 14%, transparent); }
  .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-danger > .cbui-card-header,
  .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-danger > .cbui-card-footer  { background-color: color-mix(in srgb, var(--color-danger)  14%, transparent); }
  .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-warning > .cbui-card-header,
  .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-warning > .cbui-card-footer { background-color: color-mix(in srgb, var(--color-warning) 14%, transparent); }
  .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-info > .cbui-card-header,
  .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-info > .cbui-card-footer    { background-color: color-mix(in srgb, var(--color-info)    14%, transparent); }

  /* Dark mode boosts — match Alert/bg-fill magnitude. */
  .dark .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-primary > .cbui-card-header,
  .dark .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-primary > .cbui-card-footer,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-primary > .cbui-card-header,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-primary > .cbui-card-footer { background-color: color-mix(in srgb, var(--color-primary) 22%, transparent); }
  .dark .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-success > .cbui-card-header,
  .dark .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-success > .cbui-card-footer,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-success > .cbui-card-header,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-success > .cbui-card-footer { background-color: color-mix(in srgb, var(--color-success) 22%, transparent); }
  .dark .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-warning > .cbui-card-header,
  .dark .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-warning > .cbui-card-footer,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-warning > .cbui-card-header,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-warning > .cbui-card-footer { background-color: color-mix(in srgb, var(--color-warning) 22%, transparent); }
  .dark .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-danger > .cbui-card-header,
  .dark .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-danger > .cbui-card-footer,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-danger > .cbui-card-header,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-danger > .cbui-card-footer { background-color: color-mix(in srgb, var(--color-danger) 22%, transparent); }
  .dark .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-info > .cbui-card-header,
  .dark .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-info > .cbui-card-footer,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-info > .cbui-card-header,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-info > .cbui-card-footer    { background-color: color-mix(in srgb, var(--color-info)    22%, transparent); }
  .dark .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-accent > .cbui-card-header,
  .dark .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-accent > .cbui-card-footer,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-header-fill.cbui-card-tone-accent > .cbui-card-header,
  [data-scheme="dark"] .cbui-card.cbui-card-mod-footer-fill.cbui-card-tone-accent > .cbui-card-footer  { background-color: color-mix(in srgb, var(--color-accent)  22%, transparent); }

  /* Slot bands — header/footer get a slightly stronger surface so they
     read as bands. On flat the body already sits on a tinted surface, so
     we lift the band to bg-canvas (the page underneath); on outlined and
     elevated we use bg-subtle. */
  .cbui-card-flat > .cbui-card-header,
  .cbui-card-flat > .cbui-card-footer {
    background-color: color-mix(in srgb, currentColor 4%, transparent);
  }
  .cbui-card-outlined > .cbui-card-header,
  .cbui-card-outlined > .cbui-card-footer,
  .cbui-card-elevated > .cbui-card-header,
  .cbui-card-elevated > .cbui-card-footer {
    background-color: var(--color-bg-subtle);
  }
  /* Dark-mode header/footer band — match the Dropdown header/footer
     pattern: a faint lift rather than a raw bg-subtle. */
  .dark .cbui-card-outlined > .cbui-card-header,
  .dark .cbui-card-outlined > .cbui-card-footer,
  .dark .cbui-card-elevated > .cbui-card-header,
  .dark .cbui-card-elevated > .cbui-card-footer,
  [data-scheme="dark"] .cbui-card-outlined > .cbui-card-header,
  [data-scheme="dark"] .cbui-card-outlined > .cbui-card-footer,
  [data-scheme="dark"] .cbui-card-elevated > .cbui-card-header,
  [data-scheme="dark"] .cbui-card-elevated > .cbui-card-footer {
    background-color: color-mix(in srgb, var(--color-bg-elevated) 70%, transparent);
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Accent-bar modifiers — tone-coloured edge bar                      */
  /*                                                                      */
  /*  Identical vocabulary and behaviour to Alert. Uses --cbui-card-accent */
  /*  set by every variant+tone combination above.                        */
  /* ─────────────────────────────────────────────────────────────────── */

  /* Specificity-bumped (.cbui-card.cbui-card-mod-…) so flavor rules like
     [data-flavor="aero"] .cbui-card-outlined { border-color: … } don't
     repaint the accent edge in the wrong colour. Same trick used elsewhere
     in CBUI when an opt-in modifier needs to outrank a flavor base style. */
  .cbui-card.cbui-card-mod-border-accent-start  { border-inline-start: 3px solid var(--cbui-card-accent, currentColor); }
  .cbui-card.cbui-card-mod-border-accent-end    { border-inline-end:   3px solid var(--cbui-card-accent, currentColor); }
  .cbui-card.cbui-card-mod-border-accent-top    { border-block-start:  3px solid var(--cbui-card-accent, currentColor); }
  .cbui-card.cbui-card-mod-border-accent-bottom { border-block-end:    3px solid var(--cbui-card-accent, currentColor); }

  .cbui-card.cbui-card-mod-border-accent-start-sharp {
    border-inline-start: 3px solid var(--cbui-card-accent, currentColor);
    border-start-start-radius: 0;
    border-end-start-radius: 0;
  }
  .cbui-card.cbui-card-mod-border-accent-end-sharp {
    border-inline-end: 3px solid var(--cbui-card-accent, currentColor);
    border-start-end-radius: 0;
    border-end-end-radius: 0;
  }
  .cbui-card.cbui-card-mod-border-accent-top-sharp {
    border-block-start: 3px solid var(--cbui-card-accent, currentColor);
    border-start-start-radius: 0;
    border-start-end-radius: 0;
  }
  .cbui-card.cbui-card-mod-border-accent-bottom-sharp {
    border-block-end: 3px solid var(--cbui-card-accent, currentColor);
    border-end-start-radius: 0;
    border-end-end-radius: 0;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  +border-dashed modifier — dashed border for placeholder cards     */
  /*                                                                      */
  /*  Canonical use cases: "Add card" placeholder at the end of a Kanban */
  /*  column, drop-zone for file uploads, "Empty section" affordance.    */
  /*  Combines naturally with tone (e.g. outlined+border-dashed,muted)   */
  /*  and with the bg-fill / accent-bar modifiers if needed.              */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-card.cbui-card-mod-border-dashed {
    border-style: dashed;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  INTERACTIVE state — clickable / focusable card                      */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-card-interactive {
    cursor: pointer;
    /* Reset native button/anchor chrome. */
    appearance: none;
    width: 100%;
    text-align: inherit;
    font: inherit;
  }
  .cbui-card-interactive:hover {
    background-image: linear-gradient(var(--cbui-shade-soft), var(--cbui-shade-soft));
  }
  .cbui-card-interactive:active {
    background-image: linear-gradient(var(--cbui-shade-strong), var(--cbui-shade-strong));
  }
  /* Compose with bg-fill: the tint sits under the hover overlay so both
     read. linear-gradient stacks left-to-right (top of stack first). */
  .cbui-card-interactive.cbui-card-mod-bg-fill:hover {
    background-image:
      linear-gradient(var(--cbui-shade-soft), var(--cbui-shade-soft)),
      linear-gradient(var(--_cbui-card-fill, transparent), var(--_cbui-card-fill, transparent));
  }
  /* Focus ring with gap — same two-layer pattern as Button. The inner
     shadow paints the gap in the page background colour, the outer is the
     ring. The ring is composed with the elevated shadow when present. */
  .cbui-card-interactive:focus-visible {
    outline: none;
    box-shadow:
      0 0 0 var(--focus-ring-offset, 2px) var(--color-bg-base),
      0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring, var(--color-primary));
  }
  /* Elevated cards keep their lift on focus by stacking shadows. */
  .cbui-card-elevated.cbui-card-interactive:focus-visible {
    box-shadow:
      0 0 0 var(--focus-ring-offset, 2px) var(--color-bg-base),
      0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring, var(--color-primary)),
      var(--shadow-md);
  }

  .cbui-card-disabled {
    opacity: 0.55;
    pointer-events: none;
    cursor: not-allowed;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  ACCESSIBILITY — high-contrast / reduced-motion                     */
  /* ─────────────────────────────────────────────────────────────────── */

  .a11y-reduced-motion .cbui-card {
    transition: none;
  }

  .a11y-high-contrast .cbui-card-outlined,
  .a11y-high-contrast .cbui-card-elevated {
    border-width: 2px;
  }
  /* Hover treatment for HC: harder shade overlay, bumped border. */
  .a11y-high-contrast .cbui-card-interactive:hover {
    background-image: linear-gradient(var(--cbui-shade-strong), var(--cbui-shade-strong));
  }
  /* Solid surfaces in HC — no translucent fill modifier ambiguity. */
  .a11y-high-contrast .cbui-card-mod-bg-fill {
    background-image: none;
  }
  .a11y-high-contrast .cbui-card-mod-bg-fill.cbui-card-tone-neutral { background-color: var(--color-bg-subtle); }
  .a11y-high-contrast .cbui-card-mod-bg-fill.cbui-card-tone-primary { background-color: var(--color-bg-base); border-color: var(--color-primary); }
  .a11y-high-contrast .cbui-card-mod-bg-fill.cbui-card-tone-success { background-color: var(--color-bg-base); border-color: var(--color-success); }
  .a11y-high-contrast .cbui-card-mod-bg-fill.cbui-card-tone-warning { background-color: var(--color-bg-base); border-color: var(--color-warning); }
  .a11y-high-contrast .cbui-card-mod-bg-fill.cbui-card-tone-danger  { background-color: var(--color-bg-base); border-color: var(--color-danger); }
  .a11y-high-contrast .cbui-card-mod-bg-fill.cbui-card-tone-info    { background-color: var(--color-bg-base); border-color: var(--color-info); }

  /* ── CB-3703: collapsible header, sticky header, surface fill ─────── */
  .cbui-card.cbui-card-mod-surface { background-color: var(--color-bg-base); }

  .cbui-card-header-toggle {
    display: flex;
    align-items: center;
    gap: var(--spacing-xs);
    cursor: pointer;
    user-select: none;
  }
  .cbui-card-header-toggle:hover { background-color: color-mix(in srgb, currentColor 5%, transparent); }
  .cbui-card-header-toggle:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: -2px; }
  .cbui-card-header-main { flex: 1 1 auto; min-width: 0; }
  .cbui-card-caret {
    flex: none;
    display: inline-flex;
    color: var(--color-text-secondary);
    transition: transform 150ms ease;
  }
  .cbui-card.is-collapsed .cbui-card-caret { transform: rotate(-90deg); }
  .cbui-card.is-collapsed.cbui-card-divider-header .cbui-card-header { border-block-end-color: transparent; }
  .a11y-reduced-motion .cbui-card-caret { transition: none; }

  /* overflow:clip is no scroll container; the 1px clip margin lets the sticky header paint over the card edge.
     Safari ignores overflow-clip-margin: there the stuck header loses its 1px outer edge (accepted). */
  .cbui-card-sticky-header { overflow: clip; overflow-clip-margin: 1px; }
  .cbui-card-sticky-header > .cbui-card-header {
    position: sticky;
    top: var(--cbui-card-sticky-top, 0);
    z-index: 1;
    margin: -1px -1px 0;
    border-block-start: 1px solid;
    border-inline: 1px solid;
    border-block-start-color: inherit;
    border-inline-color: inherit;
    border-start-start-radius: var(--card-radius);
    border-start-end-radius: var(--card-radius);
  }
  /* The header takes the card fill, so nothing shows through it when stuck. Outlined and elevated cards paint a
     translucent header band (0,3,0 in dark): the compound selector wins over it; header-fill keeps its own fill. */
  .cbui-card.cbui-card-sticky-header:not(.cbui-card-mod-header-fill) > .cbui-card-header { background-color: inherit; }
  /* Corner mask in the colour of the scroll parent (--cbui-card-sticky-backdrop): when the header is
     stuck, the square card side borders and fill must not show outside the curve. Unstuck, the card clip hides it. */
  .cbui-card-sticky-header > .cbui-card-header::before {
    content: '';
    position: absolute;
    inset-block-start: -1px;
    inset-inline: -1px;
    block-size: var(--card-radius);
    pointer-events: none;
    background:
      radial-gradient(circle at 100% 100%, transparent calc(var(--card-radius) - 0.5px), var(--cbui-card-sticky-backdrop, transparent) var(--card-radius)) left top / var(--card-radius) var(--card-radius) no-repeat,
      radial-gradient(circle at 0 100%, transparent calc(var(--card-radius) - 0.5px), var(--cbui-card-sticky-backdrop, transparent) var(--card-radius)) right top / var(--card-radius) var(--card-radius) no-repeat;
  }
  .cbui-card-sticky-header > .cbui-card-header-toggle:hover {
    background-image: linear-gradient(color-mix(in srgb, currentColor 5%, transparent), color-mix(in srgb, currentColor 5%, transparent));
  }
}

/* cbui/components/icon-tile */
/* ============================================================================
   IconTile — an icon on a coloured plate (identity colour of a Bringer etc.).

   The tone arrives as two local properties set inline by the template
   (--cbui-icon-tile-tone, --cbui-icon-tile-on), so 16 categorical hues need no
   rule each. Neutral sets neither and falls back to the grey defaults below.
   Radius follows the flavor through --button-radius.
   ============================================================================ */

.cbui-icon-tile {
  --cbui-icon-tile-tone: var(--color-text-secondary);
  --cbui-icon-tile-on: var(--color-bg-base);
  --cbui-icon-tile-size: 2.5rem;
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  inline-size: var(--cbui-icon-tile-size);
  block-size: var(--cbui-icon-tile-size);
  border-radius: var(--button-radius);
  font-size: calc(var(--cbui-icon-tile-size) * 0.5);
  line-height: 1;
}
.cbui-icon-tile svg {
  inline-size: 1em;
  block-size: 1em;
}

.cbui-icon-tile-size-sm { --cbui-icon-tile-size: 2rem; }
.cbui-icon-tile-size-md { --cbui-icon-tile-size: 2.5rem; }
.cbui-icon-tile-size-lg { --cbui-icon-tile-size: 3rem; }
.cbui-icon-tile-size-xl { --cbui-icon-tile-size: 3.5rem; }

.cbui-icon-tile-soft {
  background-color: color-mix(in srgb, var(--cbui-icon-tile-tone) 14%, transparent);
  color: var(--cbui-icon-tile-tone);
}
.cbui-icon-tile-solid {
  background-color: var(--cbui-icon-tile-tone);
  color: var(--cbui-icon-tile-on);
}

/* Dark: a deeper tint and a lighter glyph, same as the Badge's bg-fill tones. */
.dark .cbui-icon-tile-soft,
[data-scheme="dark"] .cbui-icon-tile-soft {
  background-color: color-mix(in srgb, var(--cbui-icon-tile-tone) 22%, transparent);
  color: color-mix(in srgb, var(--cbui-icon-tile-tone) 80%, white);
}

/* cbui/components/schematic */
/* ============================================================================
   Schematic — a neutral drawing of a window or of the workspace screen, the
   preview of a settings choice (see Schematic.html.twig).

   Everything is sized in container query units (cqw = 1% of the drawing's
   width), so the drawing scales with the card it sits in and keeps 16:10.
   Colours are the page tokens; with `scheme` the template puts the colour
   tokens of that scheme on the root (or, for auto, on each layer), which is
   why the local --cbui-schematic-* colours are declared on the LAYER: they
   must resolve below the element that carries the scheme tokens.

   The part a setting is about is drawn in the primary colour (emphasis-*).
   Radii follow the flavor (--radius-*); the three window buttons are round
   because the real ones are.
   ============================================================================ */

@layer components {
  .cbui-schematic {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 16 / 10;
    container-type: inline-size;
    overflow: hidden;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    pointer-events: none;
    user-select: none;
  }

  .cbui-schematic-layer {
    --cbui-schematic-canvas: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-schematic-surface: var(--color-bg-base);
    --cbui-schematic-chrome: color-mix(in oklab, var(--cbui-schematic-canvas) 88%, var(--color-text) 12%);
    --cbui-schematic-line: color-mix(in oklab, var(--color-text) 16%, transparent);
    --cbui-schematic-line-strong: color-mix(in oklab, var(--color-text) 30%, transparent);
    --cbui-schematic-border: var(--color-border);
    --cbui-schematic-accent: var(--color-primary);
    --cbui-schematic-accent-soft: color-mix(in oklab, var(--color-primary) 30%, transparent);

    position: absolute;
    inset: 0;
    background: var(--cbui-schematic-canvas);
  }

  /* auto: the dark drawing covers the lower right half. */
  .cbui-schematic-layer-second {
    clip-path: polygon(100% 0, 100% 100%, 0 100%);
  }
  .cbui-schematic-split::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to top right, transparent calc(50% - 0.5px), var(--color-border) 50%, transparent calc(50% + 0.5px));
  }

  .cbui-schematic i,
  .cbui-schematic b {
    display: block;
    font-style: normal;
  }

  /* ── Shared: window bar, the three buttons, content lines ── */
  .cbui-schematic-bar {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    background: color-mix(in oklab, var(--cbui-schematic-surface) 92%, var(--color-text) 8%);
    border-block-end: 1px solid var(--cbui-schematic-border);
  }

  .cbui-schematic-lights {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
  }
  .cbui-schematic-lights i {
    border-radius: 50%;
    background: var(--cbui-schematic-line-strong);
  }

  .cbui-schematic-content {
    display: flex;
    flex-direction: column;
  }
  .cbui-schematic-content i {
    background: var(--cbui-schematic-line);
    border-radius: var(--radius-sm);
  }

  /* ════ kind=window ════ */
  .cbui-schematic-window .cbui-schematic-layer {
    display: flex;
    padding: 6cqw 7cqw;
  }
  .cbui-schematic-window .cbui-schematic-frame {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    background: var(--cbui-schematic-surface);
    border: 1px solid var(--cbui-schematic-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
  }
  .cbui-schematic-window .cbui-schematic-bar {
    gap: 3.5cqw;
    height: 13cqw;
    padding-inline: 4cqw;
  }
  .cbui-schematic-window .cbui-schematic-lights { gap: 2.2cqw; }
  .cbui-schematic-window .cbui-schematic-lights i { width: 4cqw; height: 4cqw; }
  .cbui-schematic-window[data-controls='standard'] .cbui-schematic-lights i { width: 6.4cqw; height: 6.4cqw; }
  .cbui-schematic-window .cbui-schematic-lights i:nth-child(1) { background: var(--color-danger); }
  .cbui-schematic-window .cbui-schematic-lights i:nth-child(2) { background: var(--color-warning); }
  .cbui-schematic-window .cbui-schematic-lights i:nth-child(3) { background: var(--color-success); }
  .cbui-schematic-window .cbui-schematic-title {
    width: 30%;
    height: 3.4cqw;
    border-radius: var(--radius-sm);
    background: var(--cbui-schematic-line-strong);
  }

  /* mirrored: title flush left, the buttons on the right with red outermost. */
  .cbui-schematic-window[data-header-layout='mirrored'] .cbui-schematic-bar { flex-direction: row-reverse; justify-content: space-between; }
  .cbui-schematic-window[data-header-layout='mirrored'] .cbui-schematic-lights { flex-direction: row-reverse; }

  .cbui-schematic-window .cbui-schematic-body {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
  }
  .cbui-schematic-window .cbui-schematic-side {
    display: flex;
    flex-direction: column;
    gap: 2.4cqw;
    width: 26%;
    padding: 4cqw 3cqw;
    background: color-mix(in oklab, var(--cbui-schematic-surface) 95%, var(--color-text) 5%);
    border-inline-end: 1px solid var(--cbui-schematic-border);
  }
  .cbui-schematic-window .cbui-schematic-side i {
    height: 3cqw;
    border-radius: var(--radius-sm);
    background: var(--cbui-schematic-line);
  }
  .cbui-schematic-window .cbui-schematic-content {
    flex: 1 1 auto;
    gap: 2.6cqw;
    padding: 4.5cqw 5cqw;
  }
  .cbui-schematic-window .cbui-schematic-content i { height: 3cqw; }
  .cbui-schematic-window .cbui-schematic-content i:nth-child(1) { width: 70%; background: var(--cbui-schematic-line-strong); }
  .cbui-schematic-window .cbui-schematic-content i:nth-child(2) { width: 90%; }
  .cbui-schematic-window .cbui-schematic-content i:nth-child(3) { width: 60%; }
  .cbui-schematic-window .cbui-schematic-content b {
    width: 30%;
    height: 7cqw;
    margin-block-start: auto;
    border-radius: var(--radius-sm);
    background: var(--cbui-schematic-accent);
  }

  .cbui-schematic-window.cbui-schematic-emphasis-controls .cbui-schematic-lights,
  .cbui-schematic-window.cbui-schematic-emphasis-title .cbui-schematic-bar {
    outline: 0.8cqw solid var(--cbui-schematic-accent-soft);
    outline-offset: 1.2cqw;
    border-radius: var(--radius-sm);
  }
  .cbui-schematic-window.cbui-schematic-emphasis-title .cbui-schematic-bar { outline-offset: -1.4cqw; }

  /* ════ kind=screen ════ */
  .cbui-schematic-screen .cbui-schematic-layer {
    display: grid;
    grid-template-columns: 19% minmax(0, 1fr);
    grid-template-rows: 11% minmax(0, 1fr);
    grid-template-areas: 'top top' 'dock work';
  }
  .cbui-schematic-screen[data-dock-side='right'] .cbui-schematic-layer {
    grid-template-columns: minmax(0, 1fr) 19%;
    grid-template-areas: 'top top' 'work dock';
  }

  .cbui-schematic-topbar {
    grid-area: top;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding-inline: 3cqw;
  }
  .cbui-schematic-topbar i {
    width: 16%;
    height: 3cqw;
    border-radius: var(--radius-sm);
    background: var(--cbui-schematic-line);
  }

  .cbui-schematic-dock {
    grid-area: dock;
    display: flex;
    flex-direction: column;
    gap: 2cqw;
    padding: 2.5cqw 2.2cqw;
    min-width: 0;
  }
  /* Dock widgets: tall tiles, about three times a line, so they read as widgets and not as list rows (Matthias
     2026-10-28). Three of them still fit the shortest dock (floating panel). */
  .cbui-schematic-dock i {
    height: 11cqw;
    border-radius: var(--radius-sm);
    background: var(--cbui-schematic-line);
  }

  .cbui-schematic-work {
    grid-area: work;
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    padding: 2.4cqw;
    gap: 1.6cqw;
    background: var(--cbui-schematic-canvas);
  }
  .cbui-schematic-stage {
    display: flex;
    gap: 1.6cqw;
    flex: 1 1 auto;
    min-height: 0;
  }

  /* unified-chrome (default): topbar and dock are one L-shaped chrome surface, the workspace an inset with a
     rounded inner corner where the two bands meet. */
  .cbui-schematic-screen[data-dock-layout='unified-chrome'] .cbui-schematic-layer { background: var(--cbui-schematic-chrome); }
  .cbui-schematic-screen[data-dock-layout='unified-chrome'] .cbui-schematic-work { border-start-start-radius: var(--radius-md); }
  .cbui-schematic-screen[data-dock-layout='unified-chrome'][data-dock-side='right'] .cbui-schematic-work {
    border-start-start-radius: 0;
    border-start-end-radius: var(--radius-md);
  }

  /* full-height: the dock spans the whole height, the topbar only runs over the workspace. */
  .cbui-schematic-screen[data-dock-layout='full-height'] .cbui-schematic-layer { grid-template-areas: 'dock top' 'dock work'; }
  .cbui-schematic-screen[data-dock-layout='full-height'][data-dock-side='right'] .cbui-schematic-layer { grid-template-areas: 'top dock' 'work dock'; }
  .cbui-schematic-screen[data-dock-layout='full-height'] .cbui-schematic-dock { background: var(--cbui-schematic-chrome); }
  .cbui-schematic-screen[data-dock-layout='full-height'] .cbui-schematic-topbar { border-block-end: 1px solid var(--cbui-schematic-border); }

  /* floating-panel: the dock is an elevated panel floating one gap off the canvas. */
  .cbui-schematic-screen[data-dock-layout='floating-panel'] .cbui-schematic-topbar { border-block-end: 1px solid var(--cbui-schematic-border); }
  .cbui-schematic-screen[data-dock-layout='floating-panel'] .cbui-schematic-dock {
    margin: 2.4cqw;
    margin-inline-end: 0;
    background: var(--cbui-schematic-surface);
    border: 1px solid var(--cbui-schematic-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
  }
  .cbui-schematic-screen[data-dock-layout='floating-panel'][data-dock-side='right'] .cbui-schematic-dock {
    margin-inline-start: 0;
    margin-inline-end: 2.4cqw;
  }

  /* Windows: side by side, together filling the whole workspace. The WorkspaceShell never floats windows over each
     other (Matthias 2026-10-28, a standing UX decision), so neither does its drawing. The last one is the active one. */
  .cbui-schematic-win {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--cbui-schematic-surface);
    border: 1px solid var(--cbui-schematic-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
  }

  .cbui-schematic-win .cbui-schematic-bar {
    height: 5cqw;
    padding-inline: 1.6cqw;
  }
  .cbui-schematic-win .cbui-schematic-lights { gap: 0.9cqw; }
  .cbui-schematic-win .cbui-schematic-lights i { width: 1.6cqw; height: 1.6cqw; }
  .cbui-schematic-win-active .cbui-schematic-lights i:nth-child(1) { background: var(--color-danger); }
  .cbui-schematic-win-active .cbui-schematic-lights i:nth-child(2) { background: var(--color-warning); }
  .cbui-schematic-win-active .cbui-schematic-lights i:nth-child(3) { background: var(--color-success); }
  .cbui-schematic-win .cbui-schematic-content {
    gap: 1.6cqw;
    padding: 2.2cqw;
  }
  .cbui-schematic-win .cbui-schematic-content i { height: 2cqw; }
  .cbui-schematic-win .cbui-schematic-content i:nth-child(1) { width: 70%; }
  .cbui-schematic-win .cbui-schematic-content i:nth-child(2) { width: 50%; }

  /* Active window: accent ring and a tinted bar (activeHighlight); the others dimmed (recede). */
  .cbui-schematic-screen[data-active-highlight='on'] .cbui-schematic-win-active {
    border-color: var(--cbui-schematic-accent);
    box-shadow: 0 0 0 0.8cqw var(--cbui-schematic-accent-soft), var(--shadow-md);
  }
  .cbui-schematic-screen[data-active-highlight='on'] .cbui-schematic-win-active .cbui-schematic-bar {
    background: color-mix(in oklab, var(--cbui-schematic-accent) 22%, var(--cbui-schematic-surface));
  }
  .cbui-schematic-screen[data-recede='on'] .cbui-schematic-win:not(.cbui-schematic-win-active) {
    opacity: 0.5;
    filter: saturate(0.4);
  }

  /* Taskbar with the chips of minimised windows. */
  .cbui-schematic-taskbar {
    display: flex;
    gap: 1.6cqw;
    flex: 0 0 auto;
    height: 4.4cqw;
  }
  .cbui-schematic-taskbar i {
    width: 14%;
    border-radius: var(--radius-sm);
    background: var(--cbui-schematic-line-strong);
  }

  /* ── Emphasis: the part the setting is about, in the primary colour ── */
  /* The dock widgets and the sidebar entries of a window stay grey in every drawing (Matthias 2026-10-28): colour marks
     only what a setting changes, and the widgets are never that. */
  .cbui-schematic-screen.cbui-schematic-emphasis-dock[data-dock-layout='full-height'] .cbui-schematic-dock,
  .cbui-schematic-screen.cbui-schematic-emphasis-dock[data-dock-layout='floating-panel'] .cbui-schematic-dock {
    box-shadow: inset 0 0 0 0.6cqw var(--cbui-schematic-accent-soft);
  }
  .cbui-schematic-screen.cbui-schematic-emphasis-dock[data-dock-layout='floating-panel'] .cbui-schematic-dock {
    box-shadow: inset 0 0 0 0.6cqw var(--cbui-schematic-accent-soft), var(--shadow-md);
  }
  .cbui-schematic-emphasis-windows .cbui-schematic-win { border-color: var(--cbui-schematic-accent); }
  .cbui-schematic-emphasis-active .cbui-schematic-win-active { border-color: var(--cbui-schematic-accent); }
  .cbui-schematic-emphasis-taskbar .cbui-schematic-taskbar i { background: var(--cbui-schematic-accent); }

  /* ── kind="grid": the cells of a screen with its windows where they lie ───────────────────────────────────────
     Real text at the normal size (not cqw-scaled like the other kinds), so a title stays readable and is cut with an
     ellipsis when the cell is small. */
  .cbui-schematic-grid .cbui-schematic-layer {
    padding: var(--spacing-xs);
  }
  .cbui-schematic-board {
    display: grid;
    grid-template-columns: repeat(var(--cbui-schematic-cols, 4), minmax(0, 1fr));
    grid-template-rows: repeat(var(--cbui-schematic-rows, 3), minmax(0, 1fr));
    gap: var(--spacing-xs);
    width: 100%;
    height: 100%;
  }
  .cbui-schematic-cell {
    display: flex;
    min-width: 0;
    min-height: 0;
    padding: var(--spacing-xs) var(--spacing-sm);
    border: 1px solid var(--cbui-schematic-border);
    border-radius: var(--radius-sm);
    background: var(--cbui-schematic-surface);
    color: var(--color-text);
    font-size: var(--font-size-md);
    overflow: hidden;
  }
  .cbui-schematic-cell-title {
    display: flex;
    align-items: flex-start;
    gap: var(--spacing-xs);
    min-width: 0;
    max-width: 100%;
  }
  .cbui-schematic-cell-title > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cbui-schematic-cell-title > svg {
    flex: none;
    margin-top: 0.15em;
  }
}

/* cbui/components/flavor-sample */
/* ============================================================================
   FlavorSample — a card with an input and a button, drawn with the tokens of
   one flavor (FlavorSample.html.twig puts them on the element as custom
   properties). Every geometry value below is a flavor token, so the sample
   shows that flavor's real radius, border width, shadow and padding; colours
   come from the page (theme + scheme). Type stays at the body size.
   ============================================================================ */

@layer components {
  .cbui-flavor-sample {
    display: block;
    width: 100%;
    pointer-events: none;
    user-select: none;
  }

  .cbui-flavor-sample-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--spacing-2);
    padding: var(--card-padding-xs);
    background: var(--color-bg-base);
    border: var(--border-default) solid var(--color-border);
    border-radius: var(--card-radius);
    box-shadow: var(--shadow-md);
  }

  .cbui-flavor-sample-input {
    display: block;
    box-sizing: border-box;
    width: 100%;
    padding: var(--input-padding-y) var(--input-padding-x);
    overflow: hidden;
    font-size: var(--font-size-md);
    line-height: 1.4;
    color: var(--color-text-muted);
    white-space: nowrap;
    text-overflow: ellipsis;
    background: var(--color-bg-base);
    border: var(--border-default) solid var(--color-border-strong);
    border-radius: var(--input-radius);
  }

  .cbui-flavor-sample-button {
    display: inline-block;
    max-width: 100%;
    padding: var(--button-padding-y) var(--button-padding-x);
    overflow: hidden;
    font-size: var(--button-font-size);
    font-weight: var(--button-font-weight);
    line-height: 1.4;
    color: var(--color-text-on-primary);
    white-space: nowrap;
    text-overflow: ellipsis;
    background: var(--color-primary);
    border-radius: var(--button-radius);
    box-shadow: var(--shadow-sm);
  }
}

/* cbui/components/choice-card-preview */
/* ============================================================================
   Choice card preview — the `preview` block of RichRadioGroup and
   RichCheckboxGroup: a picture of what the option does, on top of the card,
   full width, with indicator and label below it (the appearance tile of macOS
   System Settings or Windows Personalization).

   Lives apart from choice-group.css on purpose: it only adds the preview row
   and the auto-fit grid, the card itself stays as choice-group.css draws it.
   ============================================================================ */

@layer components {
  .cbui-choice-card.cbui-choice-card-with-preview {
    flex-wrap: wrap;
    align-items: center;
    row-gap: 0.625rem;
  }

  /* Indicator and label share the row under the picture; the body must not claim its full text width first, or it
     wraps below the indicator. */
  .cbui-choice-card-with-preview > .cbui-choice-card-body {
    flex: 1 1 0;
    min-width: 0;
  }
  .cbui-choice-card-with-preview > .cbui-choice-card-indicator {
    align-self: flex-start;
  }

  /* After indicator and body in the DOM (the native input stays first), drawn first. */
  .cbui-choice-card-preview {
    order: -1;
    flex: 1 0 100%;
    display: block;
    min-width: 0;
    pointer-events: none;
  }

  /* columns="auto": as many columns as fit, each at least --cbui-choice-group-min wide, sharing the width. A wide
     window must not stretch a card (and with it the preview picture) past --cbui-choice-group-max, so the group
     itself is capped at "option count x maximum" and centred: a wide window ends in a centred row of capped cards
     instead of giant ones. (A grid track `minmax(min, max)` would not do: auto-fit counts columns by the maximum and
     wraps the third card.) One column on a phone. The maximum is about twice the single theme card (16rem) of the
     appearance page. */
  .cbui-choice-group.cbui-choice-group-grid.cbui-choice-group-cols-auto {
    --cbui-choice-group-gap-auto: 0.75rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--cbui-choice-group-min, 9rem)), 1fr));
    inline-size: 100%;
    max-inline-size: calc(
      var(--cbui-choice-group-count, 1) * var(--cbui-choice-group-max, 28rem)
      + (var(--cbui-choice-group-count, 1) - 1) * var(--cbui-choice-group-gap-auto)
    );
    margin-inline: auto;
    gap: var(--cbui-choice-group-gap-auto);
  }
  .cbui-choice-group.cbui-choice-group-grid.cbui-choice-group-cols-auto:has(> :only-child) {
    grid-template-columns: minmax(0, 1fr);
    max-inline-size: var(--cbui-choice-group-single, 16rem);
  }
}

/* cbui/components/checkbox */
/* cbui-checkbox — native checkbox with CBUI chrome.
 *
 * Layout: a <label> wraps a visually-hidden native input, a custom "box"
 * span, and an optional inline label. State (checked / disabled / focus
 * / indeterminate) is read from the native input's pseudo-classes via
 * sibling-selectors. Zero JavaScript.
 *
 * Variant×Tone+Modifier grammar shared with Button/Badge/Alert/Input.
 *
 *   - cbui-checkbox             base reset
 *   - cbui-checkbox-{variant}   solid | outline | overlay
 *   - cbui-checkbox-size-{sm,md,lg}
 *   - cbui-checkbox-tone-{primary|success|warning|danger|info}
 *   - cbui-checkbox-mod-block   label fills the row width
 *
 * The native input is sr-only but stays focusable; focus + hover
 * indicators paint on `.cbui-checkbox-box`.
 */

@layer components {
  /* ──────────────────────────────────────────────────────────────────────
   * Wrapper
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox {
    /* Box size cascade. Box geometry stays a constant across flavors; the
       size tier scales both the box and the label uniformly. */
    --cbui-checkbox-box:        1.125rem;   /* md */
    --cbui-checkbox-fs:         var(--font-size-md, 1rem);
    --cbui-checkbox-radius:     0.25rem;

    /* Tone cascade — overridden by tone classes. Defaults read like
       "untoned" so an unchecked, untoned checkbox stays neutral and only
       picks up tone when checked. */
    --cbui-checkbox-tone:        var(--color-primary);
    --cbui-checkbox-tone-strong: var(--color-primary);
    --cbui-checkbox-tone-focus:  var(--color-primary);
    --cbui-checkbox-tone-ring:   var(--color-focus-ring, var(--color-primary));
    --cbui-checkbox-tone-on:     var(--color-text-on-primary, white);

    /* Focus ring geometry — matches Input's gapped two-layer ring. */
    --cbui-checkbox-ring-gap:    2px;
    --cbui-checkbox-ring-width:  2px;
    --cbui-checkbox-ring-gap-color: var(--color-bg-base);

    /* `position: relative` confines the visually-hidden native input to
       the label box, so the browser's "scroll focused element into view"
       lands on the visible label rather than on a 1px box at some
       absolutely-positioned offset (which was causing the page to jump
       on every click). */
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-family: inherit;
    font-size: var(--cbui-checkbox-fs);
    line-height: 1.4;
    color: var(--color-text);
    cursor: pointer;
  }

  /* Native input overlays the entire label — invisible (opacity 0) but
     focusable, clickable, and tracked by the form. Sibling-selectors on
     `:checked` / `:focus-visible` still paint the visible box. The
     overlay technique avoids the sr-only-margin-trick scroll bug. */
  .cbui-checkbox-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    opacity: 0;
    cursor: inherit;
    appearance: none;
    -webkit-appearance: none;
  }

  /* The painted box. Both glyphs (check + dash) live inside and are
     toggled by sibling-input state.

     `pointer-events: none` — this box (and its two glyph SVGs) sits AFTER
     `.cbui-checkbox-input` in the DOM, so with default paint order it is
     what a raw hit-test finds at the label's center, not the input beneath
     it (found via CB-751 review round, 07b467b21: an icon-only checkbox —
     no visible text label, so the `<label>`'s box shrinks to the glyph's own
     footprint — hung Playwright's `.click()`/`.hover()` on the INPUT
     locator every time, "cbui-checkbox-box subtree intercepts pointer
     events", timing out rather than clicking). A real mouse click still
     worked regardless (native `<label>` click-forwarding does not care
     which descendant was hit), which is exactly why this sat latent: it
     only surfaces for automation driving the `<input>` directly, or a
     checkbox tight enough that a real click could land off the input's own
     box in a browser that does NOT forward via `<label>`. `pointer-events:
     none` does not touch paint — only who receives the click — so the
     visual is identical; hover still resolves through the label ancestor,
     since browsers hover-match by DOM ancestry rather than the ignored
     paint layer. */
  .cbui-checkbox-box {
    flex: 0 0 auto;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--cbui-checkbox-box);
    height: var(--cbui-checkbox-box);
    border-radius: var(--cbui-checkbox-radius);
    background-color: var(--color-bg-base);
    border: 1.5px solid var(--color-border-strong);
    color: var(--cbui-checkbox-tone-on);
    pointer-events: none;
    transition:
      background-color 120ms ease,
      border-color 120ms ease,
      box-shadow 120ms ease,
      transform 80ms ease;
  }

  .cbui-checkbox-glyph {
    position: absolute;
    inset: 10%;
    width: 80%;
    height: 80%;
    opacity: 0;
    transform: scale(0.6);
    transition: opacity 80ms ease, transform 80ms ease;
  }

  .cbui-checkbox-glyph-dash { stroke-width: 3; }

  .cbui-checkbox-label {
    flex: 1 1 auto;
    color: var(--color-text);
  }

  /* CB-4111 - an optional description under the label, shaped like the Switch's: a column, secondary colour, never below
     the normal size. The box sits on the label line, not centred between the two lines. */
  .cbui-checkbox-text {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
  }
  .cbui-checkbox-description {
    color: var(--color-text-secondary);
    font-size: var(--font-size-md, 1rem);
  }
  .cbui-checkbox:has(.cbui-checkbox-description) {
    align-items: flex-start;
  }
  .cbui-checkbox:has(.cbui-checkbox-description) .cbui-checkbox-box {
    margin-top: calc((var(--cbui-checkbox-fs) * 1.4 - var(--cbui-checkbox-box)) / 2);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Sizes
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-size-sm {
    --cbui-checkbox-box: 0.9375rem;
    --cbui-checkbox-fs:  var(--font-size-md, 1rem);
  }
  .cbui-checkbox-size-md {
    --cbui-checkbox-box: 1.125rem;
    --cbui-checkbox-fs:  var(--font-size-md, 1rem);
  }
  .cbui-checkbox-size-lg {
    --cbui-checkbox-box: 1.375rem;
    --cbui-checkbox-fs:  var(--font-size-md, 1rem);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Hover (only when the control is interactive)
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox:hover .cbui-checkbox-input:not(:disabled) ~ .cbui-checkbox-box {
    border-color: var(--cbui-checkbox-tone);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Focus (canonical gapped two-layer ring on the box)
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-input:focus-visible ~ .cbui-checkbox-box,
  .cbui-checkbox[data-cbui-demo-state="focused"] .cbui-checkbox-box {
    outline: none;
    box-shadow:
      0 0 0 var(--cbui-checkbox-ring-gap) var(--cbui-checkbox-ring-gap-color),
      0 0 0 calc(var(--cbui-checkbox-ring-gap) + var(--cbui-checkbox-ring-width)) var(--cbui-checkbox-tone-ring);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: solid — checked = filled with tone, glyph in contrast-text.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-solid .cbui-checkbox-input:checked ~ .cbui-checkbox-box,
  .cbui-checkbox-solid .cbui-checkbox-input[data-indeterminate="true"] ~ .cbui-checkbox-box,
  .cbui-checkbox-solid[data-cbui-demo-state="checked"] .cbui-checkbox-box,
  .cbui-checkbox-solid[data-cbui-demo-state="indeterminate"] .cbui-checkbox-box {
    background-color: var(--cbui-checkbox-tone);
    border-color: var(--cbui-checkbox-tone);
    color: var(--cbui-checkbox-tone-on);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: outline — checked = tone border + tone glyph, no fill.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-outline .cbui-checkbox-input:checked ~ .cbui-checkbox-box,
  .cbui-checkbox-outline .cbui-checkbox-input[data-indeterminate="true"] ~ .cbui-checkbox-box,
  .cbui-checkbox-outline[data-cbui-demo-state="checked"] .cbui-checkbox-box,
  .cbui-checkbox-outline[data-cbui-demo-state="indeterminate"] .cbui-checkbox-box {
    background-color: transparent;
    border-color: var(--cbui-checkbox-tone);
    color: var(--cbui-checkbox-tone);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: overlay — a large round check that lies ON a picture (a tile preview).
   * The ring is 3px, white in light mode and black in dark mode (`light-dark()`, the scheme comes from `color-scheme`), a
   * neutral grey fill inside (opaque, so a blue picture never shines through and reads as "checked") and a hairline outside
   * keep it readable on a white or a black picture alike. Checked = filled with the tone
   * (primary), the ring stays. The visible circle is 1.75rem; the label around it is padded to a 2.75rem touch target.
   * It owns no position: the host places the checkbox (FilePreview puts it in a corner of the tile).
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-overlay {
    --cbui-checkbox-box: 1.75rem;
    --cbui-checkbox-ring-colour: light-dark(#fff, #000);
    padding: 0.5rem;
  }
  .cbui-checkbox-overlay .cbui-checkbox-box {
    border: 3px solid var(--cbui-checkbox-ring-colour);
    border-radius: 50%;
    background-color: var(--color-text-tertiary);
    box-shadow: 0 0 0 1px rgb(0 0 0 / 0.4);
  }
  :where([data-scheme='dark'], .dark) .cbui-checkbox-overlay .cbui-checkbox-box {
    box-shadow: 0 0 0 1px rgb(255 255 255 / 0.5);
  }
  .cbui-checkbox-overlay .cbui-checkbox-input:checked ~ .cbui-checkbox-box {
    background-color: var(--cbui-checkbox-tone);
    border-color: var(--cbui-checkbox-ring-colour);
  }
  .cbui-checkbox-overlay:hover .cbui-checkbox-input:not(:disabled) ~ .cbui-checkbox-box {
    border-color: var(--cbui-checkbox-ring-colour);
  }
  .cbui-checkbox-overlay .cbui-checkbox-input:focus-visible ~ .cbui-checkbox-box {
    box-shadow:
      0 0 0 1px rgb(0 0 0 / 0.4),
      0 0 0 4px var(--cbui-checkbox-tone-ring);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Glyph visibility — drives checked / indeterminate state.
   *
   * Checked → check glyph visible.
   * Indeterminate → dash glyph visible (overrides check via specificity
   * + later rule order; data-indeterminate attribute sits on the input
   * itself for static demos, and matches the JS-driven .indeterminate
   * property at runtime when the controller sets data-indeterminate).
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-input:checked ~ .cbui-checkbox-box .cbui-checkbox-glyph-check,
  .cbui-checkbox[data-cbui-demo-state="checked"] .cbui-checkbox-box .cbui-checkbox-glyph-check {
    opacity: 1;
    transform: scale(1);
  }

  .cbui-checkbox-input[data-indeterminate="true"] ~ .cbui-checkbox-box .cbui-checkbox-glyph-check,
  .cbui-checkbox[data-cbui-demo-state="indeterminate"] .cbui-checkbox-box .cbui-checkbox-glyph-check {
    opacity: 0;
    transform: scale(0.6);
  }

  .cbui-checkbox-input[data-indeterminate="true"] ~ .cbui-checkbox-box .cbui-checkbox-glyph-dash,
  .cbui-checkbox[data-cbui-demo-state="indeterminate"] .cbui-checkbox-box .cbui-checkbox-glyph-dash {
    opacity: 1;
    transform: scale(1);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Disabled
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-input:disabled ~ .cbui-checkbox-box,
  .cbui-checkbox-disabled .cbui-checkbox-box {
    opacity: 0.55;
  }

  .cbui-checkbox-input:disabled ~ .cbui-checkbox-label,
  .cbui-checkbox-input:disabled ~ .cbui-checkbox-text,
  .cbui-checkbox-disabled .cbui-checkbox-label,
  .cbui-checkbox-disabled .cbui-checkbox-text {
    opacity: 0.55;
  }

  .cbui-checkbox:has(.cbui-checkbox-input:disabled),
  .cbui-checkbox-disabled {
    cursor: not-allowed;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Modifier: block — label fills the row.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-mod-block {
    display: flex;
    width: 100%;
  }
  .cbui-checkbox-mod-block .cbui-checkbox-label {
    flex: 1 1 auto;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Tones
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-checkbox-tone-primary {
    --cbui-checkbox-tone:        var(--color-primary);
    --cbui-checkbox-tone-strong: var(--color-primary);
    --cbui-checkbox-tone-focus:  var(--color-primary);
    --cbui-checkbox-tone-ring:   var(--color-primary);
    --cbui-checkbox-tone-on:     var(--color-text-on-primary, white);
  }
  .cbui-checkbox-tone-success {
    --cbui-checkbox-tone:        var(--color-success);
    --cbui-checkbox-tone-strong: var(--color-success);
    --cbui-checkbox-tone-focus:  var(--color-success);
    --cbui-checkbox-tone-ring:   var(--color-success);
    --cbui-checkbox-tone-on:     var(--color-text-on-success, white);
  }
  .cbui-checkbox-tone-warning {
    --cbui-checkbox-tone:        var(--color-warning);
    --cbui-checkbox-tone-strong: var(--color-warning);
    --cbui-checkbox-tone-focus:  var(--color-warning);
    --cbui-checkbox-tone-ring:   var(--color-warning);
    --cbui-checkbox-tone-on:     var(--color-text-on-warning, white);
  }
  .cbui-checkbox-tone-danger {
    --cbui-checkbox-tone:        var(--color-danger);
    --cbui-checkbox-tone-strong: var(--color-danger);
    --cbui-checkbox-tone-focus:  var(--color-danger);
    --cbui-checkbox-tone-ring:   var(--color-danger);
    --cbui-checkbox-tone-on:     var(--color-text-on-danger, white);
  }
  .cbui-checkbox-tone-info {
    --cbui-checkbox-tone:        var(--color-info);
    --cbui-checkbox-tone-strong: var(--color-info);
    --cbui-checkbox-tone-focus:  var(--color-info);
    --cbui-checkbox-tone-ring:   var(--color-info);
    --cbui-checkbox-tone-on:     var(--color-text-on-info, white);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * High contrast (a11y)
   *
   * Bumps border weight and forces full-strength tones — same rule as
   * Input. The focus-ring widens to 3px for higher visibility.
   * ────────────────────────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-checkbox .cbui-checkbox-box {
    border-width: 2px;
    border-color: var(--color-text);
    --cbui-checkbox-ring-width: 3px;
  }

  .a11y-high-contrast .cbui-checkbox-solid .cbui-checkbox-input:checked ~ .cbui-checkbox-box,
  .a11y-high-contrast .cbui-checkbox-solid .cbui-checkbox-input[data-indeterminate="true"] ~ .cbui-checkbox-box {
    border-color: var(--cbui-checkbox-tone);
  }
}

/* cbui/components/combobox */
/* cbui-combobox — a server-backed suggestion field (CB-1967).
 * Chrome only: the input is the Input component, the rows are the shared
 * command-palette rows. Popup mode floats the listbox under the field,
 * persistent mode keeps it in the flow. Tokens only. */

@layer components {

  .cbui-combobox {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-width: 0;
  }

  .cbui-combobox-chips[hidden] { display: none; }
  .cbui-combobox-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.125rem;
  }

  .cbui-combobox-listbox {
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 0.5rem);
    max-height: 16rem;
    overflow: auto;
    padding: 0.25rem;
  }
  .cbui-combobox-listbox[hidden] { display: none; }

  /* Popup: a top-layer popover (an ancestor that clips cannot cut it off), placed against the
   * field by combobox_controller.js (left, width, top or bottom, max-height). */
  .cbui-combobox:not(.cbui-combobox-persistent) .cbui-combobox-listbox {
    position: fixed;
    inset: auto;
    margin: 0;
    box-sizing: border-box;
    color: inherit;
    box-shadow: var(--shadow-md, 0 4px 12px rgb(0 0 0 / 0.12));
  }
  /* Popup mode: the chips sit above the field, the popup anchors to the whole box. */

  .cbui-combobox-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4375rem 0.625rem;
    border-radius: var(--radius-md, 0.5rem);
    cursor: pointer;
    user-select: none;
  }
  .cbui-combobox-option.is-active { background-color: var(--color-bg-subtle); }
  .cbui-combobox-option-disabled { cursor: default; opacity: 0.5; }

  .cbui-combobox-option-icon {
    flex: none;
    display: inline-flex;
    font-size: 1rem;
    color: var(--color-text-secondary);
  }
  .cbui-combobox-option-icon iconify-icon { display: block; }

  .cbui-combobox-option-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }
  .cbui-combobox-option-label {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium, 500);
    color: var(--color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .cbui-combobox-option-desc {
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .cbui-combobox-note {
    padding: 0.75rem;
    text-align: center;
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
  }

  /* The live region is for assistive tech only; the note above is the visible copy. */
  .cbui-combobox-status {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* cbui/components/dropdown */
/* cbui-dropdown — contextual menu anchored to a trigger.
 *
 * Anatomy:
 *   .cbui-dropdown              wrapper (positioning context)
 *   .cbui-dropdown-trigger      user-provided trigger area (standalone mode)
 *   .cbui-dropdown-panel        floating panel
 *   .cbui-dropdown-label        non-interactive section header
 *   .cbui-dropdown-separator    horizontal rule divider
 *   .cbui-dropdown-item         interactive item (link or button)
 *   .cbui-dropdown-sub          submenu wrapper (item row + nested panel)
 *
 * Item anatomy:
 *   .cbui-dropdown-item-icon         leading icon slot
 *   .cbui-dropdown-item-icon-spacer  alignment spacer when no icon
 *   .cbui-dropdown-item-label        label text
 *   .cbui-dropdown-item-check        check mark for checkbox items
 *   .cbui-dropdown-item-icon-end     trailing icon
 *   .cbui-dropdown-item-shortcut     keyboard shortcut hint
 *   .cbui-dropdown-item-arrow        submenu caret
 *
 * Item tones:
 *   .cbui-dropdown-item-tone-danger
 *   .cbui-dropdown-item-tone-warning
 *
 * Panel sizes:
 *   .cbui-dropdown-panel-sm   (10rem min-width)
 *   .cbui-dropdown-panel-md   (14rem min-width, default)
 *   .cbui-dropdown-panel-lg   (18rem min-width)
 *
 * Panel alignment:
 *   .cbui-dropdown-panel-align-start  left: 0
 *   .cbui-dropdown-panel-align-end    right: 0
 *   .cbui-dropdown-panel-align-center centered
 *
 * Panel side:
 *   .cbui-dropdown-panel-side-bottom  opens below trigger
 *   .cbui-dropdown-panel-side-top     opens above trigger
 *
 * Submenu:
 *   .cbui-dropdown-panel-sub          side-flyout panel
 *   .cbui-dropdown-sub-fly-start      JS-applied: fly to inline-start instead of end
 *
 * Accessibility:
 *   .a11y-reduced-motion  — disables open/close animations
 */

@layer components {

  /* ── Local design tokens ─────────────────────────────────────────── */
  :where(.cbui-dropdown) {
    --cbui-dd-panel-bg:     var(--color-surface);
    --cbui-dd-panel-border: var(--color-border-strong);
    --cbui-dd-panel-radius: var(--dropdown-radius, var(--radius-md, 0.5rem));
    --cbui-dd-panel-shadow: 0 2px 8px rgb(0 0 0 / 0.08);

    --cbui-dd-item-color:         var(--color-text);
    --cbui-dd-item-color-muted:   var(--color-text-secondary);
    --cbui-dd-item-hover-bg:      var(--color-bg-subtle);
    --cbui-dd-item-active-bg:     var(--color-bg-muted);
    --cbui-dd-item-focus-ring:    var(--color-focus-ring, var(--color-primary));
    /* Check-mark glyph color for menuitemcheckbox items. Defaults to the item's
       text color so contrast on the panel background is high in both light and
       dark mode (primary doesn't always have enough contrast on a dark panel). */
    --cbui-dd-item-check-color:   var(--cbui-dd-item-color);

    --cbui-dd-item-px:      0.75rem;
    --cbui-dd-item-py:      0.5rem;
    --cbui-dd-item-gap:     0.5rem;
    --cbui-dd-item-icon-sz: 1rem;
    /* Item FONT default (size=md) is the base body size (16px) — menu items
       are meaningful content and never read below base by default; sub-base
       item type is reserved for the deliberately dense chrome scale
       (panel-sm). Same recipe as the ListGroup md default (list.css). */
    --cbui-dd-item-fs:      var(--font-size-base, 1rem);
    --cbui-dd-label-fs:     var(--font-size-md, 1rem);
    --cbui-dd-shortcut-fs:  var(--font-size-md, 1rem);

    --cbui-dd-min-w-sm: 10rem;
    --cbui-dd-min-w-md: 14rem;
    --cbui-dd-min-w-lg: 18rem;

    /* Width modifier sizes — used by `width="sm|md|lg|xl|full"`.
       Responsive: capped at viewport via min() at the modifier class. */
    --cbui-dd-panel-w-sm:     20rem;
    --cbui-dd-panel-w-md:     32rem;
    --cbui-dd-panel-w-lg:     48rem;
    --cbui-dd-panel-w-xl:     64rem;
    --cbui-dd-panel-w-margin: 1rem;  /* viewport edge margin for w-full and capping */

    /* Sits in the overlay band — above the window stack so a topbar
       Dropdown is always visible over any focused / docked window
       (HANDOVER-SHELL §6 + UiKernel z-index bands). */
    --cbui-dd-z: var(--z-dropdown, var(--cbui-z-overlay, 1900));
    --cbui-dd-item-radius: 0px;
    /* Inline padding of the panel (0 for full, 0.25rem for inset/aero).
       Header and footer negate this to reach the panel edge. */
    --cbui-dd-panel-px: 0px;
    /* Block (vertical) padding of the panel. Used by header/footer to absorb top/bottom inset. */
    --cbui-dd-panel-py: 0px;
    /* Padding for explicit DropdownColumn wrappers (per-column padding to inset items from col
       edges + vertical separators). 0 in full mode → items reach col/separator edges. 0.25rem in inset. */
    --cbui-dd-col-px: 0px;
    --cbui-dd-col-py: 0px;
    /* Separator visual inset from the panel border (applies to both the horizontal item separator
       and the vertical column separators). Independent of full/inset item layout — stays uniform. */
    --cbui-dd-sep-inset: 0.625rem;

    /* Header / footer background — subtle gray, same intent as a Card surface. */
    --cbui-dd-hf-bg: var(--color-bg-subtle);
    /* Separator between header/footer and the item list. In light mode this matches
       the panel border (looks like a continuation of the panel frame). Dark mode
       overrides this to a color DARKER than --cbui-dd-hf-bg so the line "sinks in"
       instead of standing out — a brighter line on a darker bg reads as a ridge,
       which fights the layered look we want here. */
    --cbui-dd-hf-border: var(--cbui-dd-panel-border);

    /* x-offset for align-center; included in keyframe so animation doesn't jump */
    --cbui-dd-panel-ox: 0;
  }

  /* ── Mega backdrop — transparent fixed overlay, closes mega on click ── */
  .cbui-dropdown-backdrop {
    position: fixed;
    inset: 0;
    z-index: calc(var(--cbui-dd-z, 200) - 1);
  }

  /* ── Wrapper ─────────────────────────────────────────────────────── */
  .cbui-dropdown {
    position: relative;
    display: inline-block;
  }

  .cbui-dropdown-block {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
  }

  /* Detached mode (CB-1907, contextTarget prop): the wrapper anchors an
     external element instead of its own trigger block — it takes no layout
     space of its own. Safe because the context-menu panel positions via the
     top-layer popover's fixed coords (CB-83), never via this wrapper's
     position:relative. */
  .cbui-dropdown-detached,
  .cbui-dropdown-manual {
    display: contents;
  }

  /* ── Panel ───────────────────────────────────────────────────────── */
  .cbui-dropdown-panel {
    position: absolute;
    z-index: var(--cbui-dd-z);
    min-width: var(--cbui-dd-min-w-md);
    background-color: var(--cbui-dd-panel-bg);
    border: 1px solid var(--cbui-dd-panel-border);
    border-radius: var(--cbui-dd-panel-radius);
    box-shadow: var(--cbui-dd-panel-shadow);
    padding-block: 0;
    /* Both axes come from the token, so the two inset variants below only have to
       move the token. Stating the inline axis at all is what matters: a top-layer
       panel is a `[popover]`, and the UA sheet gives every popover `padding:
       0.25em`. `padding-block: 0` alone left that 4px standing on the inline axis,
       so in FULL mode (Clean: panel-px = 0) the items stopped 4px short of the
       panel border while their edge corners were still cut for a border they no
       longer touched — visible as a wrong hover shape on the first and last row of
       every runtime dropdown, and nowhere else (an inline panel is not a popover). */
    padding-inline: var(--cbui-dd-panel-px, 0px);
    /* Visible by default (sub-flyouts escape the box; corner hover bleeds show).
       Also neutralizes the UA `[popover] { overflow: auto }` default so a
       top-layer panel paints identically to an inline one. Columns / clamped
       panels override this below. */
    overflow: visible;
    outline: none;

    /* Open animation — only plays when [hidden] is removed */
    animation: cbui-dropdown-open 130ms cubic-bezier(0.16, 1, 0.3, 1);

    &[hidden] { display: none; }
  }

  /* ── Top-layer (Popover API) positioning — CB-83 ─────────────────────
   * Runtime dropdowns render their panel in the top layer (popover="manual")
   * so it is immune to ancestor overflow clipping and stacking-context traps.
   * In the top layer the containing block is the viewport, so the CSS side /
   * align classes no longer apply — the controller sets fixed viewport coords
   * (left/top/right/bottom) inline. These rules just hand positioning over:
   * neutralize the UA centering (inset:0; margin:auto), the class-based
   * absolute offsets, and the align-center transform. Matches ONLY the
   * component's own panels — the hand-rolled .cbui-dropdown-panel consumers
   * (taskbar / menu-bar / menu-hoist) are not popovers, so they keep the
   * class-based positioning untouched. */
  .cbui-dropdown-panel:popover-open {
    position: fixed;
    inset: auto;            /* controller sets top/left/right/bottom inline */
    margin: 0;
    --cbui-dd-panel-ox: 0;  /* align-center keyframe x-offset off (JS centers via left) */
  }
  .cbui-dropdown-panel-align-center:popover-open {
    transform: none;        /* JS computes the centered left; drop the -50% shift */
  }

  @keyframes cbui-dropdown-open {
    from { opacity: 0; transform: translateX(var(--cbui-dd-panel-ox)) scale(0.97) translateY(-4px); }
    to   { opacity: 1; transform: translateX(var(--cbui-dd-panel-ox)) scale(1)    translateY(0); }
  }

  .a11y-reduced-motion .cbui-dropdown-panel,
  :where(.a11y-reduced-motion) .cbui-dropdown-panel {
    animation: none;
  }

  /* ── Viewport clamp (JS-applied) ─────────────────────────────────────
   * The controller adds this class + an inline max-height when the panel's
   * natural height would run past the viewport edge: the panel ends at the
   * window edge and scrolls inside. overflow stays default (visible) on
   * unclamped panels so submenu flyouts can escape the panel box.
   * Scrollbar: thin + muted, track transparent — overlay clips at the
   * panel's border-radius automatically. */
  .cbui-dropdown-panel-clamped {
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, var(--cbui-dd-item-color-muted) 45%, transparent) transparent;
  }

  /* ── Panel alignment (horizontal) ───────────────────────────────── */
  .cbui-dropdown-panel-align-start  { left: 0; }
  .cbui-dropdown-panel-align-end    { right: 0; }
  .cbui-dropdown-panel-align-center { left: 50%; transform: translateX(-50%); --cbui-dd-panel-ox: -50%; }

  /* ── Panel side (vertical) ───────────────────────────────────────── */
  .cbui-dropdown-panel-side-bottom { top: calc(100% + 0.3rem); }
  .cbui-dropdown-panel-side-top    { bottom: calc(100% + 0.3rem); }

  /* ── Panel sizes ─────────────────────────────────────────────────── */
  /* sm = the dense chrome tier: tighter padding only, the item type stays at
     --font-size-md (CB-2918; type never renders below normal). */
  .cbui-dropdown-panel-sm { min-width: var(--cbui-dd-min-w-sm); --cbui-dd-item-px: 0.625rem; --cbui-dd-item-py: 0.375rem; --cbui-dd-item-fs: var(--font-size-md, 1rem); }
  .cbui-dropdown-panel-md { min-width: var(--cbui-dd-min-w-md); }
  /* lg keeps the base font (same as the md default) and only scales up the
     spacing + icon box — "generous" is a spacing tier, not a type tier. */
  .cbui-dropdown-panel-lg {
    min-width: var(--cbui-dd-min-w-lg);
    --cbui-dd-item-px: 0.875rem;
    --cbui-dd-item-py: 0.625rem;
    --cbui-dd-item-fs: var(--font-size-base, 1rem);
    --cbui-dd-item-gap: 0.625rem;
    --cbui-dd-item-icon-sz: 1.125rem;
  }

  /* ── Submenu panel (side-flyout) ─────────────────────────────────── */
  .cbui-dropdown-panel-sub {
    top: -0.25rem;
    left: calc(100% + 0.25rem); /* fly right by default */
  }

  /* JS adds this class when no room to fly right → fly left instead */
  .cbui-dropdown-sub-fly-start .cbui-dropdown-panel-sub {
    left: auto;
    right: calc(100% + 0.25rem);
  }

  /* ── Stacked sub-panel layout (drill-down overlay) ───────────────────
   * Sub-panel is a fully independent panel layered directly on top of its
   * parent panel: same width, natural content height, shifted down 0.5rem so
   * the parent's top strip peeks through as the only visible "stack" cue.
   * Parent items are visually faded (opacity) and disabled (pointer-events)
   * while a sub is open; the back-header inside the sub provides drill-back.
   * Same behavior on narrow viewports (<40rem) without the opt-in class.
   *
   * Two triggers, identical declarations: */

  /* Drop the sub-Dropdown wrapper's own positioning context so the sub-panel
     positions relative to the parent panel (next position:absolute ancestor)
     instead of the trigger row. */
  .cbui-dropdown-stacked .cbui-dropdown-sub {
    position: static;
  }
  @media (max-width: 40rem) {
    .cbui-dropdown:not(.cbui-dropdown-stacked) .cbui-dropdown-sub {
      position: static;
    }
  }

  .cbui-dropdown-stacked .cbui-dropdown-panel-sub,
  .cbui-dropdown-stacked .cbui-dropdown-panel-sub.cbui-dropdown-sub-fly-start {
    position: absolute;
    /* Stack offset: 0.5rem down from parent.top so the user sees the parent's
       top edge as a depth hint. Bottom auto = natural content height. */
    top: 0.5rem;
    bottom: auto;
    /* Slight protrusion beyond the parent's outer border on each side: 2px extra
       (== 4px wider total) for a subtle "lifted card" look — same in every flavor.
       The -1px compensates the parent's border, since the containing block is the
       parent's padding-box (sits inside the border). Result: sub.outer-left is at
       parent.outer-left - 2px on every flavor. */
    left: -3px;
    right: -3px;
    width: auto;
    height: auto;
    /* Drop the panel-size min-widths (14rem on `md`, etc) so the sub width
       follows the parent rather than the size token. */
    min-width: 0;
    z-index: 1;
    /* Sub keeps its default panel chrome (rounded corners, shadow, bg). */
  }
  @media (max-width: 40rem) {
    .cbui-dropdown:not(.cbui-dropdown-stacked) .cbui-dropdown-panel-sub,
    .cbui-dropdown:not(.cbui-dropdown-stacked) .cbui-dropdown-panel-sub.cbui-dropdown-sub-fly-start {
      position: absolute;
      top: 0.5rem;
      bottom: auto;
      left: -3px;
      right: -3px;
      width: auto;
      height: auto;
      min-width: 0;
      z-index: 1;
    }
  }

  /* Fade + disable parent items while a stacked sub is open. No dim overlay —
     the parent's top strip still shows through with faded items, signalling
     "below the active level". The sub-trigger row of the OPEN sub gets the
     same treatment via :first-child of the sub-wrapper (opacity on the wrapper
     itself would also fade the open sub-panel inside it, which we don't want). */
  .cbui-dropdown-stacked .cbui-dropdown-panel:has(> .cbui-dropdown-sub > .cbui-dropdown-panel-sub:not([hidden])) > :is(.cbui-dropdown-item, .cbui-dropdown-label, .cbui-dropdown-separator, .cbui-dropdown-description, .cbui-dropdown-header, .cbui-dropdown-footer),
  .cbui-dropdown-stacked .cbui-dropdown-panel:has(> .cbui-dropdown-sub > .cbui-dropdown-panel-sub:not([hidden])) > .cbui-dropdown-sub > .cbui-dropdown-item-sub-trigger {
    opacity: 0.3;
    pointer-events: none;
  }
  @media (max-width: 40rem) {
    .cbui-dropdown:not(.cbui-dropdown-stacked) .cbui-dropdown-panel:has(> .cbui-dropdown-sub > .cbui-dropdown-panel-sub:not([hidden])) > :is(.cbui-dropdown-item, .cbui-dropdown-label, .cbui-dropdown-separator, .cbui-dropdown-description, .cbui-dropdown-header, .cbui-dropdown-footer),
    .cbui-dropdown:not(.cbui-dropdown-stacked) .cbui-dropdown-panel:has(> .cbui-dropdown-sub > .cbui-dropdown-panel-sub:not([hidden])) > .cbui-dropdown-sub > .cbui-dropdown-item-sub-trigger {
      opacity: 0.45;
      pointer-events: none;
    }
  }

  /* ── Stacked back-header (drill-back navigation) ─────────────────────
   * Rendered as the first child of every sub Dropdown panel by Twig, hidden
   * by default; surfaces only when the panel is in stacked drill-down mode.
   * The back-button click closes its own sub via Stimulus action — drilling
   * one level back at a time. */
  .cbui-dropdown-stacked-back {
    display: none;
    align-items: center;
    gap: 0.375rem;
    /* <button> elements have an intrinsic shrink-to-fit width, even with
       display: flex — so neither `width: auto` nor `width: 100%` paints the
       hover bg flush to the panel border in inset flavors (Aero/Soft). Use an
       explicit border-box width that compensates the negative margins:
         outer width = container.content-width + 2 * panel-px
       which lands the box edges exactly on the sub-panel's padding-box (= sub's
       inner border edge) regardless of flavor. In full-mode flavors panel-px=0
       so this reduces to plain 100%. */
    box-sizing: border-box;
    width: calc(100% + 2 * var(--cbui-dd-panel-px, 0px));
    /* Negate the sub-panel's inline padding so the bg reaches sub's border edge. */
    margin-inline: calc(-1 * var(--cbui-dd-panel-px, 0px));
    margin-top: calc(-1 * var(--cbui-dd-panel-py, 0px));
    /* Re-add the same offset to padding so the text aligns with item content below. */
    padding-inline: calc(var(--cbui-dd-item-px) + var(--cbui-dd-panel-px, 0px));
    padding-block: 0.5rem;
    font-size: var(--cbui-dd-label-fs);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--cbui-dd-item-color-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    text-align: start;
    background: none;
    border: none;
    border-bottom: 1px solid var(--cbui-dd-hf-border);
    cursor: pointer;
    /* Top corners hug the panel's outer curve via the same edge-corner formula
       used by first-child items elsewhere — keeps the "matched to the box"
       polish in Aero/Soft (where panel-radius is large) and Clean (where item-
       radius is 0 but panel-radius isn't). Without this, Aero's 20px panel
       curve would sit atop a 6px-radius back-header — an obvious visual seam. */
    border-start-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
    border-start-end-radius:   max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
    /* Bottom corners use the regular item-radius (0 in full, item-radius in inset). */
    border-end-start-radius: var(--cbui-dd-item-radius);
    border-end-end-radius:   var(--cbui-dd-item-radius);
  }
  .cbui-dropdown-stacked-back:hover,
  .cbui-dropdown-stacked-back:focus-visible {
    background-color: var(--cbui-dd-item-hover-bg);
    color: var(--cbui-dd-item-color);
    outline: none;
  }
  .cbui-dropdown-stacked-back svg {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
  }
  /* Surface only in stacked drill-down panels (opt-in or responsive). */
  .cbui-dropdown-stacked .cbui-dropdown-panel-sub > .cbui-dropdown-stacked-back {
    display: flex;
  }
  @media (max-width: 40rem) {
    .cbui-dropdown:not(.cbui-dropdown-stacked) .cbui-dropdown-panel-sub > .cbui-dropdown-stacked-back {
      display: flex;
    }
  }

  /* Inset flavors (Aero/Soft default + explicit panel-inset) — back-item sits
     inside the panel padding and uses rounded item-radius corners on hover,
     matching the surrounding inset menu items. The full-mode chrome (negative
     margins reaching panel border, full-width hover bg) only fits flush layouts.
     Note: the top-corner edge formula stays inherited from the base rule above —
     in Aero (panel-radius=20px, panel-py=4px) it computes to 15px, hugging the
     panel's outer curve so the hover bg matches what other inset items do. */
  .cbui-dropdown-panel-inset.cbui-dropdown-panel-sub > .cbui-dropdown-stacked-back,
  [data-flavor="aero"] .cbui-dropdown-panel-sub:not(.cbui-dropdown-panel-full) > .cbui-dropdown-stacked-back,
  [data-flavor="soft"] .cbui-dropdown-panel-sub:not(.cbui-dropdown-panel-full) > .cbui-dropdown-stacked-back {
    /* Reset the full-mode offsets — sit inside panel padding like a normal item. */
    width: 100%;
    margin-inline: 0;
    margin-top: 0;
    padding-inline: var(--cbui-dd-item-px);
    /* Drop the border-bottom and use a margin-bottom instead so the visual gap
       between the back-item and the items below matches the regular item rhythm
       (no full-width divider line cutting through the inset gutter). */
    border-bottom: none;
    margin-bottom: 0.125rem;
  }

  /* ── Section label ───────────────────────────────────────────────── */
  .cbui-dropdown-label {
    padding-inline: var(--cbui-dd-item-px);
    padding-block: 0.375rem 0.125rem;
    font-size: var(--cbui-dd-label-fs);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    line-height: 1.4;
    user-select: none;
  }

  /* ── Separator ───────────────────────────────────────────────────── */
  /* Horizontal margin computes "panel-border distance minus already-existing panel padding"
     so the visual gap to the panel border is uniform regardless of item_style. */
  .cbui-dropdown-separator {
    height: 1px;
    margin-block: 0.25rem;
    margin-inline: max(0px, calc(var(--cbui-dd-sep-inset) - var(--cbui-dd-panel-px)));
    background-color: var(--cbui-dd-panel-border);
  }

  /* ── Shadow modifiers ────────────────────────────────────────────── */
  .cbui-dropdown-panel-shadow-none { --cbui-dd-panel-shadow: none; }
  .cbui-dropdown-panel-shadow-sm   { --cbui-dd-panel-shadow: 0 2px 8px rgb(0 0 0 / 0.08); }
  .cbui-dropdown-panel-shadow-md   { --cbui-dd-panel-shadow: 0 6px 20px rgb(0 0 0 / 0.12), 0 2px 6px rgb(0 0 0 / 0.06); }
  .cbui-dropdown-panel-shadow-lg   { --cbui-dd-panel-shadow: 0 10px 30px rgb(0 0 0 / 0.12), 0 4px 8px rgb(0 0 0 / 0.06); }

  /* ── Inset item style ────────────────────────────────────────────── */
  /* Tokens — always applied. Auto-flow (.cbui-dropdown-panel-columns) and DropdownColumn
     panels (:has(> .cbui-dropdown-col)) override --cbui-dd-panel-px to 0px below: in
     those modes the per-item / per-column inset comes from --cbui-dd-col-px instead.
     This keeps a single inset value (col-px) for "gap between hover-bg and any nearby
     edge — panel border, vertical separator, neighboring cell" — see also the
     auto-flow margin rule down in the multi-column section. */
  .cbui-dropdown-panel-inset {
    --cbui-dd-item-radius: var(--radius-sm, 0.375rem);
    --cbui-dd-panel-px: 0.25rem;
    --cbui-dd-panel-py: 0.25rem;
    --cbui-dd-col-px: 0.25rem;
    --cbui-dd-col-py: 0.25rem;

    /* Sub-trigger sits in a nested .cbui-dropdown that resets the token —
       override it here so the trigger row looks like the rest of the items. */
    > .cbui-dropdown-sub {
      --cbui-dd-item-radius: var(--radius-sm, 0.375rem);
    }
  }
  /* Panel-level inset padding — single-flow only (NOT auto-flow, NOT DropdownColumn).
     Auto-flow uses per-item margin-inline: col-px instead, so the hover background
     keeps the same distance to vsep as to the panel border (see multi-column section).
     DropdownColumn panels delegate the inset to each column. */
  .cbui-dropdown-panel-inset:not(:has(> .cbui-dropdown-col)):not(.cbui-dropdown-panel-columns) {
    padding-inline: 0.25rem;
    padding-block: 0.25rem;
  }
  /* Auto-flow inset: only block padding on the panel; inline insetting comes from
     per-item margin (col-px). Override panel-px to 0 so the vsep formula collapses
     to `i * round(100%/N)` and the items+vsep share one inset budget = col-px. */
  .cbui-dropdown-panel-inset.cbui-dropdown-panel-columns {
    --cbui-dd-panel-px: 0px;
    padding-block: 0.25rem;
  }

  /* ── Edge-item corner matching ──────────────────────────────────────
   * Items at the very top/bottom of a panel get corners that hug the panel's
   * inner curve so the hover background flows smoothly into the rounded panel.
   *
   * Single formula handles all cases via --cbui-dd-panel-py:
   *   FULL  (py=0):    radius = panel-radius - 1px         (matches inner border)
   *   INSET (py=4px):  radius = panel-radius - py - 1px    (matches inner border, offset by inset)
   * max() ensures the radius never falls below the small uniform item-radius.
   *
   * Excludes multi-column / mega panels — their first/last child positions
   * aren't single-flow visual top/bottom; they have separate corner rules below.
   *
   * `:first-child` / `:last-child` only describe the visual edge while every
   * child is on display. In an OVERFLOW menu (`ui-component--overflow`) all rows
   * are rendered up front and only the collapsed ones are shown, so the first and
   * last DOM children are usually hidden and the rounding landed on nothing while
   * the visible edge rows stayed square inside a rounded panel. CSS has no
   * "first visible child", so that controller marks them with
   * `.cbui-dropdown-item-edge-start` / `-edge-end` and they join the rule here. */
  .cbui-dropdown-panel:not(.cbui-dropdown-panel-columns):not(:has(> .cbui-dropdown-col)) > .cbui-dropdown-item:first-child,
  .cbui-dropdown-panel:not(.cbui-dropdown-panel-columns):not(:has(> .cbui-dropdown-col)) > .cbui-dropdown-item-edge-start,
  .cbui-dropdown-panel:not(.cbui-dropdown-panel-columns):not(:has(> .cbui-dropdown-col)) > .cbui-dropdown-sub:first-child > .cbui-dropdown-item {
    border-start-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
    border-start-end-radius:   max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  .cbui-dropdown-panel:not(.cbui-dropdown-panel-columns):not(:has(> .cbui-dropdown-col)) > .cbui-dropdown-item:last-child,
  .cbui-dropdown-panel:not(.cbui-dropdown-panel-columns):not(:has(> .cbui-dropdown-col)) > .cbui-dropdown-item-edge-end,
  .cbui-dropdown-panel:not(.cbui-dropdown-panel-columns):not(:has(> .cbui-dropdown-col)) > .cbui-dropdown-sub:last-child > .cbui-dropdown-item {
    border-end-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
    border-end-end-radius:   max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  /* Sub-Dropdowns always render `.cbui-dropdown-stacked-back` as the first DOM
     child of their panel (so it can surface in stacked / responsive mode without
     extra Twig logic). In flyout mode the back-header is `display: none`, but
     `:first-child` still resolves to it — so the rule above misses the actual
     visible first item. Match "first item after a hidden back-header" only when
     we're NOT in stacked drill-down (viewport above 40rem AND panel-sub is not
     a descendant of `.cbui-dropdown-stacked`); in stacked / responsive mode the
     back-header itself owns the curve and the item below it stays at regular
     item-radius.
     NOTE: the guard must be on the panel-sub element itself via
     `:not(.cbui-dropdown-stacked .cbui-dropdown-panel-sub)` — checking
     `.cbui-dropdown:not(.cbui-dropdown-stacked)` on the immediate parent fails
     because the sub-dropdown wrapper never carries `.cbui-dropdown-stacked`
     directly; the stacked class lives on the outer parent. */
  @media not all and (max-width: 40rem) {
    .cbui-dropdown-panel-sub:not(.cbui-dropdown-stacked .cbui-dropdown-panel-sub):not(.cbui-dropdown-panel-columns):not(:has(> .cbui-dropdown-col)) > .cbui-dropdown-stacked-back + .cbui-dropdown-item {
      border-start-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
      border-start-end-radius:   max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
    }
  }

  /* ── Description (inline prose inside a panel) ──────────────────── */
  .cbui-dropdown-description {
    padding-inline: var(--cbui-dd-item-px);
    padding-block: var(--cbui-dd-item-py);
    font-size: var(--cbui-dd-item-fs);
    color: var(--cbui-dd-item-color-muted);
    line-height: 1.5;
    user-select: none;
  }

  /* ── Header / Footer ─────────────────────────────────────────────── */
  .cbui-dropdown-header,
  .cbui-dropdown-footer {
    /* Negate the panel's inline padding so we reach the panel border edge */
    margin-inline: calc(-1 * var(--cbui-dd-panel-px, 0px));
    /* Re-add whichever inset is in effect (panel-px in single-flow; col-px in
       auto-flow / DropdownColumn — they're orthogonal but never both non-zero),
       so the header text always aligns with item content (item-px + inset). */
    padding-inline: calc(var(--cbui-dd-item-px) + max(var(--cbui-dd-panel-px, 0px), var(--cbui-dd-col-px, 0px)));
    padding-block: var(--cbui-dd-item-py);
    min-height: 2.5rem; /* tall enough for a sm button */
    display: flex;
    align-items: center;
    background-color: var(--cbui-dd-hf-bg);
  }

  /* Top corners of a leading header */
  .cbui-dropdown-panel > .cbui-dropdown-header:first-child {
    border-start-start-radius: calc(var(--cbui-dd-panel-radius) - 1px);
    border-start-end-radius:   calc(var(--cbui-dd-panel-radius) - 1px);
    border-bottom: 1px solid var(--cbui-dd-hf-border);
    margin-top: calc(-1 * var(--cbui-dd-panel-py, 0px));
  }

  /* Bottom corners of a trailing footer */
  .cbui-dropdown-panel > .cbui-dropdown-footer:last-child {
    border-end-start-radius: calc(var(--cbui-dd-panel-radius) - 1px);
    border-end-end-radius:   calc(var(--cbui-dd-panel-radius) - 1px);
    border-top: 1px solid var(--cbui-dd-hf-border);
    margin-bottom: calc(-1 * var(--cbui-dd-panel-py, 0px));
  }

  /* sticky footer: on a panel that scrolls (clamped to the window) the footer stays at the bottom edge. The panel's own bottom padding is
     taken back so the footer meets the edge; the fill is the footer tint OVER the panel colour, so scrolling items never show through. */
  .cbui-dropdown-panel > .cbui-dropdown-footer.cbui-dropdown-footer-sticky {
    position: sticky;
    inset-block-end: calc(-1 * var(--cbui-dd-panel-py, 0px));
    z-index: 1;
    background:
      linear-gradient(var(--cbui-dd-hf-bg), var(--cbui-dd-hf-bg)),
      var(--cbui-dd-panel-bg);
  }

  /* sticky header: twin of the sticky footer, the header stays at the top edge of a scrolling (clamped) panel. */
  .cbui-dropdown-panel > .cbui-dropdown-header.cbui-dropdown-header-sticky {
    position: sticky;
    inset-block-start: calc(-1 * var(--cbui-dd-panel-py, 0px));
    z-index: 1;
    background:
      linear-gradient(var(--cbui-dd-hf-bg), var(--cbui-dd-hf-bg)),
      var(--cbui-dd-panel-bg);
  }

  /* Breathing room: 0.25rem gap between header separator and first item */
  .cbui-dropdown-panel > .cbui-dropdown-header
    + :is(.cbui-dropdown-item, .cbui-dropdown-sub, .cbui-dropdown-label,
          .cbui-dropdown-separator, .cbui-dropdown-description) {
    margin-top: 0.25rem;
  }

  /* Breathing room: 0.25rem gap between last item and footer separator */
  .cbui-dropdown-panel
    > :is(.cbui-dropdown-item, .cbui-dropdown-sub, .cbui-dropdown-label,
          .cbui-dropdown-separator, .cbui-dropdown-description):has(+ .cbui-dropdown-footer) {
    margin-bottom: 0.25rem;
  }

  /* Breathing room: an EmptyState placed directly in the panel (the "nothing
     here" surface of e.g. the Live-Status / Notifications dropdowns). Its
     subtle tint is a visible box, so unlike items it must not touch the panel
     frame or the header/footer bands: inline inset = item padding (aligns the
     box with item content), block inset = item padding-y. */
  .cbui-dropdown-panel > .cbui-empty-state {
    margin-block: var(--cbui-dd-item-py);
    margin-inline: var(--cbui-dd-item-px);
  }

  /* In a columns panel, header/footer always span all columns */
  .cbui-dropdown-panel-columns > .cbui-dropdown-header,
  .cbui-dropdown-panel-columns > .cbui-dropdown-footer {
    grid-column: 1 / -1;
  }

  /* ── Multi-column panel ──────────────────────────────────────────── */
  /* overflow:hidden clips hover backgrounds at rounded corners without
     needing the manual first/last-child radius rules. Columns panels
     don't have submenu flyouts that need to escape. */
  .cbui-dropdown-panel-columns {
    display: grid;
    /* Twig builds --cbui-dd-panel-cols using calc(round(100%/N, 1px)) for the first N-1
       columns and minmax(0, 1fr) for the last — guaranteeing integer-pixel cell boundaries
       that align exactly with the absolutely-positioned vsep overlays. */
    grid-template-columns: var(--cbui-dd-panel-cols, repeat(2, minmax(0, 1fr)));
    /* When `rows=M` is set, --cbui-dd-panel-rows is repeat(M, auto); otherwise unset
       and the implicit row sizing handles auto-flow content. */
    grid-template-rows: var(--cbui-dd-panel-rows);
    overflow: hidden;

    /* Labels, separators, header, footer always span all columns */
    > .cbui-dropdown-label,
    > .cbui-dropdown-separator,
    > .cbui-dropdown-header,
    > .cbui-dropdown-footer {
      grid-column: 1 / -1;
    }
  }

  /* When `rows=M` is set: items flow column-first (down each column then to the next).
     Used together with `columns=N` for a fixed N×M grid (e.g. mega-menu category grids).
     The row template is set on .cbui-dropdown-panel-columns above (also defaults there). */
  .cbui-dropdown-panel-rows {
    grid-auto-flow: column;
  }

  /* Vertical column separator — absolutely positioned overlay (NOT in the grid).
     Position is set inline by Twig as `left: round(N% * i, 1px)` matching the rounded
     grid column boundaries → cell hover ends exactly where sep starts, no sub-pixel mismatch.
     Being outside the grid means it doesn't interfere with auto-flow placement of items
     and full-width labels/headers. */
  .cbui-dropdown-vsep {
    position: absolute;
    top: var(--cbui-dd-sep-inset);
    bottom: var(--cbui-dd-sep-inset);
    width: 1px;
    background-color: var(--cbui-dd-panel-border);
    /* On top of items so the sep is always visible regardless of which cell hovers. */
    z-index: 1;
    pointer-events: none;
  }

  /* Auto-flow per-cell inset — mirrors DropdownColumn's per-column padding so the
     hover background has the same gap to a vertical separator as it has to the panel
     border. Items use width: auto (set below) so the grid's default justify-self:
     stretch fills the cell minus margins. Labels / descriptions span 1/-1 and pick
     up the same horizontal inset, keeping their text aligned with items.
     In full mode --cbui-dd-col-px is 0, so this rule is inert. */
  .cbui-dropdown-panel-columns > :is(.cbui-dropdown-item, .cbui-dropdown-label, .cbui-dropdown-description) {
    margin-inline: var(--cbui-dd-col-px, 0px);
  }
  /* Items in auto-flow cells must use width: auto (not 100%) so margin-inline doesn't
     overflow the cell — grid's justify-self: stretch fills the cell minus margins. */
  .cbui-dropdown-panel-columns > .cbui-dropdown-item {
    width: auto;
  }

  /* Multi-column: round the items at the four panel corners so they hug the panel's rounded
     border. Uses the same edge-radius formula as single-flow panels.
     `:nth-child(N of :not(.cbui-dropdown-vsep))` skips the absolutely-positioned vsep
     overlay divs (which are first in DOM) and targets the actual first/last content child.
     Full-width children (label/header/separator/footer) get BOTH start+end corners since
     they span all columns. */
  .cbui-dropdown-panel-columns > :nth-child(1 of :not(.cbui-dropdown-vsep)) {
    border-start-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  .cbui-dropdown-panel-columns > :nth-child(1 of :not(.cbui-dropdown-vsep)):is(.cbui-dropdown-label, .cbui-dropdown-separator, .cbui-dropdown-header) {
    border-start-end-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  .cbui-dropdown-panel-columns > :nth-last-child(1 of :not(.cbui-dropdown-vsep)) {
    border-end-end-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  .cbui-dropdown-panel-columns > :nth-last-child(1 of :not(.cbui-dropdown-vsep)):is(.cbui-dropdown-label, .cbui-dropdown-separator, .cbui-dropdown-footer) {
    border-end-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  /* Bottom-start corner: the FIRST item of the LAST row (=col 1 of last row) gets a rounded
     bottom-left to hug the panel curve. CSS can't see grid column positions, so we detect
     the column count from the inline grid template's `round(33.3333%, ...)` etc. and pick
     nth-last-child(N) accordingly — that's the first item of the last row in an N-col grid
     when the last row is fully populated. */
  /* 2-col grid */
  .cbui-dropdown-panel-columns[style*="round(50%"] > .cbui-dropdown-item:nth-last-child(2 of :not(.cbui-dropdown-vsep)):not(:is(.cbui-dropdown-label, .cbui-dropdown-separator, .cbui-dropdown-header, .cbui-dropdown-footer)) {
    border-end-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  /* 3-col grid */
  .cbui-dropdown-panel-columns[style*="round(33.3333%"] > .cbui-dropdown-item:nth-last-child(3 of :not(.cbui-dropdown-vsep)):not(:is(.cbui-dropdown-label, .cbui-dropdown-separator, .cbui-dropdown-header, .cbui-dropdown-footer)) {
    border-end-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  /* 4-col grid */
  .cbui-dropdown-panel-columns[style*="round(25%"] > .cbui-dropdown-item:nth-last-child(4 of :not(.cbui-dropdown-vsep)):not(:is(.cbui-dropdown-label, .cbui-dropdown-separator, .cbui-dropdown-header, .cbui-dropdown-footer)) {
    border-end-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }
  /* 5-col grid */
  .cbui-dropdown-panel-columns[style*="round(20%"] > .cbui-dropdown-item:nth-last-child(5 of :not(.cbui-dropdown-vsep)):not(:is(.cbui-dropdown-label, .cbui-dropdown-separator, .cbui-dropdown-header, .cbui-dropdown-footer)) {
    border-end-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-panel-py) - 1px));
  }

  /* DropdownColumn corner matching: leftmost col first/last child = panel top-left/bottom-left;
     rightmost col first/last child = panel top-right/bottom-right.
     Uses --cbui-dd-col-py for the inset offset (panel-py is 0 when col children present).
     (CB-1941: no `:has(> .cbui-dropdown-col)` guard on these four rules — the child combinator
     already implies a column. A `:has()` rule whose subject is a bare `> :first-child` makes
     Chrome restyle the WHOLE document on every DOM insertion; on a page with thousands of
     tiles that was ~150 ms per infinite-scroll page.) */
  .cbui-dropdown-panel > .cbui-dropdown-col:first-child > :first-child {
    border-start-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-col-py) - 1px));
  }
  .cbui-dropdown-panel > .cbui-dropdown-col:first-child > :last-child {
    border-end-start-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-col-py) - 1px));
  }
  .cbui-dropdown-panel > .cbui-dropdown-col:last-child > :first-child {
    border-start-end-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-col-py) - 1px));
  }
  .cbui-dropdown-panel > .cbui-dropdown-col:last-child > :last-child {
    border-end-end-radius: max(var(--cbui-dd-item-radius), calc(var(--cbui-dd-panel-radius) - var(--cbui-dd-col-py) - 1px));
  }

  /* ── Width modifiers ─────────────────────────────────────────────────
     `width="sm|md|lg|xl"` sets a fixed responsive panel width (min() caps at viewport).
     `width="full"` is full-viewport, position:fixed (replaces the old `mega` prop).
     All can combine with `columns=N` (auto-flow grid) or DropdownColumn children (flex). */
  .cbui-dropdown-panel-w-sm { width: min(var(--cbui-dd-panel-w-sm), calc(100vw - 2 * var(--cbui-dd-panel-w-margin))); }
  .cbui-dropdown-panel-w-md { width: min(var(--cbui-dd-panel-w-md), calc(100vw - 2 * var(--cbui-dd-panel-w-margin))); }
  .cbui-dropdown-panel-w-lg { width: min(var(--cbui-dd-panel-w-lg), calc(100vw - 2 * var(--cbui-dd-panel-w-margin))); }
  .cbui-dropdown-panel-w-xl { width: min(var(--cbui-dd-panel-w-xl), calc(100vw - 2 * var(--cbui-dd-panel-w-margin))); }
  .cbui-dropdown-panel-w-full {
    position: fixed;
    left: var(--cbui-dd-panel-w-margin);
    right: var(--cbui-dd-panel-w-margin);
    width: auto;
    /* JS controller positions `top` relative to the trigger */
  }

  /* ── DropdownColumn layout — explicit per-column wrappers (any panel width) ─────
     When DropdownColumn children are present, the panel switches to flex-row layout.
     Cols handle their own inset via --cbui-dd-col-px/py, so the panel padding is reset. */
  .cbui-dropdown-panel:has(> .cbui-dropdown-col) {
    overflow: hidden;
    padding-inline: 0;
    padding-block: 0;

    /* :not([hidden]) prevents display:flex from overriding the panel's [hidden]{display:none} rule */
    &:not([hidden]) {
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
      align-items: stretch;
    }
  }

  /* ── DropdownColumn ─────────────────────────────────────────────── */
  .cbui-dropdown-col {
    position: relative;
    flex-shrink: 0;
    flex-grow: 0;
    flex-basis: var(--cbui-dd-col-w, var(--cbui-dd-min-w-md));
    min-width: var(--cbui-dd-col-w, var(--cbui-dd-min-w-md));
    display: flex;
    flex-direction: column;
    /* Tokens default to 0 (full mode); inset rules raise to 0.25rem so items + separator gain visible spacing */
    padding-block: var(--cbui-dd-col-py);
    padding-inline: var(--cbui-dd-col-px);
  }

  /* Vertical separator — sits at the boundary between columns.
     The panel padding is 0 when col children are present (cols own their inset),
     so the inset-block from the col edge equals the visual distance to the panel border. */
  .cbui-dropdown-col + .cbui-dropdown-col::before {
    content: '';
    position: absolute;
    inset-block: var(--cbui-dd-sep-inset);
    inset-inline-start: 0;
    width: 1px;
    background-color: var(--cbui-dd-panel-border);
  }

  /* No-separator modifier: suppress the divider before this column */
  .cbui-dropdown-col-no-separator::before {
    display: none;
  }

  /* fill modifier: column expands to consume remaining panel width */
  .cbui-dropdown-col-fill {
    flex-grow: 1;
    flex-shrink: 1;
  }

  /* ── Items ───────────────────────────────────────────────────────── */
  .cbui-dropdown-item {
    display: flex;
    align-items: center;
    gap: var(--cbui-dd-item-gap);
    width: 100%;
    padding-inline: var(--cbui-dd-item-px);
    padding-block: var(--cbui-dd-item-py);
    font-size: var(--cbui-dd-item-fs);
    line-height: 1.4;
    color: var(--cbui-dd-item-color);
    background: none;
    border: none;
    border-radius: var(--cbui-dd-item-radius);
    cursor: pointer;
    text-align: start;
    text-decoration: none;
    outline: none;
    transition: background-color 100ms, color 100ms;

    /* Focus via keyboard */
    &:focus-visible,
    &.cbui-dropdown-item-focused {
      background-color: var(--cbui-dd-item-hover-bg);
    }

    &:hover {
      background-color: var(--cbui-dd-item-hover-bg);
    }

    &:active {
      background-color: var(--cbui-dd-item-active-bg);
    }
  }

  /* The plain [hidden] attribute must win over the explicit `display` set on
     the dropdown wrapper / its items / labels / separators / descriptions — so a
     consumer or driver (e.g. the Filter add-dimension engine hiding the whole
     "+ Filter" picker when exhausted, or an already-active dimension item) can
     hide with the standard attribute, no inline style override. Specificity
     0,2,0 beats the single-class display rules. */
  .cbui-dropdown[hidden],
  .cbui-dropdown-panel > [hidden],
  .cbui-dropdown-col > [hidden] {
    display: none;
  }

  /* Disabled state */
  .cbui-dropdown-item-disabled,
  .cbui-dropdown-item[disabled],
  .cbui-dropdown-item[aria-disabled="true"] {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
  }

  /* ── Search (searchable dropdowns) ───────────────────────────────── */
  /* Filtered-out items — a class (not [hidden]) so a driver hiding items via
     [hidden] (e.g. the Filter add-dimension engine) and the search filter
     compose: an item is visible only when neither hides it. */
  .cbui-dropdown-item-search-hidden {
    display: none;
  }

  .cbui-dropdown-search {
    position: relative;
    display: flex;
    align-items: center;
    padding: 0.25rem 0.5rem 0.5rem;
  }

  .cbui-dropdown-search-icon {
    position: absolute;
    inset-inline-start: 1rem;
    color: var(--color-text-tertiary, var(--color-text-secondary));
    pointer-events: none;
    font-size: 1rem;
  }

  .cbui-dropdown-search-input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.375rem 0.625rem 0.375rem 2rem;
    font: inherit;
    color: var(--color-text);
    background: var(--color-bg-base);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 0.5rem);
    outline: none;
  }

  .cbui-dropdown-search-input:focus-visible {
    border-color: var(--color-border-strong, var(--color-border));
    box-shadow: 0 0 0 2px var(--color-focus-ring, transparent);
  }

  .cbui-dropdown-empty {
    padding: 0.5rem 0.75rem;
    color: var(--color-text-secondary);
    font-size: var(--font-size-md, 1rem);
  }

  /* ── Item tones ──────────────────────────────────────────────────── */
  .cbui-dropdown-item-tone-danger {
    color: var(--color-tone-danger, #dc2626);

    .cbui-dropdown-item-icon { color: currentColor; }

    &:hover,
    &:focus-visible,
    &.cbui-dropdown-item-focused {
      background-color: color-mix(in srgb, var(--color-tone-danger, #dc2626) var(--cbui-dd-tone-danger-mix, 8%), transparent);
    }
  }

  .cbui-dropdown-item-tone-warning {
    color: var(--color-tone-warning, #d97706);

    .cbui-dropdown-item-icon { color: currentColor; }

    &:hover,
    &:focus-visible,
    &.cbui-dropdown-item-focused {
      background-color: color-mix(in srgb, var(--color-tone-warning, #d97706) var(--cbui-dd-tone-warning-mix, 8%), transparent);
    }
  }

  /* ── Icon slots ──────────────────────────────────────────────────── */
  .cbui-dropdown-item-icon,
  .cbui-dropdown-item-icon-end {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: var(--cbui-dd-item-icon-sz);
    height: var(--cbui-dd-item-icon-sz);
    color: var(--cbui-dd-item-color-muted);
  }

  .cbui-dropdown-item-icon-spacer {
    flex-shrink: 0;
    width: var(--cbui-dd-item-icon-sz);
  }

  /* ── Label ───────────────────────────────────────────────────────── */
  .cbui-dropdown-item-label {
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* ── Item hint — a second line under the label ─────────────────────
   * For a menu row whose label alone does not answer the question it
   * raises. The case it was built for (CB-378): an action that is
   * DISABLED, where the reason is the only thing that turns a dead
   * control into an understandable one. On a toolbar that reason lives
   * in a `title` nobody hovers; in a menu there is room to just say it.
   *
   * Truncation is lifted for these rows on purpose — a reason cut off
   * mid-sentence is no better than no reason.
   *
   * But it is lifted for the REASON, not for the name of the action. The
   * lift used to sit on the whole label box, so a hint turned the action
   * name into wrapping text too — invisible while the panel was 20rem
   * wide and every disabled label was short, and a wrapped action name
   * the moment either changed (Matthias, 2026-08-14: „so dass kein
   * Action-Label umbricht. Die Info-Texte dürfen und sollen natürlich
   * weiterhin umbrechen."). Hence `-title`: the wrapping box is the
   * label, the one line inside it is the title, and the two rules can
   * finally differ. Rows WITHOUT a hint keep their bare text node and
   * are untouched by all of this. */
  .cbui-dropdown-item:has(.cbui-dropdown-item-hint) { align-items: flex-start; }
  .cbui-dropdown-item-label:has(> .cbui-dropdown-item-hint) {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }
  .cbui-dropdown-item-title {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .cbui-dropdown-item-hint {
    display: block;
    margin-block-start: 0.125rem;
    font-size: var(--font-size-md, 1rem);
    line-height: 1.35;
    color: var(--cbui-dd-item-color-muted);
    white-space: normal;
    /* CB-1937: the hint wraps inside the width the titles define; it never widens the shrink-to-fit panel. */
    contain: inline-size;
    min-inline-size: 10rem;
  }

  /* ── Keyboard shortcut hint ──────────────────────────────────────── */
  .cbui-dropdown-item-shortcut {
    flex-shrink: 0;
    margin-inline-start: auto;
    padding-inline-start: 0.75rem;
    font-size: var(--cbui-dd-shortcut-fs);
    font-family: inherit;
    font-weight: 400;
    color: var(--cbui-dd-item-color-muted);
    background: none;
    border: none;
    padding: 0;
    padding-inline-start: 0.75rem;
  }

  /* CB-2642: a lazily filled slot keeps room for up to 99.999 while empty, so the entries do not jump when the text arrives. */
  .cbui-dropdown-item-shortcut-lazy {
    min-inline-size: calc(0.75rem + 6ch);
    text-align: end;
    font-variant-numeric: tabular-nums;
  }

  /* The badge slot of an item: after the label, one line, never shrunk (the label is what gives way). */
  .cbui-dropdown-item-badge {
    flex: none;
    display: inline-flex;
    align-items: center;
    margin-inline-start: 0.5rem;
    white-space: nowrap;
  }

  /* ── Check mark (checkbox items) ─────────────────────────────────── */
  /* The glyph is always rendered for layout stability; visibility is toggled
     via aria-checked so the panel doesn't reflow when the user toggles a
     checkbox item (the JS controller flips aria-checked on click). */
  .cbui-dropdown-item-check {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: var(--cbui-dd-item-icon-sz);
    height: var(--cbui-dd-item-icon-sz);
    color: var(--cbui-dd-item-check-color);
  }
  .cbui-dropdown-item[aria-checked="false"] .cbui-dropdown-item-check svg {
    visibility: hidden;
  }

  /* ── Check mark (single-choice items) ─────────────────────────────────
     A `role="menuitemradio"` item — a view switch, a sort mode, a list
     placement — carries its state mark in the TRAILING icon slot, because
     the leading one is already spoken for: `:checked` renders its glyph
     where `icon` would go, and a placement menu that trades away
     ph:sidebar-simple for a tick has lost more than it gained.

     ⚠ Until this rule existed the component documented the pairing
     (`role="menuitemradio"` + `aria-checked`, DropdownItem docblock) but
     shipped no VISUAL for it. The tick from `iconEnd="ph:check"` was drawn
     on every row, so all five options of a placement switch read as
     active at once. Four Bringers had each written the same two rules by
     hand against their own item class; three others (agents, wiki,
     conversations) never did, and showed the defect. It belongs here, once
     — the popover is portaled, but these selectors travel with the item.

     Keyed on the role, so an ordinary item's `iconEnd` stays the
     decorative trailing slot it is. */
  .cbui-dropdown-item[role="menuitemradio"] .cbui-dropdown-item-icon-end,
  .cbui-dropdown-item[role="menuitemcheckbox"][aria-checked] .cbui-dropdown-item-icon-end {
    color: var(--color-primary);
    margin-inline-start: auto;
  }
  .cbui-dropdown-item[role="menuitemradio"][aria-checked="false"] .cbui-dropdown-item-icon-end,
  .cbui-dropdown-item[role="menuitemcheckbox"][aria-checked="false"] .cbui-dropdown-item-icon-end {
    visibility: hidden;
  }
  /* The same trailing tick for a SWITCH entry: an item given `role="menuitemcheckbox"` explicitly (not through the `checked`
     prop, whose tick stands in the leading slot) together with `iconEnd="ph:check"` and `aria-checked`. The dropdown flips
     `aria-checked` on a click, the tick follows. */

  /* ── Submenu trigger item ────────────────────────────────────────── */
  .cbui-dropdown-item-sub-trigger {
    .cbui-dropdown-item-arrow {
      flex-shrink: 0;
      margin-inline-start: auto;
      color: var(--cbui-dd-item-color-muted);
      transition: transform 120ms;
    }

    &[aria-expanded="true"] .cbui-dropdown-item-arrow {
      transform: rotate(90deg);
    }
  }

  /* Reduced motion: keep the chevron pointing right (no rotation, no animation)
     even when the sub-menu is expanded. */
  .a11y-reduced-motion .cbui-dropdown-item-sub-trigger .cbui-dropdown-item-arrow,
  :where(.a11y-reduced-motion) .cbui-dropdown-item-sub-trigger .cbui-dropdown-item-arrow {
    transition: none;
  }
  .a11y-reduced-motion .cbui-dropdown-item-sub-trigger[aria-expanded="true"] .cbui-dropdown-item-arrow,
  :where(.a11y-reduced-motion) .cbui-dropdown-item-sub-trigger[aria-expanded="true"] .cbui-dropdown-item-arrow {
    transform: none;
  }

  /* ── Submenu wrapper ─────────────────────────────────────────────── */
  .cbui-dropdown-sub {
    position: relative;
    display: block; /* override inline-block from .cbui-dropdown so trigger fills panel width */
  }

  .cbui-dropdown-sub-disabled {
    pointer-events: none;
    opacity: 0.45;
  }

  /* ── Dark mode — stronger hover + tone backgrounds + distinct panel layers ── */
  .dark :where(.cbui-dropdown),
  [data-scheme="dark"] :where(.cbui-dropdown) {
    /* Panel slightly darker than the surface so it recedes from the page */
    --cbui-dd-panel-bg:           color-mix(in srgb, var(--color-surface) 50%, var(--color-surface-elevated, black));
    /* Header/footer: noticeably lighter than the panel, but not as bright as hover */
    --cbui-dd-hf-bg:              color-mix(in srgb, var(--color-surface) 50%, var(--color-bg-muted));
    /* Header/footer separator in dark mode: darker than hf-bg so it reads as a
       depth groove rather than a bright ridge against the lifted hf surface. */
    --cbui-dd-hf-border:          color-mix(in srgb, var(--cbui-dd-panel-bg) 60%, black);
    --cbui-dd-item-hover-bg:      var(--color-bg-muted);
    --cbui-dd-item-active-bg:     var(--color-bg-strong, rgb(255 255 255 / 0.14));
    --cbui-dd-tone-danger-mix:    16%;
    --cbui-dd-tone-warning-mix:   16%;
  }

  /* ── Flavor overrides ────────────────────────────────────────────── */

  /* ── Flavor item_style defaults ─────────────────────────────────────
   * When item_style is not explicitly set (no panel-full / panel-inset class):
   *   Aero  → inset   (glass + rounded items — macOS aesthetic)
   *   Soft  → inset   (rounded everything — soft aesthetic)
   *   Clean / Glass / Sharp → full (no override needed, full is the base)
   *
   * Explicit item_style="full" adds cbui-dropdown-panel-full  → skips both Aero + Soft inset rules
   * Explicit item_style="inset" adds cbui-dropdown-panel-inset → skips base corner rounding rule
   * ─────────────────────────────────────────────────────────────────── */

  /* Aero: lifted panel — solid background, no backdrop translucency.
     The dropdown is a menu — readability of menu items beats the aesthetic
     of seeing the page through it. The "glass" Aero feel still carries via
     the inset 1px highlight on the top edge (light-from-above) and the
     premium shadow + border-color, without making the actual content
     unreadable. Override the panel-border token (not just border-color) so
     separators inside the panel inherit the same color as the panel border. */
  [data-flavor="aero"] .cbui-dropdown-panel {
    --cbui-dd-panel-border: color-mix(in srgb, var(--color-border-strong) 60%, transparent);
    background-color: var(--cbui-dd-panel-bg);
    box-shadow:
      var(--cbui-dd-panel-shadow),
      inset 0 1px 0 rgb(255 255 255 / 0.12);
  }

  /* Aero: inset tokens — always applied for non-full panels */
  [data-flavor="aero"] .cbui-dropdown-panel:not(.cbui-dropdown-panel-full) {
    --cbui-dd-item-radius: var(--radius-sm, 0.375rem);
    --cbui-dd-panel-px: 0.25rem;
    --cbui-dd-panel-py: 0.25rem;
    --cbui-dd-col-px: 0.25rem;
    --cbui-dd-col-py: 0.25rem;

    > .cbui-dropdown-sub {
      --cbui-dd-item-radius: var(--radius-sm, 0.375rem);
    }
  }
  /* Aero: panel-level inset padding — single-flow only (NOT auto-flow, NOT DropdownColumn).
     Mirrors the .cbui-dropdown-panel-inset variant above; see that comment for rationale. */
  [data-flavor="aero"] .cbui-dropdown-panel:not(.cbui-dropdown-panel-full):not(:has(> .cbui-dropdown-col)):not(.cbui-dropdown-panel-columns) {
    padding-inline: 0.25rem;
    padding-block: 0.25rem;
  }
  [data-flavor="aero"] .cbui-dropdown-panel:not(.cbui-dropdown-panel-full).cbui-dropdown-panel-columns {
    --cbui-dd-panel-px: 0px;
    padding-block: 0.25rem;
  }
  /* Aero sub-panels: kept distinct from root panels so a later author who
     reintroduces backdrop-filter at the root knows sub-panels must stay
     solid (nested backdrop-filters don't compose reliably; the inner one
     ends up filtering its parent's painted surface instead of the page
     behind). For now both are solid, but this rule documents the
     constraint. */
  [data-flavor="aero"] .cbui-dropdown-panel-sub {
    background-color: var(--cbui-dd-panel-bg);
  }

  /* Soft: inset tokens — always applied for non-full panels */
  [data-flavor="soft"] .cbui-dropdown-panel:not(.cbui-dropdown-panel-full) {
    --cbui-dd-item-radius: var(--radius-sm, 0.375rem);
    --cbui-dd-panel-px: 0.25rem;
    --cbui-dd-panel-py: 0.25rem;
    --cbui-dd-col-px: 0.25rem;
    --cbui-dd-col-py: 0.25rem;

    > .cbui-dropdown-sub {
      --cbui-dd-item-radius: var(--radius-sm, 0.375rem);
    }
  }
  /* Soft: panel-level inset padding — single-flow only. */
  [data-flavor="soft"] .cbui-dropdown-panel:not(.cbui-dropdown-panel-full):not(:has(> .cbui-dropdown-col)):not(.cbui-dropdown-panel-columns) {
    padding-inline: 0.25rem;
    padding-block: 0.25rem;
  }
  [data-flavor="soft"] .cbui-dropdown-panel:not(.cbui-dropdown-panel-full).cbui-dropdown-panel-columns {
    --cbui-dd-panel-px: 0px;
    padding-block: 0.25rem;
  }

  /* Sharp: zero radius */
  [data-flavor="sharp"] .cbui-dropdown-panel {
    border-radius: 0;
    --cbui-dd-panel-radius: 0;
  }

  /* Soft: slightly larger radius */
  [data-flavor="soft"] .cbui-dropdown-panel {
    --cbui-dd-panel-radius: var(--radius-lg, 0.75rem);
  }

  /* Clean: no shadow */
  [data-flavor="clean"] .cbui-dropdown-panel {
    --cbui-dd-panel-shadow: none;
  }

}

/* cbui/components/empty-state */
/* cbui-empty-state — placeholder for empty data views.
 *
 * Variants: default | subtle | bordered | dashed
 * Tones:    neutral | primary | accent | success | danger | warning | info | muted (default)
 * Sizes:    sm (inside lists) | md (default) | page (full-page)
 */

@layer components {

  :where(.cbui-empty-state) {
    --cbui-empty-pad-y: 2.5rem;
    --cbui-empty-pad-x: 1.5rem;
    --cbui-empty-icon-size: 2.5rem;
    --cbui-empty-title-size: var(--font-size-lg, 1.125rem);
    --cbui-empty-desc-size: var(--font-size-md, 1rem);
    --cbui-empty-accent: var(--color-text-tertiary);
  }

  .cbui-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding-block: var(--cbui-empty-pad-y);
    padding-inline: var(--cbui-empty-pad-x);
    border-radius: var(--card-radius, 0.75rem);
    border: 1px solid transparent;
    background-color: transparent;
    color: var(--color-text-secondary);
  }

  /* Sizes */
  .cbui-empty-state-size-sm {
    --cbui-empty-pad-y: 1.25rem;
    --cbui-empty-pad-x: 1rem;
    --cbui-empty-icon-size: 1.75rem;
    --cbui-empty-title-size: var(--font-size-md, 1rem);
    --cbui-empty-desc-size: var(--font-size-md, 1rem);
  }
  .cbui-empty-state-size-md {
    --cbui-empty-pad-y: 2.5rem;
    --cbui-empty-pad-x: 1.5rem;
  }
  .cbui-empty-state-size-page {
    --cbui-empty-pad-y: 4rem;
    --cbui-empty-pad-x: 2rem;
    --cbui-empty-icon-size: 3.5rem;
    --cbui-empty-title-size: var(--font-size-xl, 1.25rem);
    --cbui-empty-desc-size: var(--font-size-md, 1rem);
  }

  /* Inner pieces */
  .cbui-empty-state-icon {
    color: var(--cbui-empty-accent);
    font-size: var(--cbui-empty-icon-size);
    line-height: 1;
    margin-bottom: 0.75rem;
    display: inline-flex;
  }
  .cbui-empty-state-icon-svg { width: 1em; height: 1em; }

  .cbui-empty-state-title {
    font-size: var(--cbui-empty-title-size);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--color-text);
    margin: 0 0 0.375rem;
    line-height: 1.3;
  }
  .cbui-empty-state-description {
    font-size: var(--cbui-empty-desc-size);
    color: var(--color-text-secondary);
    margin: 0 0 1.25rem;
    max-width: 32rem;
    line-height: 1.5;
  }
  .cbui-empty-state-actions {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    justify-content: center;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Tones — drive the accent (icon) colour                              */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-empty-state-tone-neutral { --cbui-empty-accent: var(--color-text-tertiary); }
  .cbui-empty-state-tone-muted   { --cbui-empty-accent: var(--color-text-tertiary); }
  .cbui-empty-state-tone-primary { --cbui-empty-accent: var(--color-primary); }
  .cbui-empty-state-tone-accent  { --cbui-empty-accent: var(--color-accent); }
  .cbui-empty-state-tone-success { --cbui-empty-accent: var(--color-success); }
  .cbui-empty-state-tone-danger  { --cbui-empty-accent: var(--color-danger); }
  .cbui-empty-state-tone-warning { --cbui-empty-accent: var(--color-warning); }
  .cbui-empty-state-tone-info    { --cbui-empty-accent: var(--color-info); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Variants                                                             */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-empty-state-default {
    /* No chrome — pure typography + icon. */
  }
  .cbui-empty-state-subtle {
    background-color: var(--color-bg-subtle);
  }
  .cbui-empty-state-bordered {
    border-color: var(--color-border);
  }
  .cbui-empty-state-dashed {
    border-style: dashed;
    border-color: var(--color-border-strong, var(--color-border));
  }

  /* Tone tinting on chrome variants (subtle/bordered/dashed) */
  .cbui-empty-state-subtle.cbui-empty-state-tone-primary { background-color: color-mix(in srgb, var(--color-primary) 8%, transparent); }
  .cbui-empty-state-subtle.cbui-empty-state-tone-accent  { background-color: color-mix(in srgb, var(--color-accent)  8%, transparent); }
  .cbui-empty-state-subtle.cbui-empty-state-tone-success { background-color: color-mix(in srgb, var(--color-success) 8%, transparent); }
  .cbui-empty-state-subtle.cbui-empty-state-tone-danger  { background-color: color-mix(in srgb, var(--color-danger)  8%, transparent); }
  .cbui-empty-state-subtle.cbui-empty-state-tone-warning { background-color: color-mix(in srgb, var(--color-warning) 8%, transparent); }
  .cbui-empty-state-subtle.cbui-empty-state-tone-info    { background-color: color-mix(in srgb, var(--color-info)    8%, transparent); }

  .cbui-empty-state-bordered.cbui-empty-state-tone-primary { border-color: color-mix(in srgb, var(--color-primary) 35%, var(--color-border)); }
  .cbui-empty-state-bordered.cbui-empty-state-tone-accent  { border-color: color-mix(in srgb, var(--color-accent)  35%, var(--color-border)); }
  .cbui-empty-state-bordered.cbui-empty-state-tone-success { border-color: color-mix(in srgb, var(--color-success) 35%, var(--color-border)); }
  .cbui-empty-state-bordered.cbui-empty-state-tone-danger  { border-color: color-mix(in srgb, var(--color-danger)  35%, var(--color-border)); }
  .cbui-empty-state-bordered.cbui-empty-state-tone-warning { border-color: color-mix(in srgb, var(--color-warning) 35%, var(--color-border)); }
  .cbui-empty-state-bordered.cbui-empty-state-tone-info    { border-color: color-mix(in srgb, var(--color-info)    35%, var(--color-border)); }

  .cbui-empty-state-dashed.cbui-empty-state-tone-primary { border-color: color-mix(in srgb, var(--color-primary) 45%, var(--color-border)); }
  .cbui-empty-state-dashed.cbui-empty-state-tone-accent  { border-color: color-mix(in srgb, var(--color-accent)  45%, var(--color-border)); }
  .cbui-empty-state-dashed.cbui-empty-state-tone-success { border-color: color-mix(in srgb, var(--color-success) 45%, var(--color-border)); }
  .cbui-empty-state-dashed.cbui-empty-state-tone-danger  { border-color: color-mix(in srgb, var(--color-danger)  45%, var(--color-border)); }
  .cbui-empty-state-dashed.cbui-empty-state-tone-warning { border-color: color-mix(in srgb, var(--color-warning) 45%, var(--color-border)); }
  .cbui-empty-state-dashed.cbui-empty-state-tone-info    { border-color: color-mix(in srgb, var(--color-info)    45%, var(--color-border)); }
}

/* cbui/components/form */
/* CBUI Form scaffolding — label, help, error, row.
 *
 * These are the structural primitives that hold form controls together. They
 * are intentionally *unopinionated about which control they wrap* — Input,
 * Textarea, Select, Switch, Checkbox, anything — so the same row layout
 * works across the whole form-component vocabulary.
 *
 * Used both directly in templates (Stimulus-driven client-side forms) and
 * by the CBUI Symfony form-theme, which maps the form_row / form_label /
 * form_help / form_errors blocks onto these components.
 */

@layer components {
  /* ──────────────────────────────────────────────────────────────────────
   * FormLabel
   *
   * Type never renders below --font-size-md: a form label is primary content,
   * not a caption. sm, md and lg all read at md, so labels match the
   * surrounding page text; `size` is kept as an API but steers nothing here.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-form-label {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    color: var(--color-text);
    font-weight: var(--font-weight-medium, 500);
    line-height: 1.4;
  }

  .cbui-form-label-size-sm { font-size: var(--font-size-md, 1rem); }
  .cbui-form-label-size-md { font-size: var(--font-size-md, 1rem); }
  .cbui-form-label-size-lg { font-size: var(--font-size-md, 1rem); }

  .cbui-form-label-tone-danger {
    color: var(--color-text-danger);
  }

  /* The asterisk on required fields stays subtle — it's a marker, not an
     attention-grabber. */
  .cbui-form-label-marker {
    color: var(--color-text-danger);
    margin-inline-start: 0.125rem;
    font-weight: var(--font-weight-medium, 500);
  }

  .cbui-form-label-optional {
    color: var(--color-text-tertiary);
    /* Same size as the label (type never renders below normal); the
       tertiary colour and normal weight keep the hint subordinate. */
    font-weight: var(--font-weight-normal, 400);
    margin-inline-start: 0.25rem;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * FormHelp — the canonical "Secondary text" pattern from the Typography
   * reference: font-size-md + color-text-secondary. No size cascade —
   * help text is always the same size; if you need a non-secondary
   * description, you don't need FormHelp, you need a paragraph.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-form-help {
    margin: 0;
    font-size: var(--font-size-md, 1rem);
    line-height: var(--line-height-base, 1.5);
    color: var(--color-text-secondary);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * FormError — secondary text in danger colour. Same font-size as
   * FormHelp so the two read as a pair of equally-weighted hints below
   * the control, only distinguished by their colour + the warning glyph.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-form-error {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    margin: 0;
    color: var(--color-text-danger);
    font-size: var(--font-size-md, 1rem);
    line-height: var(--line-height-base, 1.5);
  }

  .cbui-form-error-line {
    display: flex;
    align-items: flex-start;
    gap: 0.375rem;
    margin: 0;
  }

  .cbui-form-error-icon {
    flex: 0 0 auto;
    width: 1em;
    height: 1em;
    /* Align the glyph with the cap-height of the first text line. */
    margin-top: 0.125em;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * FormRow — stacked layout for label + control + help + error.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-form-row {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }

  .cbui-form-row-gap-sm { gap: 0.375rem; }
  .cbui-form-row-gap-md { gap: 0.5rem; }
  .cbui-form-row-gap-lg { gap: 0.75rem; }

  .cbui-form-row-control {
    display: contents;
  }

  /* Inline layout — label sits left, control fills the rest. The help/error
     blocks span the full row below. Useful for settings-style "Setting name
     — control" pairs. */
  .cbui-form-row-inline {
    display: grid;
    grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
    align-items: center;
  }

  .cbui-form-row-inline > .cbui-form-help,
  .cbui-form-row-inline > .cbui-form-error {
    grid-column: 2;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Floating label layouts — two flavours under one mental model.
   *
   *   `floating`         → Label sits ON the input's top border when
   *                        floated, with a background patch masking the
   *                        border behind the text. Resting state:
   *                        centred inside the input.
   *   `floating-filled`  → Label stays INSIDE the input at all times.
   *                        Frame is taller (height override + extra
   *                        padding-top) to leave room for the floated
   *                        label without crowding the value.
   *
   * Both rely on `:placeholder-shown` to track the empty / filled state.
   * Input / Textarea default to `placeholder=" "` so the detection works
   * even when the caller didn't pass an explicit placeholder.
   *
   * Frame-scoped tokens (vs the inner control's `--cbui-input-pad-*`):
   * the absolutely-positioned label sits OUTSIDE the input's CSS-variable
   * cascade, so it cannot read the input's padding tokens directly. We
   * mirror them onto the frame and size-detect via `:has()`.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-form-row-floating,
  .cbui-form-row-floating-filled {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
  }

  .cbui-form-row-floating-frame {
    position: relative;
    display: block;
    /* Resting label x — matches input's padding-x so the text aligns
       with where the value sits. */
    --cbui-floating-pad-x:        0.75rem;
    /* Floated label x — for the on-border variant, the label moves ONTO
       the border. It needs to clear the radius corner curve, but should
       sit immediately past it (not far down the straight). Derived from
       `--input-radius` + a constant buffer so the *visual* distance to
       where the curve ends is consistent across flavors (Clean radius-md
       8px, Aero radius-md 12px, Soft radius-lg 20px would otherwise all
       look subtly different relative to their curve). */
    --cbui-floating-floated-start: calc(var(--input-radius, 0.5rem) + 0.375rem);
  }
  .cbui-form-row-floating-frame:has(.cbui-input-size-sm),
  .cbui-form-row-floating-frame:has(.cbui-textarea-size-sm) {
    --cbui-floating-pad-x: 0.5rem;
  }
  .cbui-form-row-floating-frame:has(.cbui-input-size-lg),
  .cbui-form-row-floating-frame:has(.cbui-textarea-size-lg) {
    --cbui-floating-pad-x: 1rem;
  }

  .cbui-form-row-floating-label {
    position: absolute;
    top: 50%;
    inset-inline-start: var(--cbui-floating-pad-x);
    transform: translateY(-50%);
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-tertiary);
    line-height: 1.2;
    pointer-events: none;
    transition: top 150ms ease, font-size 150ms ease, color 150ms ease, transform 150ms ease, background-color 150ms ease;
    background: transparent;
    /* Don't intercept clicks; the parent <label> handles them. */
  }

  .cbui-form-row-floating-marker {
    color: var(--color-text-danger);
    margin-inline-start: 0.125rem;
  }

  /* Focused-or-filled detection (shared by both floating variants). */
  .cbui-form-row-floating-frame:has(.cbui-input:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame:has(.cbui-textarea:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame:has(.cbui-input:not(:placeholder-shown)) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame:has(.cbui-textarea:not(:placeholder-shown)) .cbui-form-row-floating-label {
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
  }

  .cbui-form-row-floating-frame:has(.cbui-input:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame:has(.cbui-textarea:focus) .cbui-form-row-floating-label {
    color: var(--color-primary);
  }

  .cbui-form-row-floating-frame:has(.cbui-input-tone-danger) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame:has(.cbui-textarea-tone-danger) .cbui-form-row-floating-label {
    color: var(--color-text-danger);
  }

  /* ── Variant 1: floating (label on border) ───────────────────────────
   * The floated label sits ON the input's top border with a background
   * patch (--cbui-floating-label-bg, defaulting to the page bg) masking
   * the 1px border behind the text. Resting state centres the label
   * inside the input — same vertical position as the input's value.
   * Inputs don't need extra padding-top: the label is centred when
   * empty (which is where any typed text would also begin), so there's
   * no overlap. */
  .cbui-form-row-floating-frame-outlined {
    --cbui-floating-label-bg: var(--color-bg-base);
  }

  /* Filled-variant inputs use bg-subtle, so when the label sits on the
     border its mask must match the inner surface too. */
  .cbui-form-row-floating-frame-outlined:has(.cbui-input-filled),
  .cbui-form-row-floating-frame-outlined:has(.cbui-textarea-filled) {
    --cbui-floating-label-bg: var(--color-bg-subtle);
  }

  .cbui-form-row-floating-frame-outlined:has(.cbui-input:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame-outlined:has(.cbui-textarea:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame-outlined:has(.cbui-input:not(:placeholder-shown)) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame-outlined:has(.cbui-textarea:not(:placeholder-shown)) .cbui-form-row-floating-label {
    top: 0;
    /* Label centre aligns with the top border of the input — translateY
       moves it up by half its own height. */
    transform: translateY(-50%);
    /* Horizontal padding extends the bg-mask beyond the text so the
       border behind disappears cleanly. */
    padding-inline: 0.375rem;
    /* Push the label past the radius corner so it sits on the *straight*
       part of the border, not on the curve. Token is size-aware
       (sm/md/lg) and can be overridden per page if a theme uses a larger
       radius than the defaults. */
    inset-inline-start: var(--cbui-floating-floated-start);
    background-color: var(--cbui-floating-label-bg);
  }

  /* Textarea in outlined floating: anchor the resting label at the top
     of the textarea (centring is meaningless across multiple lines). */
  .cbui-form-row-floating-frame-outlined:has(.cbui-textarea) .cbui-form-row-floating-label {
    top: var(--cbui-input-pad-y, 0.5rem);
    transform: none;
  }
  .cbui-form-row-floating-frame-outlined:has(.cbui-textarea:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame-outlined:has(.cbui-textarea:not(:placeholder-shown)) .cbui-form-row-floating-label {
    top: 0;
    transform: translateY(-50%);
  }

  /* ── Variant 2: floating-filled (label inside) ───────────────────────
   *
   * The label always stays inside the input. The frame is deliberately
   * TALLER than a regular input — `box-sizing: border-box` is global in
   * CBUI, so just bumping padding-top wouldn't add height (it'd just
   * shrink the content area). We override `height` per size so the input
   * physically grows, then distribute the new vertical room as:
   *
   *   ┌─────────────────────────────────┐
   *   │  ↑ padding-top                  │  reserves the floated-label
   *   │    label-floated row           │  zone (label sits here when
   *   │  ↓                              │  focused / filled)
   *   │                                 │
   *   │  value text                     │  the actual text input row
   *   │                                 │
   *   │  ↑ small padding-bottom         │  visual balance
   *   └─────────────────────────────────┘
   *
   * The label-resting (empty) state stays centred via `top: 50%; trans-
   * lateY(-50%)` on the larger frame, so the empty field reads "label
   * inside an input" naturally.
   * ────────────────────────────────────────────────────────────────── */

  .cbui-form-row-floating-frame-filled > .cbui-input {
    /* Grow the input vertically just enough for the floated label and
       the value to coexist without crowding — ~6px gap between the
       label baseline and the value baseline. */
    height: calc(var(--cbui-input-height) + 1.25rem);
    padding-top: 1.625rem;
    padding-bottom: 0.25rem;
  }

  .cbui-form-row-floating-frame-filled > .cbui-textarea {
    /* Textareas size via `rows` × line-height, not via `height`. Just
       give them the same top-padding so the floated label has room. */
    padding-top: 1.625rem;
    padding-bottom: 0.25rem;
  }

  /* Size-aware tweaks — sm gets less extra growth, lg gets more. */
  .cbui-form-row-floating-frame-filled:has(.cbui-input-size-sm) > .cbui-input {
    height: calc(var(--cbui-input-height) + 1rem);
    padding-top: 1.375rem;
    padding-bottom: 0.25rem;
  }
  .cbui-form-row-floating-frame-filled:has(.cbui-textarea-size-sm) > .cbui-textarea {
    padding-top: 1.375rem;
    padding-bottom: 0.25rem;
  }

  .cbui-form-row-floating-frame-filled:has(.cbui-input-size-lg) > .cbui-input {
    height: calc(var(--cbui-input-height) + 1.5rem);
    padding-top: 1.875rem;
    padding-bottom: 0.375rem;
  }
  .cbui-form-row-floating-frame-filled:has(.cbui-textarea-size-lg) > .cbui-textarea {
    padding-top: 1.875rem;
    padding-bottom: 0.375rem;
  }

  /* Floated label — small top offset; the difference between label
     bottom and value top is ~5-6px which reads as a clear separation
     without leaving an unused vertical gap. */
  .cbui-form-row-floating-frame-filled:has(.cbui-input:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame-filled:has(.cbui-textarea:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame-filled:has(.cbui-input:not(:placeholder-shown)) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame-filled:has(.cbui-textarea:not(:placeholder-shown)) .cbui-form-row-floating-label {
    top: 0.375rem;
    transform: none;
  }

  /* Textarea resting: anchored near the top, not centred. */
  .cbui-form-row-floating-frame-filled:has(.cbui-textarea) .cbui-form-row-floating-label {
    top: 1.625rem;
    transform: none;
  }
  .cbui-form-row-floating-frame-filled:has(.cbui-textarea:focus) .cbui-form-row-floating-label,
  .cbui-form-row-floating-frame-filled:has(.cbui-textarea:not(:placeholder-shown)) .cbui-form-row-floating-label {
    top: 0.375rem;
  }

  /* Reduced motion: drop the transition so users who set the preference
     get an instant snap rather than a slide. */
  .a11y-reduced-motion .cbui-form-row-floating-label {
    transition: none;
  }
  @media (prefers-reduced-motion: reduce) {
    .cbui-form-row-floating-label {
      transition: none;
    }
  }

  /* HC: stronger contrast on the resting + floating colours. */
  .a11y-high-contrast .cbui-form-row-floating-label {
    color: var(--color-text);
    font-weight: var(--font-weight-medium, 500);
  }
  .a11y-high-contrast .cbui-form-row-floating-frame:has(.cbui-input:focus) .cbui-form-row-floating-label,
  .a11y-high-contrast .cbui-form-row-floating-frame:has(.cbui-textarea:focus) .cbui-form-row-floating-label {
    color: var(--color-primary);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * High contrast (a11y capability)
   *
   * The form scaffolding picks up the same a11y rule the rest of the
   * library follows: when High-Contrast is active, semantic colours go
   * solid (no color-mix transparency), text weight steps up, and the
   * required-marker grows to a more visible glyph weight.
   * ────────────────────────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-form-label             { font-weight: var(--font-weight-semibold, 600); }
  .a11y-high-contrast .cbui-form-label-tone-danger { color: var(--color-danger); }
  .a11y-high-contrast .cbui-form-label-marker      { color: var(--color-danger); font-weight: var(--font-weight-bold, 700); }
  .a11y-high-contrast .cbui-form-help              { color: var(--color-text); }
  .a11y-high-contrast .cbui-form-error             { color: var(--color-danger); font-weight: var(--font-weight-medium, 500); }
}


/* cbui/components/input */
/* cbui-input — single-line text input.
 *
 * Variant×Tone+Modifier grammar shared with Button/Badge/Alert/Card.
 *   - cbui-input             base reset
 *   - cbui-input-{variant}   default | filled | flush
 *   - cbui-input-size-{sm,md,lg}
 *   - cbui-input-tone-{primary|success|warning|danger|info}
 *
 * Focus model — per-variant by default:
 *   - default → border-color shifts to tone-focus + soft tone-mixed glow
 *   - filled  → bottom border thickens to a 2px tone-focus accent
 *   - flush   → bottom rule thickens to 2px in the tone-focus colour
 *
 * Optional opt-in modifier:
 *   - cbui-input-mod-focus-ring  replaces the variant's focus indicator
 *     with a gapped two-layer ring (composed via box-shadow). Themes /
 *     flavors that want a system-wide focus-ring can apply this modifier
 *     globally; HC mode also forces it.
 *
 *   - cbui-input-mod-focus-underline  add a 2px bottom accent in
 *     addition to whatever focus indicator already applies. Useful as
 *     an additional cue.
 */

@layer components {
  /* ──────────────────────────────────────────────────────────────────────
   * Base
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input {
    /* Size cascade. */
    --cbui-input-pad-x:    var(--input-padding-x-md);
    --cbui-input-pad-y:    var(--input-padding-y-md);
    --cbui-input-height:   var(--input-height-md);
    --cbui-input-fs:       var(--input-font-size-md);

    /* Tone cascade — overridden by .cbui-input-tone-*. */
    --cbui-input-tone:        var(--color-border);
    --cbui-input-tone-strong: var(--color-border-strong);
    --cbui-input-tone-focus:  var(--color-primary);
    --cbui-input-tone-ring:   var(--color-focus-ring, var(--color-primary));

    /* Focus indicator opacity used by the soft-glow recipe — bumped in
       dark mode (see below) so the glow stays visible on dark surfaces. */
    --cbui-input-glow-alpha: 35%;

    /* Focus ring geometry — used by the +focus-ring modifier and by HC. */
    --cbui-input-ring-gap:        2px;
    --cbui-input-ring-width:      2px;
    --cbui-input-ring-gap-color:  var(--color-bg-base);

    /* Flush surface — see flush variant below. */
    --cbui-input-flush-bg:    var(--color-bg-subtle);

    display: block;
    width: 100%;
    min-width: 0;
    height: var(--cbui-input-height);
    padding-inline: var(--cbui-input-pad-x);
    padding-block: var(--cbui-input-pad-y);
    font-family: inherit;
    font-size: var(--cbui-input-fs);
    line-height: 1.5;
    color: var(--color-text);
    background-color: transparent;
    border: 1px solid transparent;
    border-radius: var(--input-radius);
    appearance: none;
    -webkit-appearance: none;
    box-shadow: none;
    transition:
      border-color 150ms ease,
      background-color 150ms ease,
      box-shadow 150ms ease;
  }

  .cbui-input::placeholder {
    color: var(--color-text-tertiary);
    opacity: 1;
  }

  /* Native search-cancel/spinner reset. */
  .cbui-input[type="search"]::-webkit-search-decoration,
  .cbui-input[type="search"]::-webkit-search-cancel-button,
  .cbui-input[type="search"]::-webkit-search-results-button,
  .cbui-input[type="search"]::-webkit-search-results-decoration {
    appearance: none;
    -webkit-appearance: none;
  }

  .cbui-input:disabled,
  .cbui-input.cbui-input-disabled {
    opacity: 0.55;
    cursor: not-allowed;
  }

  .cbui-input:read-only:not([type="checkbox"]):not([type="radio"]),
  .cbui-input.cbui-input-readonly {
    cursor: default;
  }

  /* Dark mode focus carries weight through *two* layers that mirror the
     light-mode behaviour:
       1. A clearly visible focus border — works automatically now that
          --color-primary (and --color-focus-ring) invert to a light gray
          in dark mode, so the border-color shift is as visible as the
          light-mode dark-gray shift.
       2. A subtle glow on top — same role as in light, just inverted in
          colour. Alpha is bumped from 35% to 50% so the lifted ring
          colour still produces a visible halo without overpowering the
          border-colour shift. The lighter ring colour means we don't
          need to push opacity higher — 70% was overcompensation. */
  .dark .cbui-input,
  [data-scheme="dark"] .cbui-input {
    --cbui-input-glow-alpha: 50%;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Sizes
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input-size-sm {
    --cbui-input-pad-x:  var(--input-padding-x-sm);
    --cbui-input-pad-y:  var(--input-padding-y-sm);
    --cbui-input-height: var(--input-height-sm);
    --cbui-input-fs:     var(--input-font-size-sm);
  }

  .cbui-input-size-md {
    --cbui-input-pad-x:  var(--input-padding-x-md);
    --cbui-input-pad-y:  var(--input-padding-y-md);
    --cbui-input-height: var(--input-height-md);
    --cbui-input-fs:     var(--input-font-size-md);
  }

  .cbui-input-size-lg {
    --cbui-input-pad-x:  var(--input-padding-x-lg);
    --cbui-input-pad-y:  var(--input-padding-y-lg);
    --cbui-input-height: var(--input-height-lg);
    --cbui-input-fs:     var(--input-font-size-lg);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: default — surface + border. Focus = tone-focus border + soft
   * tone-mixed glow.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input-default {
    background-color: var(--color-bg-base);
    border-color: var(--cbui-input-tone);
  }

  .cbui-input-default:hover:not(:disabled):not(:focus) {
    border-color: var(--cbui-input-tone-strong);
  }

  .cbui-input-default:focus,
  .cbui-input-default:focus-visible,
  .cbui-input-default[data-cbui-demo-state="focused"] {
    outline: none;
    border-color: var(--cbui-input-tone-focus);
    box-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--cbui-input-tone-ring) var(--cbui-input-glow-alpha), transparent);
  }

  .cbui-input-default:disabled,
  .cbui-input-default.cbui-input-disabled {
    background-color: var(--color-bg-subtle);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: filled — bg-subtle, no top/side border. Focus reveals a 2px
   * bottom accent in the tone-focus colour.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input-filled {
    background-color: var(--color-bg-subtle);
    border-color: transparent;
    /* Reserve room for the 2px focus accent so the value doesn't jump. */
    border-bottom: 2px solid transparent;
    padding-bottom: calc(var(--cbui-input-pad-y) - 1px);
  }

  .cbui-input-filled:hover:not(:disabled):not(:focus) {
    background-color: var(--color-bg-muted);
  }

  .cbui-input-filled:focus,
  .cbui-input-filled:focus-visible,
  .cbui-input-filled[data-cbui-demo-state="focused"] {
    outline: none;
    background-color: var(--color-bg-subtle);
    border-bottom-color: var(--cbui-input-tone-focus);
    box-shadow: none;
  }

  .cbui-input-filled:disabled,
  .cbui-input-filled.cbui-input-disabled {
    background-color: var(--color-bg-subtle);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: flush — themed surface + bottom rule.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input-flush {
    background-color: var(--cbui-input-flush-bg);
    border-color: transparent;
    border-bottom: 1px solid var(--cbui-input-tone);
    border-radius: 0;
    padding-inline: 0;
    padding-bottom: calc(var(--cbui-input-pad-y) - 1px);
  }

  .cbui-input-flush:hover:not(:disabled):not(:focus) {
    border-bottom-color: var(--cbui-input-tone-strong);
  }

  .cbui-input-flush:focus,
  .cbui-input-flush:focus-visible,
  .cbui-input-flush[data-cbui-demo-state="focused"] {
    outline: none;
    border-bottom-width: 2px;
    border-bottom-color: var(--cbui-input-tone-focus);
    padding-bottom: calc(var(--cbui-input-pad-y) - 2px);
    box-shadow: none;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Modifier: +focus-ring (opt-in for flavors / themes)
   *
   * Replaces the variant's per-variant focus indicator with a gapped two-
   * layer ring composed via box-shadow. Composes with the comma grammar:
   *   variant="default+focus-ring,danger"
   * Themes that want a system-wide focus-ring can either apply this
   * modifier on every Input or override the base focus CSS in their
   * `variants/X.css` (see HC below for the override pattern).
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input.cbui-input-mod-focus-ring:focus,
  .cbui-input.cbui-input-mod-focus-ring:focus-visible,
  .cbui-input.cbui-input-mod-focus-ring[data-cbui-demo-state="focused"] {
    outline: none;
    box-shadow:
      0 0 0 var(--cbui-input-ring-gap) var(--cbui-input-ring-gap-color),
      0 0 0 calc(var(--cbui-input-ring-gap) + var(--cbui-input-ring-width)) var(--cbui-input-tone-ring);
  }

  /* Keep the variant's intrinsic accent (filled underline, flush thick
     bottom) when the ring modifier is composed on top — the ring is the
     loud cue, the per-variant accent is the secondary affordance. */
  .cbui-input-filled.cbui-input-mod-focus-ring:focus,
  .cbui-input-filled.cbui-input-mod-focus-ring:focus-visible {
    border-bottom-color: var(--cbui-input-tone-focus);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Modifier: +focus-underline (opt-in additional accent)
   *
   * Adds a 2px bottom accent on focus, regardless of variant. Useful on
   * the default variant when a flavor wants more emphasis without the
   * full ring. Composes alongside the default soft glow.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input.cbui-input-mod-focus-underline {
    border-bottom: 2px solid transparent;
    padding-bottom: calc(var(--cbui-input-pad-y) - 1px);
  }

  .cbui-input.cbui-input-mod-focus-underline:focus,
  .cbui-input.cbui-input-mod-focus-underline:focus-visible,
  .cbui-input.cbui-input-mod-focus-underline[data-cbui-demo-state="focused"] {
    border-bottom-color: var(--cbui-input-tone-focus);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Tones
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input-tone-primary {
    --cbui-input-tone:        color-mix(in srgb, var(--color-primary) 55%, var(--color-border));
    --cbui-input-tone-strong: var(--color-primary);
    --cbui-input-tone-focus:  var(--color-primary);
    --cbui-input-tone-ring:   var(--color-primary);
  }
  .cbui-input-tone-success {
    --cbui-input-tone:        color-mix(in srgb, var(--color-success) 55%, var(--color-border));
    --cbui-input-tone-strong: var(--color-success);
    --cbui-input-tone-focus:  var(--color-success);
    --cbui-input-tone-ring:   var(--color-success);
  }
  .cbui-input-tone-warning {
    --cbui-input-tone:        color-mix(in srgb, var(--color-warning) 60%, var(--color-border));
    --cbui-input-tone-strong: var(--color-warning);
    --cbui-input-tone-focus:  var(--color-warning);
    --cbui-input-tone-ring:   var(--color-warning);
  }
  .cbui-input-tone-danger {
    --cbui-input-tone:        var(--color-danger);
    --cbui-input-tone-strong: var(--color-danger);
    --cbui-input-tone-focus:  var(--color-danger);
    --cbui-input-tone-ring:   var(--color-danger);
  }
  .cbui-input-tone-info {
    --cbui-input-tone:        color-mix(in srgb, var(--color-info) 55%, var(--color-border));
    --cbui-input-tone-strong: var(--color-info);
    --cbui-input-tone-focus:  var(--color-info);
    --cbui-input-tone-ring:   var(--color-info);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * High contrast (a11y capability)
   *
   * HC always forces the gapped ring as the focus indicator (regardless
   * of variant), with full-strength colour and a wider ring. The default
   * variant's soft glow is replaced; the filled / flush bottom accent
   * stays on top of the ring as a redundant cue.
   * ────────────────────────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-input {
    border-width: 2px;
    --cbui-input-tone:        var(--color-text);
    --cbui-input-tone-strong: var(--color-text);
    --cbui-input-tone-ring:   var(--color-text);
    --cbui-input-ring-width:  3px;
  }

  .a11y-high-contrast .cbui-input-tone-primary { --cbui-input-tone: var(--color-primary); --cbui-input-tone-strong: var(--color-primary); --cbui-input-tone-ring: var(--color-primary); --cbui-input-tone-focus: var(--color-primary); }
  .a11y-high-contrast .cbui-input-tone-success { --cbui-input-tone: var(--color-success); --cbui-input-tone-strong: var(--color-success); --cbui-input-tone-ring: var(--color-success); --cbui-input-tone-focus: var(--color-success); }
  .a11y-high-contrast .cbui-input-tone-warning { --cbui-input-tone: var(--color-warning); --cbui-input-tone-strong: var(--color-warning); --cbui-input-tone-ring: var(--color-warning); --cbui-input-tone-focus: var(--color-warning); }
  .a11y-high-contrast .cbui-input-tone-danger  { --cbui-input-tone: var(--color-danger);  --cbui-input-tone-strong: var(--color-danger);  --cbui-input-tone-ring: var(--color-danger);  --cbui-input-tone-focus: var(--color-danger); }
  .a11y-high-contrast .cbui-input-tone-info    { --cbui-input-tone: var(--color-info);    --cbui-input-tone-strong: var(--color-info);    --cbui-input-tone-ring: var(--color-info);    --cbui-input-tone-focus: var(--color-info); }

  /* Force the ring in HC regardless of variant (drops the default's soft
     glow and the filled/flush underline-only focus). */
  .a11y-high-contrast .cbui-input:focus,
  .a11y-high-contrast .cbui-input:focus-visible,
  .a11y-high-contrast .cbui-input[data-cbui-demo-state="focused"] {
    outline: none;
    box-shadow:
      0 0 0 var(--cbui-input-ring-gap) var(--cbui-input-ring-gap-color),
      0 0 0 calc(var(--cbui-input-ring-gap) + var(--cbui-input-ring-width)) var(--cbui-input-tone-ring);
  }

  .a11y-high-contrast .cbui-input-flush {
    background-color: var(--color-bg-subtle);
    border-bottom-width: 2px;
  }
  .a11y-high-contrast .cbui-input-flush:focus,
  .a11y-high-contrast .cbui-input-flush:focus-visible,
  .a11y-high-contrast .cbui-input-flush[data-cbui-demo-state="focused"] {
    border-bottom-width: 3px;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Width control
   *
   * Default behaviour is `width: 100%` (fills the form-row column). The
   * `width` prop on the Twig component opts into a fixed width or an
   * intrinsic auto-sized control. CSS-length values come through as
   * inline style; the named modes use these classes.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-input-w-full { width: 100%; }
  .cbui-input-w-auto { width: auto; }
}

/* cbui/components/list-controls */
/* cbui-list-controls — the leading control cluster of a Bringer menubar:
 * search field · sort · filter toggle.
 *
 * Extracted (S0.5 Batch C) from the `_listctl.html.twig` mock partials —
 * the sizing below is a direct port of `agents-mock.css` § "The control
 * cluster", generalised to the component's own class names. The search
 * field is the only elastic element; sort and filter stay intrinsically
 * sized so a narrowing bar yields the search field first.
 *
 * Anatomy:
 *   .cbui-list-controls               flex row, the cluster itself
 *     .cbui-list-controls-search        the <SearchField> (elastic)
 *     .cbui-list-controls-sort          the sort Dropdown wrapper (intrinsic)
 *     .cbui-list-controls-filter        the filter toggle wrapper (intrinsic)
 *       .cbui-list-controls-filter-count  active-facet count pill, corner-anchored
 */

@layer components {
  .cbui-list-controls {
    display: inline-flex;
    align-items: center;
    gap: var(--cbui-action-gap, 0.625rem); /* search · sort · filter are neighbouring actions (Matthias 2026-10-29) */
    min-width: 0;
  }

  .cbui-list-controls-search {
    flex: 1 1 15rem;
    width: 15rem;
    min-width: 11rem;
    max-width: 22rem;
    container-type: inline-size;
    container-name: cbui-list-controls-search;
  }

  /* Drop the placeholder, never clip it, once the field is squeezed below
     its own readable width — the magnifier carries the meaning and the
     field stays typable. The accessible name comes from the attribute
     (or the `label` prop), not from the painted pseudo-element. */
  @container cbui-list-controls-search (max-width: 10.75rem) {
    .cbui-list-controls-search .cbui-input::placeholder {
      color: transparent;
    }
  }

  /* searchWidth="wide": the field for a long placeholder (about 17rem with the icon
     padding). Still shrinks down to its own floor; the cluster keeps a floor of its
     own so the host's overflow controller, not the flexbox, decides what leaves. */
  /* CB-1929: the floor is the cluster's real minimum, not the field's alone: the field's
     11rem + sort 2.5rem + filter 2.5rem + two 0.625rem action gaps = 17.25rem. With 12rem the
     cluster could be squeezed below its own content and sort/filter then ran over
     whatever sat next to it (the view toggle). */
  .cbui-list-controls--search-wide {
    min-width: 17.25rem;
  }

  .cbui-list-controls--search-wide .cbui-list-controls-search {
    flex-basis: 18rem;
    width: 18rem;
  }

  /* Compact form (host bar: `ui-component--overflow`, target `compact` on the cluster).
     The search field becomes its magnifier: a square button the width of its neighbours, the
     text hidden. Focus (a click on the button) opens the field again as an OVERLAY — the box in
     the flow stays 2.5rem wide, so typing never re-lays out the bar. The query survives; the
     placeholder and the clear button stay out of the compact box. */
  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search {
    flex: none;
    width: 2.5rem;
    min-width: 2.5rem;
    max-width: 2.5rem;
  }

  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search .cbui-search-field-input {
    padding-inline: 0;
    color: transparent;
    caret-color: transparent;
    cursor: pointer;
    transition: none;
  }

  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search .cbui-search-field-input::placeholder {
    color: transparent;
  }

  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search .cbui-search-field-clear {
    display: none;
  }

  /* The magnifier sits ON the input; it must not take the click that focuses it. */
  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search .cbui-search-field-icon {
    pointer-events: none;
  }

  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search:focus-within {
    z-index: 5;
  }

  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search:focus-within .cbui-search-field-input {
    width: 15rem;
    padding-inline: var(--cbui-search-field-gutter);
    color: inherit;
    caret-color: auto;
    cursor: text;
    background-color: var(--color-surface, var(--color-bg));
    box-shadow: var(--shadow-md);
  }

  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search:focus-within .cbui-search-field-input::placeholder {
    color: var(--color-text-secondary);
  }

  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search:focus-within .cbui-search-field-clear:not([hidden]) {
    display: inline-flex;
    inset-inline-end: calc(-15rem + 2.5rem + 1px);
    z-index: 6;
  }

  /* While the compact search field is open it lies over the cluster and its neighbours. They are
     hidden, not covered (visibility keeps the bar from re-laying out); they come back when the
     field folds in. Cluster-internal siblings first, then the siblings after the cluster. */
  .cbui-list-controls[data-overflow-compacted] .cbui-list-controls-search:focus-within ~ * {
    visibility: hidden;
  }

  .cbui-list-controls[data-overflow-compacted]:has(.cbui-list-controls-search:focus-within) ~ * {
    visibility: hidden;
  }

  .cbui-list-controls-sort {
    flex: none;
  }

  .cbui-list-controls-filter {
    position: relative;
    display: inline-flex;
    flex: none;
  }

  .cbui-list-controls-filter-count {
    position: absolute;
    inset-block-start: -0.25rem;
    inset-inline-end: -0.25rem;
    pointer-events: none;
  }

  .cbui-list-controls-filter-count[hidden] {
    display: none;
  }

  /* CB-2610 - the sentence at the top of the sort menu says who decides the order; it only
     shows while the menu is locked. Hidden through the cascade, not the hidden attribute:
     a display rule would beat the attribute. */
  .cbui-list-controls-sort:not([data-cbui-sort-locked="true"]) .cbui-list-controls-sort-note {
    display: none;
  }
}

/* cbui/components/list */
/* cbui-list — structured list. Visual language deliberately mirrors the
 * Dropdown menu-item recipe and the Bringer navigation- / list-column pattern.
 *
 * Token model (mirrors dropdown.css):
 *   --cbui-list-item-px       inline item padding (size token)
 *   --cbui-list-item-py       block item padding  (size + density)
 *   --cbui-list-item-gap      gap between item anatomy parts
 *   --cbui-list-item-fs       item font-size       (size token)
 *   --cbui-list-item-icon     item icon-box size   (size token)
 *   --cbui-list-item-radius   item corner radius  — 0px (full) / radius-sm (inset)
 *   --cbui-list-panel-px      inline panel padding — 0px (full) / 0.25rem (inset)
 *   --cbui-list-panel-py      block panel padding  — 0px (full) / 0.25rem (inset)
 *   --cbui-list-sep-inset     visual gap from divider tip to the panel border
 *   --cbui-list-divider-inset computed inset for the divider bar
 *
 *   item_style: full   — hover/active surface spans the full row; edge items
 *                        still hug the container's inner radius. Dividers
 *                        run edge to edge.
 *   item_style: inset  — items sit in an inline-padding bracket, rounded
 *                        corners; the divider BAR is inset to leave a uniform
 *                        gap to the panel border. CRITICALLY the row itself
 *                        is NOT shifted — the divider is a pseudo-element,
 *                        so every item starts and ends at the same x.
 *   Flavor default     — Aero + Soft → inset, Clean / Sharp / Glass → full.
 *
 *   interactive: true  — items show a hover surface (menu-style list).
 *   interactive: false — no hover; only active / selected rows are tinted
 *                        (the VaultBringer navigation- / list-column pattern).
 *
 *   size: sm / md / lg — proportional scale (padding + font-size + icon-size).
 *                        Mirrors Dropdown's panel-sm / -md / -lg sizing.
 *                        Composes with `density` (which only tunes py).
 *
 * ROW HIERARCHY — the one rule every list row obeys (Matthias, 2026-09-03)
 * ------------------------------------------------------------------------
 * „Es muss eine Hauptinformation geben im ListItem. Diese Hauptinformation muss
 *  in normaler Schriftgroesse sein -- nie sm oder xs. Das ist eine Regel die
 *  immer gelten muss."
 *
 * A row is ABOUT one thing -- a name, a key, a title. That thing is
 * `.cbui-list-item-label`. Type never renders below `--font-size-md` (1rem):
 * the label AND the supporting line (`.cbui-list-item-description`) are both
 * floored there, so `size="sm"` can no longer shrink either of them. The tier
 * keeps every other effect it had (padding, icon box, gap), which is what makes
 * a dense list dense.
 *
 * The floor is written as `max(--cbui-list-item-fs, --font-size-md)`, not as a
 * flat `--font-size-md`: `size="lg"` may still raise the type, `size="sm"` can
 * no longer lower it.
 *
 * Rank therefore comes from WEIGHT and GREY only: the label is medium weight in
 * the text colour, the description and the trailing meta cell
 * (`.cbui-list-item-secondary`) are secondary grey. Never from a smaller size --
 * 12px or 14px is not a rank, it is unreadable for the target audience.
 *
 * The full guideline, including where badges may sit, is at
 * /_storybook/guidelines/list-row-hierarchy.
 */

.cbui-list {
    /* Default (size=md) — spacing matches the Dropdown default menu-item
       recipe. The item FONT default is the base body size (16px) — list rows
       are meaningful content, so they never read below base by default;
       sub-base item type is reserved for the deliberately dense chrome scale
       (size=sm). */
    --cbui-list-item-px:     0.75rem;
    --cbui-list-item-py:     0.5rem;
    --cbui-list-item-gap:    0.5rem;
    --cbui-list-item-fs:     var(--font-size-md, 1rem);
    --cbui-list-item-icon:   1rem;
    --cbui-list-item-radius: 0px;
    --cbui-list-panel-px:    0px;
    --cbui-list-panel-py:    0px;
    --cbui-list-sep-inset:   0.625rem;
    --cbui-list-divider-inset: 0px;
    --cbui-list-radius:      var(--radius-md, 0.5rem);

    /* Surfaces — match Dropdown's item recipe exactly. */
    --cbui-list-item-hover-bg:  var(--color-bg-subtle);
    --cbui-list-item-press-bg:  var(--color-bg-muted);
    /* Active = the "current" row. Primary wash, stronger than selected so the
       two states differ at a glance. */
    --cbui-list-item-active-bg: color-mix(in srgb, var(--color-primary) 14%, transparent);
    /* Selected = a row the user picked (checkbox / multi-select). Quiet
       neutral tint, no primary hue — visually clearly distinct from active. */
    --cbui-list-item-selected-bg: color-mix(in srgb, currentColor 8%, transparent);

    margin: 0;
    padding: var(--cbui-list-panel-py) var(--cbui-list-panel-px);
    list-style: none;
    color: var(--color-text);
}

[data-scheme="dark"] .cbui-list,
.dark .cbui-list {
    --cbui-list-item-hover-bg: var(--color-bg-muted);
    --cbui-list-item-press-bg: var(--color-bg-strong, var(--color-bg-muted));
    --cbui-list-item-active-bg: color-mix(in srgb, var(--color-primary) 26%, transparent);
    --cbui-list-item-selected-bg: color-mix(in srgb, currentColor 11%, transparent);
}

.cbui-list-row {
    position: relative;
    display: block;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Trailing per-row actions (e.g. edit / delete). The model (Matthias 17.07.):
   the actions live INSIDE the item and appear on the ACTIVE (selected) row and
   the hovered row — never behind their own little background strip. So:

   - Actions are IN FLOW at the trailing edge and hidden (display:none) by
     default, which means they take NO space and the label uses the FULL column
     width. They reveal only on hover / focus-within / on the active row; there,
     the label truncates to leave room for them (no overlay, no text under icons,
     no gradient).
   - The ACTIVE row carries a FULL-WIDTH background wash that spans content AND
     actions as one item. Plain hover paints NO background — only the active row
     is tinted (so hover just reveals the actions, it doesn't wash the row). */
.cbui-list-row-has-actions {
    display: flex;
    align-items: center;
    border-radius: var(--cbui-list-item-radius);
}
.cbui-list-row-has-actions > .cbui-list-item {
    flex: 1 1 auto;
    min-width: 0;
    width: auto;
}
.cbui-list-item-actions {
    display: none;
    flex: 0 0 auto;
    align-items: center;
    /* The one action gap (10px) between icons so adjacent touch targets never fuse. Exposed as a
     * token — `table.css`'s `.cbui-table-actions-cell` min-width (CB-1722)
     * reads it to reserve the same space without duplicating the literal. */
    --cbui-list-item-actions-gap: var(--cbui-action-gap, 0.625rem); /* Matthias 2026-10-29: every neighbouring action stands 10px apart */
    gap: var(--cbui-list-item-actions-gap);
    padding-inline-end: var(--cbui-list-item-px, 0.75rem);
}
/* ── The "…" trigger of a folded action bar (CB-1954, Nacharbeit 2) ──────────
 * Matthias, 2026-10-02: the kebab needs a background that is ALWAYS there, with a clear
 * contrast to the surface behind it — not only on hover, not muted. It was the neutral
 * `ghost+bg-fill` of the button, whose light fill (`--color-bg-subtle`) is the very colour
 * of a table cell (measured contrast 1.00) and whose dark fill is a 10 % overlay (1.36).
 * One token, one overlay of the text colour: it darkens a light surface and lifts a dark one,
 * so it reads on a table cell, on a card and on a plain list row alike. Raise the percentage
 * here, never per consumer. The selector is as specific as the dark-mode fill rules of the
 * button (four classes) so that it wins in both schemes without `!important`. */
.cbui-button.cbui-list-item-actions-more.cbui-button-mod-bg-fill.cbui-button-tone-neutral {
    /* Opaque (Matthias 2026-10-31): the veil lies on the surface of the row (`--cbui-list-item-actions-base`, a host may set it to
     * the tint of a row), so the text under the button never shines through, and the button lies ABOVE its neighbours. */
    --cbui-list-item-actions-more-bg: color-mix(in srgb, currentColor 18%, var(--cbui-list-item-actions-base, var(--color-surface, var(--color-bg-base))));
    background-color: var(--cbui-list-item-actions-more-bg);
    position: relative;
    z-index: 2;
}
/* ── Overflowing actions (`actionsOverflow`) ────────────────────────────────
 * The width CAP is what makes priority-plus possible at all (CB-378). The
 * generic overflow controller decides by `scrollWidth > clientWidth` on this
 * box; an uncapped `flex: 0 0 auto` box always sizes to its content, so the two
 * are equal by definition and NOTHING ever collapses, however narrow the row
 * gets. That is why the `actionsOverflow` prop shipped inert: the cap its own
 * docblock promised (`--cbui-list-item-actions-max`) existed in prose only, in
 * no stylesheet.
 *
 * HALF the row (Matthias, 2026-08-01: „Das Overflow soll wirken sobald die
 * Buttons mehr als 50% der Breite einnehmen."). The box takes no space at all
 * while the row is at rest (`display: none`), so the label keeps the whole line
 * until somebody points at the row; the cap only decides how much the label gives
 * up for the moment the actions are on screen — and past half the row the actions
 * would be taking more of the line than the thing they act on.
 *
 * …and a SECOND, absolute cap: the label keeps at least
 * `--cbui-list-item-label-min` (140px) of the row, whatever the percentage
 * would allow. A percentage alone can never empty the bar: at the narrowest a
 * list pane can be dragged (200px, `H_MIN` in list_detail_controller), half the
 * row is 100px and one icon button plus the "…" trigger needs ~85px of it — so
 * the overflow controller reports „fits" with one action still sitting outside
 * the menu and stops there. Measured 2026-08-14 on the Values list: exactly one
 * action stayed on the row at every width from 260px down to the 200px minimum.
 * Matthias asked for the opposite („Es soll so erweitert werden, dass
 * letztendlich alle Action-Buttons im Kebab-Menü verschwinden bei der schmalsten
 * Größe"), and the honest way to say that is not a floor in the controller but a
 * reserve for the thing the row is ABOUT: at 200px the cap becomes 60px, which
 * holds the "…" trigger and nothing else.
 *
 * Measured after the change, same list: all six are in the menu at 220px and
 * below, one action returns at 230px, and from 280px up the 50% rule is the
 * narrower of the two again — 280px is where `100% - 140px` and `50%` cross, so
 * every width the old behaviour was tuned for is bit-for-bit unchanged (six
 * actions at 550px, four at 500px, three at 420px, two at 340px).
 *
 * NO `overflow: hidden` here, however tempting it looks for the one frame
 * between "revealed" and "recalculated": the "…" menu is a child of this box,
 * and clipping the box clips the open menu. (Its panel is a top-layer popover
 * and therefore escapes anyway — but a clip on the box would still cut the
 * trigger's own focus ring, and the next person to render something non-popover
 * in here would be debugging a "z-index bug" that is not one.) */
.cbui-list-item-actions-overflow {
    /* The floor is the "…" button plus the end padding of the row: in a very narrow list the label gives way (it truncates), the
     * button never sticks out of the row. */
    max-width: max(
        calc(var(--cbui-button-icon-size, 2.5rem) + var(--cbui-list-item-px, 0.75rem)),
        min(
            var(--cbui-list-item-actions-max, 50%),
            calc(100% - var(--cbui-list-item-label-min, 8.75rem))
        )
    );
    min-width: 0;
}
/* ── A row WITH badges gives the actions less ─────────────────────────────
 * 30 % instead of 50 % (Matthias, 2026-09-04):
 *
 *   „In den List-items nehmen die Badges zu viel Platz weg. Sie sind aber
 *    wichtig und sinnvoll, weil man sofort sieht, wo Handlungsbedarf ist. […]
 *    Ich glaube aktuell haben die Action-Buttons ca 50% der Breite Platz. Das
 *    sollten wir auf 30% reduzieren, damit mehr Platz ist für Titel und
 *    Beschreibung."
 *
 * The 50 % cap above was measured on a row whose trailing edge held nothing but
 * the actions. A badge column changes that arithmetic: it is `flex: none` — it
 * never yields — so on a badged row the label pays for the actions AND for the
 * badges, and it is the label that runs out. Dropping the cap to 30 % moves the
 * lowest-priority icons into the "…" menu EARLIER, which is the trade Matthias
 * asked for: the badge stays (it is the „wo ist Handlungsbedarf"-signal), the
 * actions become one click deeper, and the name stays readable.
 *
 * Keyed on the MARKUP, not on an opt-in prop, so it cannot drift: a row that
 * grows a badge later gets the tighter cap the same day. `:has()` on the row
 * covers both the CBUI helper (`.cbui-list-item-badge`) and a consumer's own
 * trailing badge column, because a `.cbui-badge` inside the row's own item is
 * what the rule is really about. The `-actions` box is a SIBLING of the item, so
 * the badge cannot be inside it — no self-match.
 *
 * The second cap (`--cbui-list-item-label-min`) is untouched and still wins on a
 * very narrow row, where it empties the bar completely. */
.cbui-list-row-has-actions:has(> .cbui-list-item .cbui-badge),
.cbui-list-row-has-actions:has(> .cbui-list-item .cbui-list-item-badge) {
    --cbui-list-item-actions-max: 30%;
}
/* The derived "…" menu. A row-action menu is a MENU: its width comes from the
   convention, not from its longest line. Left uncapped the panel sizes to
   content, and a single disabled entry carrying its one-sentence reason stretched
   it across the whole window. The cap also gives the reason somewhere to wrap —
   which is the point of putting it on a second line at all.

   14rem, not 20rem (Matthias, 2026-08-14: „Das Dropdown für die Item-Actions muss
   schmaler werden. Etwa gleich schmal wie das Sort-Dropdown. Aber so dass kein
   Action-Label umbricht."). The floor is not taste: an action LABEL may never
   wrap or be cut, so the cap has to clear the longest one plus the item chrome
   (icon + gaps + padding = 46px). Measured 2026-08-14 at the sm item type
   (14px): „Aufbewahrung bearbeiten" is the widest at 168px → 214px of panel.
   14rem = 224px leaves that 10px and lands next to the sort dropdown's 204px.
   Raise this only together with a fresh measurement of the widest label —
   lowering it below 13.5rem starts truncating. The HINT is unaffected and goes
   on wrapping, which is why it has its own line (dropdown.css § Item hint). */
/* Since 2026-10-31 (Matthias: „Das Item-Dropdown-Menü ist zu schmal. Es sollte nicht fixe Breite haben sondern so breit sein
   wie die Menü-Items es erfordern.") the panel takes the width of its longest label (`max-content`); the old 14rem cap cut
   labels such as „Nächste Nummern kopieren". The ceiling stays only as a guard against a runaway text (a disabled action's
   reason lives in its tooltip, not in its label, so it never widens the menu). */
.cbui-list-item-actions-menu {
    width: max-content;
    max-width: min(var(--cbui-list-item-actions-menu-max, 24rem), calc(100vw - 2rem));
}
/* Reveal the actions on hover / keyboard focus and always on the SELECTED row
   (`aria-selected`, i.e. the `selected` prop) — not on the ACTIVE one
   (`aria-current`). ListItem keeps those two props apart on purpose, and the
   docblock used to say „active row" for a rule that has always read
   aria-selected. Corrected on both sides on 2026-08-07 (CB-324).

   ⛔ These three selectors are deliberately NOT scoped under the
   `.cbui-list-actions-hover` class that ListGroup emits for
   `actionsReveal="hover"`. The actions box is `display: none` by default, so a
   scoped rule would hide the actions FOREVER on every ListItem that has no
   ListGroup ancestor — and ListItem is used standalone in row partials across
   at least seven bringers. The default behaviour must never depend on a class
   that was added later; `actionsReveal="always"` overrides by adding a rule,
   not by taking one away (CB-1328 A4). */
.cbui-list-row-has-actions:hover > .cbui-list-item-actions,
.cbui-list-row-has-actions:focus-within > .cbui-list-item-actions,
.cbui-list-row-has-actions:has(> .cbui-list-item[aria-selected="true"]) > .cbui-list-item-actions,
/* CB-2122: ListDetail rows that mark the open row with aria-current. */
.cbui-list-detail-list .cbui-list-row-has-actions:has(> .cbui-list-item[aria-current="true"]) > .cbui-list-item-actions {
    display: flex;
}
/* CB-1965: hold the box while a menu inside it is open — independent of hover and
   of the incidental focus on the menu's first item. A SEPARATE rule on purpose: in
   the selector list above, a browser without `:popover-open` would drop the whole
   rule and with it every reveal behaviour. */
.cbui-list-row-has-actions:has(> .cbui-list-item-actions :popover-open) > .cbui-list-item-actions {
    display: flex;
}
/* CB-1947: no hover on touch (tablet, phone) — a box that only appears on hover would never
   appear, so every row with actions shows them. */
@media (hover: none) {
    .cbui-list-row-has-actions > .cbui-list-item-actions {
        display: flex;
    }
}
/* `actionsReveal="always"` (ListGroup) — the actions box never hides. Same
   vocabulary as Tree.actionsReveal / FilePreview.actionsReveal (CB-1328 A4):
   for surfaces where hover/touch discovery of the row's actions is not
   acceptable (e.g. the Cockpit Vorgangs-Eintrag). */
.cbui-list-actions-always .cbui-list-item-actions {
    display: flex;
}
/* Selected row (`aria-selected`): full-width wash spanning content + actions,
   painted on the ROW so it covers the actions too. */
.cbui-list-interactive .cbui-list-row-has-actions:has(> .cbui-list-item[aria-selected="true"]),
.cbui-list-detail-list .cbui-list-interactive .cbui-list-row-has-actions:has(> .cbui-list-item[aria-current="true"]) {
    background-color: var(--cbui-list-item-active-bg);
}
/* The inner <a> never paints its OWN surface in an actions row — the row owns
   the wash. Without this, ListDetail's selected-row background
   (.cbui-list-detail-list .cbui-list-item[aria-selected]) would double up behind
   the title only, and plain hover would tint just the content. */
.cbui-list-interactive .cbui-list-row-has-actions > .cbui-list-item:hover,
.cbui-list-interactive .cbui-list-row-has-actions > .cbui-list-item:active,
.cbui-list-detail-list .cbui-list-row-has-actions > .cbui-list-item:is([aria-selected="true"], [aria-current="true"]),
.cbui-list-detail-list .cbui-list-row-has-actions > .cbui-list-item:is([aria-selected="true"], [aria-current="true"]):hover {
    background-color: transparent;
}
/* …and the same again for the two DARK channels, because the rule above did not
   reach them (Matthias, 2026-08-13: „Da liegen 2 Schichten mit unterschiedlichen
   Farben aufeinander").

   Why it was dark-only, measured on the Values versions list: list-detail.css
   paints the selected ITEM, and its light rule
   (.cbui-list-detail-list .cbui-list-item[aria-selected="true"]) scores 2 classes
   + 1 attribute — it LOSES to the neutraliser above (3 classes + 1 attribute), so
   light mode was always correct. The dark override adds `.dark` / `[data-scheme]`
   and thereby TIES at 3 classes + 1 attribute; the tie fell to source order, and
   list-detail.css loads last. So the item painted its own wash after all, on top
   of the row's — same colour, both at 26% alpha, which composites to ~45% behind
   the label and stays 26% behind the action icons. Two tones, one row.

   The fix is specificity, not order: adding the dark channel to the ROW-scoped
   selector puts it at 4 classes + 1 attribute, which wins wherever the files
   happen to land in the bundle. Fixing it here rather than in list-detail.css
   keeps the whole „in an actions row the ROW owns the wash" rule in one place —
   that is the invariant, and it should not be spread over two files.

   ⚠ This was already patched LOCALLY once, in
   bundles/value-store-bringer/assets/styles/values.css for `.cb-values-trash-list`
   („the item must stay transparent or the two washes stack"). That local patch is
   why the trash looked right while the versions list did not. It can go once this
   has been through a visual round; it is left standing for now because it also
   swaps the wash from primary to neutral, which is a separate decision. */
:root[data-color-scheme="dark"] .cbui-list-detail-list .cbui-list-row-has-actions > .cbui-list-item:is([aria-selected="true"], [aria-current="true"]),
:root[data-color-scheme="dark"] .cbui-list-detail-list .cbui-list-row-has-actions > .cbui-list-item:is([aria-selected="true"], [aria-current="true"]):hover,
[data-scheme="dark"] .cbui-list-detail-list .cbui-list-row-has-actions > .cbui-list-item:is([aria-selected="true"], [aria-current="true"]),
[data-scheme="dark"] .cbui-list-detail-list .cbui-list-row-has-actions > .cbui-list-item:is([aria-selected="true"], [aria-current="true"]):hover,
.dark .cbui-list-detail-list .cbui-list-row-has-actions > .cbui-list-item:is([aria-selected="true"], [aria-current="true"]),
.dark .cbui-list-detail-list .cbui-list-row-has-actions > .cbui-list-item:is([aria-selected="true"], [aria-current="true"]):hover {
    background-color: transparent;
}

/* Unread surface (`unread` + `unreadSurface`): the ROW paints the surface, so it carries content AND actions as one block
   (a wash on the content alone ends before the buttons and splits the row in two). Same colour as the active row. */
.cbui-list-row-unread-surface {
    background-color: var(--cbui-list-item-unread-bg, var(--cbui-list-item-active-bg));
    border-radius: var(--cbui-list-item-radius);
}

/* `actionsWrap`: the content takes the row up to a floor of 18rem; what does not fit next to it goes to its own line under
   the text, flush with the text, in full (no squeezing, no "…" while it fits). Wide rows keep the actions at the end. */
.cbui-list-row-actions-wrap {
    flex-wrap: wrap;
}
.cbui-list-row-actions-wrap > .cbui-list-item {
    flex: 1 1 18rem;
}
.cbui-list-row-actions-wrap > .cbui-list-item-actions {
    max-width: 100%;
    padding-inline-start: var(--cbui-list-item-px, 0.75rem);
    margin-block: 0.25rem;
}

/* ── An actions row: the ROW owns EVERY surface ──────────────────────────────
 * Matthias, 2026-11-02: "Der Hintergrund eines Items muss immer über das komplette Item gehen und darf nie vor den Actions
 * enden." The interactive element (`.cbui-list-item`) and the actions box are SIBLINGS in the row, so a state surface painted
 * on the item ends where the actions begin. In a row WITH actions the item therefore never paints its own surface; the row
 * paints it, from the item's state, over content AND actions: hover, pressed, active (and active + hover), selected, toned
 * hover. (`unread` + `unreadSurface` is on the row from the start, see above.) The selected / current row of a ListDetail list
 * is handled above and stays as it is; a ListDetail list has no hover chrome by design, so it gets none here either.
 * Measured by tests/ui-component/list-item-surface-reaches-actions.spec.ts (light and dark, Aero and Clean). */
.cbui-list .cbui-list-row-has-actions > .cbui-list-item.cbui-list-item,
.cbui-list .cbui-list-row-has-actions > .cbui-list-item.cbui-list-item:is(:hover, :active) {
    background-color: transparent;
}
.cbui-list-interactive .cbui-list-row-has-actions:hover:not(.cbui-list-detail-list *) {
    background-color: var(--cbui-list-item-hover-bg);
}
.cbui-list-interactive .cbui-list-row-has-actions:has(> .cbui-list-item:active):not(.cbui-list-detail-list *) {
    background-color: var(--cbui-list-item-press-bg);
}
.cbui-list .cbui-list-row-has-actions:has(> .cbui-list-item-selected:not(.cbui-list-item-active)) {
    background-color: var(--cbui-list-item-selected-bg);
}
.cbui-list .cbui-list-row-has-actions:has(> .cbui-list-item-active) {
    background-color: var(--cbui-list-item-active-bg);
}
.cbui-list-interactive .cbui-list-row-has-actions:has(> .cbui-list-item-active):hover:not(.cbui-list-detail-list *) {
    background-color: color-mix(in srgb, var(--cbui-list-item-active-bg) 80%, var(--color-text));
}
.cbui-list-active-style-solid .cbui-list-row-has-actions:has(> .cbui-list-item-active),
.cbui-list-row-has-actions:has(> .cbui-list-item-active.cbui-list-item-active-solid) {
    background-color: var(--color-primary);
}
.cbui-list-interactive .cbui-list-row-unread-surface:hover:not(.cbui-list-detail-list *) {
    background-color: color-mix(in srgb, var(--cbui-list-item-unread-bg, var(--cbui-list-item-active-bg)) 80%, var(--color-text));
}
.cbui-list-interactive .cbui-list-row-has-actions:has(> .cbui-list-item-tone-danger):hover:not(.cbui-list-detail-list *) {
    background-color: color-mix(in srgb, var(--color-danger) 9%, transparent);
}
.cbui-list-interactive .cbui-list-row-has-actions:has(> .cbui-list-item-tone-warning):hover:not(.cbui-list-detail-list *) {
    background-color: color-mix(in srgb, var(--color-warning) 9%, transparent);
}
.cbui-list-interactive .cbui-list-row-has-actions:has(> .cbui-list-item-tone-success):hover:not(.cbui-list-detail-list *) {
    background-color: color-mix(in srgb, var(--color-success) 9%, transparent);
}
.cbui-list-interactive .cbui-list-row-has-actions:has(> .cbui-list-item-tone-info):hover:not(.cbui-list-detail-list *) {
    background-color: color-mix(in srgb, var(--color-info) 9%, transparent);
}

/* ── Density tiers (vertical only — composes with size) ─────────────────── */
.cbui-list-density-compact     { --cbui-list-item-py: 0.375rem; }
.cbui-list-density-comfortable { --cbui-list-item-py: 0.5rem; }
.cbui-list-density-spacious    { --cbui-list-item-py: 0.75rem; }

/* ── Size scale — proportional (padding + font-size + icon size + gap).
 * Mirrors the Dropdown panel-sm / -md / -lg sizing. Density still tunes py
 * within the chosen size. Density rules below re-assert the size's py so
 * the user can pick either dimension independently. */
.cbui-list-size-sm {
    --cbui-list-item-px:   0.625rem;
    --cbui-list-item-py:   0.375rem;
    --cbui-list-item-gap:  0.4375rem;
    --cbui-list-item-fs:   var(--font-size-md, 1rem);
    --cbui-list-item-icon: 0.875rem;
}
/* lg keeps the base font (same as the md default) and only scales up the
   spacing + icon box — "generous" is a spacing tier, not a type tier. */
.cbui-list-size-lg {
    --cbui-list-item-px:   0.875rem;
    --cbui-list-item-py:   0.625rem;
    --cbui-list-item-gap:  0.625rem;
    --cbui-list-item-fs:   var(--font-size-md, 1rem);
    --cbui-list-item-icon: 1.125rem;
}
/* edge="block" (CB-2238): the item text starts exactly where the 1rem block padding of the surrounding detail
 * panel starts — panel padding 0, item padding 1rem — instead of the size tier's 0.75rem plus the panel's own
 * inset (about 13px). Declared after the size tiers so it wins over their item padding. */
.cbui-list-edge-block {
    --cbui-list-panel-px: 0px;
    --cbui-list-item-px:  var(--spacing-md, 1rem);
}
/* Density's py applies on top of size's py — declared after so it wins.
   (Authors who don't want density tweaking the size's py can omit the
   density prop / class entirely.) */
.cbui-list-size-sm.cbui-list-density-compact     { --cbui-list-item-py: 0.25rem; }
.cbui-list-size-sm.cbui-list-density-spacious    { --cbui-list-item-py: 0.5rem; }
.cbui-list-size-lg.cbui-list-density-compact     { --cbui-list-item-py: 0.5rem; }
.cbui-list-size-lg.cbui-list-density-spacious    { --cbui-list-item-py: 0.875rem; }

/* ── Container variants ──────────────────────────────────────────────────── */
.cbui-list-bordered {
    border: 1px solid var(--color-border);
    border-radius: var(--cbui-list-radius);
    background-color: var(--color-surface, var(--color-bg-elevated));
    overflow: hidden;
}
/* `flush` is `bordered` MINUS the container chrome — no border, no radius,
 * no background — and NOTHING ELSE changes. Item padding, item radius,
 * panel padding all stay at their defaults so a flush list reads exactly
 * like a bordered list whose frame was lifted, not a "different" list.
 *
 * The variant exists for callsites that explicitly want to declare "the
 * parent container supplies the frame (Card body, multi-column cell, …)".
 * Visually a `flush` list and a `default` list look identical — the
 * variant is mostly a documentation signal. */
.cbui-list-flush {
    /* intentionally empty */
}

/* ── item_style: explicit inset ──────────────────────────────────────────── */
.cbui-list-item-style-inset {
    --cbui-list-item-radius: var(--radius-sm, 0.375rem);
    --cbui-list-panel-px: 0.25rem;
    --cbui-list-panel-py: 0.25rem;
    /* Divider sits inset by sep-inset − panel-px so the visual gap to the
       panel border is constant (~0.375rem). Mirrors dropdown.css's separator. */
    --cbui-list-divider-inset: max(0px, calc(var(--cbui-list-sep-inset) - var(--cbui-list-panel-px)));
}

/* ── item_style: flavor default ──────────────────────────────────────────────
 * Auto mode — Aero + Soft pick inset (matches their Dropdown default), the
 * other flavors stay full. Mirrors dropdown.css's flavor handling. Applies
 * uniformly to default / bordered / flush — the variant only controls the
 * container chrome, not the item layout. */
[data-flavor="aero"] .cbui-list-item-style-auto,
[data-flavor="soft"] .cbui-list-item-style-auto {
    --cbui-list-item-radius: var(--radius-sm, 0.375rem);
    --cbui-list-panel-px: 0.25rem;
    --cbui-list-panel-py: 0.25rem;
    --cbui-list-divider-inset: max(0px, calc(var(--cbui-list-sep-inset) - var(--cbui-list-panel-px)));
}
/* Sharp collapses every radius to 0. */
[data-flavor="sharp"] .cbui-list {
    --cbui-list-item-radius: 0px;
    --cbui-list-radius: 0px;
}
/* Soft + Aero use a larger container radius — mirrors the Dropdown panel
   radius (both flavors set dropdown-radius / panel-radius to radius-lg).
   Without this, a bordered List in Aero / Soft would look tighter than
   the matching Dropdown panel — same item family, different chrome. */
[data-flavor="aero"] .cbui-list,
[data-flavor="soft"] .cbui-list {
    --cbui-list-radius: var(--radius-lg, 0.75rem);
}

/* ── ListItem ────────────────────────────────────────────────────────────── */
.cbui-list-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--cbui-list-item-gap);
    width: 100%;
    padding-inline: var(--cbui-list-item-px);
    padding-block: var(--cbui-list-item-py);
    border: 0;
    border-radius: var(--cbui-list-item-radius);
    color: inherit;
    background-color: transparent;
    cursor: pointer;
    text-decoration: none;
    text-align: start;
    font-family: inherit;
    font-size: var(--cbui-list-item-fs);
    line-height: 1.4;
    transition: background-color 100ms ease, color 100ms ease;
}

/* Hover — only when the list is interactive. Static lists (nav / list columns)
   never hover; the active row carries all the visual weight. */
.cbui-list-interactive .cbui-list-item:hover {
    background-color: var(--cbui-list-item-hover-bg);
}
.cbui-list-interactive .cbui-list-item:active {
    background-color: var(--cbui-list-item-press-bg);
}

.cbui-list-item:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -2px;
    z-index: 1;
}

/* Active ("current") row — primary wash, medium weight. NO leading accent
   bar (removed by request): the primary tint + the dropped surrounding
   dividers + the bolder label already mark the row clearly. */
.cbui-list-item-active {
    background-color: var(--cbui-list-item-active-bg);
    font-weight: var(--font-weight-medium, 500);
}
.cbui-list-item-active .cbui-list-item-label {
    font-weight: var(--font-weight-semibold, 600);
}
/* In interactive lists the active row still lifts a touch on hover. */
.cbui-list-interactive .cbui-list-item-active:hover {
    background-color: color-mix(in srgb, var(--cbui-list-item-active-bg) 80%, var(--color-text));
}

/* ── activeStyle="solid" — paint the active row with the FULL primary colour.
 * Use when the active row is the canonical "you are here" highlight that
 * must read at a glance (e.g. a primary-coloured sidebar). Contrasting
 * text + softened secondary text so the row reads cleanly.
 *
 * Per-item override: drop `.cbui-list-item-active-solid` on a single
 * ListItem to make THAT row solid even when the group default is soft —
 * useful in mixed lists. */
.cbui-list-active-style-solid .cbui-list-item-active,
.cbui-list-item-active.cbui-list-item-active-solid {
    background-color: var(--color-primary);
    color: var(--color-text-on-primary, white);
}
.cbui-list-active-style-solid .cbui-list-item-active .cbui-list-item-description,
.cbui-list-active-style-solid .cbui-list-item-active .cbui-list-item-secondary,
.cbui-list-active-style-solid .cbui-list-item-active .cbui-list-item-icon,
.cbui-list-active-style-solid .cbui-list-item-active .cbui-list-item-icon-end,
.cbui-list-item-active.cbui-list-item-active-solid .cbui-list-item-description,
.cbui-list-item-active.cbui-list-item-active-solid .cbui-list-item-secondary,
.cbui-list-item-active.cbui-list-item-active-solid .cbui-list-item-icon,
.cbui-list-item-active.cbui-list-item-active-solid .cbui-list-item-icon-end {
    color: color-mix(in srgb, currentColor 78%, transparent);
}
.cbui-list-interactive .cbui-list-active-style-solid .cbui-list-item-active:hover,
.cbui-list-interactive .cbui-list-item-active.cbui-list-item-active-solid:hover {
    background-color: color-mix(in srgb, var(--color-primary) 90%, var(--color-text));
}

/* Selected ("checked") row — quiet neutral tint, NO primary hue, never the
   same look as active. Survives next to an active row in multi-select. */
.cbui-list-item-selected:not(.cbui-list-item-active) {
    background-color: var(--cbui-list-item-selected-bg);
}

.cbui-list-item-disabled,
.cbui-list-item:disabled {
    opacity: 0.5;
    pointer-events: none;
    cursor: not-allowed;
}

/* ── Inner anatomy ───────────────────────────────────────────────────────── */
/* `min-width`/`min-height`, NOT `width`/`height` (Matthias, 2026-09-04: „Es muss
   zwischen dem Avatar und dem Titel/Beschreibung Abstand ergänzt werden.").
   The box is sized for an ICON (16px at size=md), but rows routinely put
   something bigger in it — an Avatar at `size="sm"` is 32px. With a fixed
   `width` the box stayed 16px and the avatar overflowed it by 8px on EACH side,
   which is exactly the row's `gap`: measured on the agent list, the avatar ended
   at x=277 and the body began at x=277. Zero space, on every list in the product
   that leads with an avatar.

   A `min-` pair changes nothing for an actual icon (its content is the token
   size) and lets a larger leading element size its own cell, so the gap stays a
   gap. `flex-shrink: 0` still keeps the cell from being squeezed. */
.cbui-list-item-icon,
.cbui-list-item-icon-end,
.cbui-list-item-check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--cbui-list-item-icon);
    min-height: var(--cbui-list-item-icon);
    flex-shrink: 0;
    color: var(--color-text-secondary);
}
.cbui-list-item-check { color: var(--color-primary); }
.cbui-list-item-active .cbui-list-item-icon { color: var(--color-primary); }

/* CB-1730 Sichtprüfung — `min-width: 0` alone let the row's OWN name
 * collapse to nothing: `.cbui-list-item-secondary` was `flex-shrink: 0`
 * (never yields), so a row with two badges plus revealed actions (measured:
 * a 320px column, two `.cbui-badge`s at 163.6px combined, actions at 96px)
 * squeezed the body down to ~4px — the label the row is ABOUT vanished
 * while its badges and actions stayed fully drawn. `--cbui-list-item-label-min`
 * (140px) already reserved space for the label against the ACTIONS box
 * (see `.cbui-list-item-actions-overflow` above); it did nothing against
 * `.cbui-list-item-secondary`, a separate non-shrinking flex item the
 * actions-side arithmetic never accounted for. The body now claims that
 * SAME reserve directly, so flexbox takes the difference from whichever
 * sibling still has room to give — the now-shrinkable/wrapping secondary
 * column below, never the label. */
.cbui-list-item-body {
    flex: 1 1 auto;
    min-width: var(--cbui-list-item-label-min, 8.75rem);
    display: inline-flex;
    flex-direction: column;
    gap: 0.0625rem;
}
/* The row's MAIN information. Base size is a FLOOR, not a default: see
   „ROW HIERARCHY" at the top of this file. `size="sm"` narrows the padding and
   the icon box, and leaves the label alone. Medium weight is part of the same
   ranking -- the label has to win against a full meta line without needing a
   larger size than the rest of the row. */
.cbui-list-item-label {
    font-size: max(var(--cbui-list-item-fs), var(--font-size-md, 1rem));
    font-weight: var(--font-weight-medium, 500);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Wrapping variant: a label that IS a path (a trash row's full "folder/sub/name")
 * loses the part that disambiguates it when clipped from the end — the tail is
 * what says which of several same-named files this is (CB-1917). Opt in per
 * label; truncation stays the default everywhere else. */
.cbui-list-item-label-wrap {
    white-space: normal;
    overflow-wrap: anywhere;
    text-overflow: clip;
}
/* Unread row (CB-3524): the label alone turns bold, same size. Placed after the active rule's weight so an unread active row stays bold. */
.cbui-list-item-unread .cbui-list-item-label {
    font-weight: var(--font-weight-bold, 700);
}
/* A leading state word in the description (a draft): accent tone, same size. */
.cbui-list-item-description-accent {
    color: var(--color-accent);
}
.dark .cbui-list-item-description-accent, [data-scheme="dark"] .cbui-list-item-description-accent {
    color: color-mix(in srgb, var(--color-accent) 80%, white);
}
/* Two-line variant: a label that is a whole question (a decision) wraps to at most two lines and is then cut with an
 * ellipsis. Opt in per label next to `.cbui-list-item-label`; truncation to one line stays the default. */
.cbui-list-item-label-clamp {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    white-space: normal;
    overflow-wrap: anywhere;
    line-height: 1.3;
}
/* Same size as the label (the floor), ranked below it by secondary grey and weight. */
.cbui-list-item-description {
    font-size: max(var(--cbui-list-item-fs), var(--font-size-md, 1rem));
    color: var(--color-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.35;
}
/* CB-2616: a supporting line that is a whole sentence the reader must get in full (the reason a background run failed and
 * what is done and what is not) wraps instead of being cut; truncation stays the default for every other description. */
.cbui-list-item-description-wrap {
    white-space: normal;
    overflow-wrap: anywhere;
    text-overflow: clip;
}
/* Mini mode (CB-1822, ListItem's `mini` prop) — the dock widget body: one
 * line only, avatar/icon + title, nothing else. The row content itself is
 * unchanged (same component, same markup) — this only switches off the
 * second line, mirroring how `context` already only switches off what is
 * meaningless in a given spot (LEITFADEN § Der Eintrags-Vertrag). */
.cbui-list-row.cbui-list-item-mini .cbui-list-item-description { display: none; }
/* CB-1822 review fix (major finding 3): a row whose leading element is a bare
 * icon (ModelEntry, `.cbui-list-item-icon`'s own box floors at
 * `--cbui-list-item-icon`, 1–1.125rem) collapsed to a SHORTER line than a row
 * whose leading element is an Avatar (ConversationEntry/AgentEntry, always
 * 2rem at `size="sm"`) — measured 38.4px vs 48px, breaking "Höhe = eine
 * Zeile … gleich für alle Typen" (dossier acceptance). Flooring the icon box
 * at the SAME 2rem in mini mode make every type's row the same height
 * regardless of which leading element it renders — proven by
 * `cb-1822-dock-mini-mode.spec.ts`. */
.cbui-list-row.cbui-list-item-mini .cbui-list-item-icon {
    min-width: 2rem;
    min-height: 2rem;
}
.cbui-list-item-with-description { align-items: flex-start; }
.cbui-list-item-with-description .cbui-list-item-icon,
.cbui-list-item-with-description .cbui-list-item-icon-end,
.cbui-list-item-with-description .cbui-list-item-check {
    margin-block-start: 0.125rem;
}

/* Trailing slots — secondary text, badge, shortcut. */
/* CB-1730 Sichtprüfung — gives ground to the label's reserve (see
 * `.cbui-list-item-body` above) instead of holding it hostage. A plain-text
 * secondary (the common case: "2 Tage her") is a single anonymous flex
 * item, so `white-space: nowrap` still keeps it on one line — nothing
 * changes for that caller. A secondary that holds several `.cbui-badge`
 * elements (this ticket's case: contact-type + "Gesperrt") is now itself a
 * small flex container: `flex-wrap: wrap` lets a badge that no longer fits
 * the first line drop to a second one, growing the ROW's height instead of
 * shrinking the label's width. `min-width: 0` is what makes the wrap (not
 * an overflow) the thing that happens once `flex-shrink: 1` starts giving
 * space back. */
.cbui-list-item-secondary {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.25rem;
    flex-shrink: 1;
    min-width: 0;
    margin-inline-start: auto;
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
    white-space: nowrap;
}
/* CB-1728 Sichtprüfung — a `.cbui-list-item-secondary` that holds `.cbui-badge`s
 * shrinks (flex-shrink: 1, min-width: 0, see above) once the row is tight —
 * but shrinking a flex item does not shrink its CHILDREN. A `.cbui-badge` has
 * no shrink story of its own (badge.css), so once the secondary box is
 * narrower than even its first badge, that badge keeps its full size and
 * paints PAST the box's own left edge — directly over the label/description,
 * because `.cbui-list-item-secondary` has no `overflow: hidden` (deliberate,
 * see its own docblock: hiding would just clip the badge instead). Measured
 * (Ops-Plattform "Mitarbeiter" list, 930px window, CB-1728 Sichtprüfung): a
 * single "Ohne Betriebszugang" badge overlapping the row's own description
 * text, and two badges ("Aktiv" + "Administrator") stacking on top of it.
 *
 * Fix: once the row actually carries badges, let the ROW itself wrap.
 * `.cbui-list-item-secondary`'s own `flex-wrap: wrap` already breaks several
 * badges onto more than one line WITHIN the box; wrapping the OUTER row
 * additionally lets the WHOLE secondary box drop to its own full-width line
 * below icon+body the moment it does not fit next to the label, instead of
 * being squeezed into a slice too narrow for even one badge. The row grows
 * taller instead of a badge painting over the text — exactly the target
 * Matthias described: badges break UNDER the name, in their own line of the
 * row body. Icon+body always keep the FIRST line to themselves (icon ~1rem +
 * the 140px label reserve is far under any real row width), so this never
 * wraps the label away from its own icon.
 *
 * Scoped to badge-carrying rows only, same `:has()` precedent as the
 * actions-max rule above — a plain-text secondary ("2 Tage her") keeps its
 * one-line, same-row layout unchanged. */
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-badge),
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-list-item-badge) {
    flex-wrap: wrap;
    row-gap: 0.125rem;
}
/* ── CB-1730 (Bau 2) — the badge-wrap above split ICON from BODY ─────────────
 * Regression measured on the platform Mitarbeiter list (:9306, ~320px list
 * column, Aero, dark): the icon rendered ALONE on line 1, with name / email /
 * badges all stacked below it — not the intended "icon+name on line 1,
 * badges below when they don't fit".
 *
 * Root cause: `.cbui-list-item-icon` and `.cbui-list-item-body` are separate
 * flex children of the now-wrapping `.cbui-list-item`. The browser's line-fit
 * check for wrapping uses each item's HYPOTHETICAL (un-shrunk) main size —
 * for `.cbui-list-item-body` that is `flex: 1 1 auto`, i.e. its full CONTENT
 * width, and `.cbui-list-item-label` is `white-space: nowrap`, so a normal
 * name's unclipped width is easily 150-250px. Icon + that huge hypothetical
 * body size already overflow the ~280px column on their own — so body itself
 * gets bumped to a fresh line, stranding the icon alone on line 1.
 *
 * Fix, two parts:
 *   1. Icon leaves the wrapping flex flow (`position: absolute`), so the
 *      wrap decision only ever runs between BODY and SECONDARY — icon can
 *      never be separated from the name again. Its footprint is reserved as
 *      `padding-inline-start` on the row, so the text column starts at
 *      exactly the x it always did, and a wrapped SECONDARY line (which
 *      starts at that same padded edge) lands indented under the name, not
 *      under the icon — "eingerückt auf Höhe des Namens", never under the icon.
 *   2. Body's flex-basis drops to 0% (from `auto`), so its HYPOTHETICAL size
 *      for the wrap check is its `min-width` floor (the 140px label reserve,
 *      `--cbui-list-item-label-min`), not the full unclipped name. That
 *      restores the "badges sit next to the name whenever the 140px-floored
 *      name plus the badges still fit" behaviour — body only forces its own
 *      line when its true content needs more room, exactly as before.
 * Rows without a leading icon are untouched (icon-less selectors don't
 * match), and rows without badges never hit this branch at all. */
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-badge):has(> .cbui-list-item-icon),
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-list-item-badge):has(> .cbui-list-item-icon),
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-badge):has(> .cbui-list-item-check),
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-list-item-badge):has(> .cbui-list-item-check) {
    padding-inline-start: calc(var(--cbui-list-item-px) + var(--cbui-list-item-icon) + var(--cbui-list-item-gap));
}
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-badge) > .cbui-list-item-icon,
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-list-item-badge) > .cbui-list-item-icon,
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-badge) > .cbui-list-item-check,
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-list-item-badge) > .cbui-list-item-check {
    position: absolute;
    inset-inline-start: var(--cbui-list-item-px);
    top: var(--cbui-list-item-py);
}
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-badge) > .cbui-list-item-body,
.cbui-list-item:has(> .cbui-list-item-secondary .cbui-list-item-badge) > .cbui-list-item-body {
    flex-basis: 0%;
}
.cbui-list-item-badge {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    height: 1.25rem;
    padding-inline: 0.375rem;
    border-radius: 9999px;
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-semibold, 600);
    line-height: 1;
    background-color: color-mix(in srgb, currentColor 18%, transparent);
    color: var(--color-text);
    margin-inline-start: auto;
}
.cbui-list-item-secondary + .cbui-list-item-badge { margin-inline-start: 0; }

/* Trailing badge GROUP (CB-2235): a row that carries several .cbui-badge at its trailing edge puts them in ONE
 * wrapper with this class. The group is always ONE line, so a row has the same height with 0, 1 or 3 badges at
 * every width (Matthias: wrapping gives rows an uncontrolled height; no text ellipsis either).
 * What gives way is the badge TEXT: once the ROW is narrower than its badges can afford, ALL badges of the row switch
 * together to icon-only. The label stays in the accessibility tree (visually clipped, not display: none) and every
 * badge carries a title with its text as the tooltip (the Badge component canon for an icon-only badge).
 * The row is the container (named cbui-list-row), so the switch needs no JavaScript measuring. The breakpoint
 * grows with the number of badges: 1 badge 28rem, 2 badges 36rem, 3 or more 44rem. Measured before the change on the
 * Agents list: badges lay over the revealed actions by 110px at a 560px column and 46px at 200px.
 * A consumer must give every badge of the group an icon, otherwise the icon-only pill is empty. */
.cbui-list-row:has(.cbui-list-item-badges) {
    container-type: inline-size;
    container-name: cbui-list-row;
}
.cbui-list-item .cbui-list-item-badges {
    flex: none;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.375rem;
    margin-inline-start: auto;
}
.cbui-list-item-badges > .cbui-badge { position: relative; flex: none; }
@container cbui-list-row (max-width: 28rem) {
    .cbui-list-item-badges { --cbui-badge-pad-x-current: 0.375rem; --cbui-badge-labels: inline; }
    .cbui-list-item-badges > .cbui-badge > .cbui-badge-label,
    .cbui-list-item-badges > * > .cbui-badge > .cbui-badge-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
/* Below ~30rem the name reserve (140px) cannot stay for a row with a badge group: it yields to 5rem. At the narrowest a list
 * column can be dragged to (200px: avatar, name, the actions box and the pills no longer fit) the group keeps only its
 * FIRST badge (the most important one comes first; the others stay in the row detail) and the name keeps 2.5rem.
 * Rows without a badge group are untouched. */
@container cbui-list-row (max-width: 30rem) {
    .cbui-list-item:has(> .cbui-list-item-badges) > .cbui-list-item-body { min-width: min(var(--cbui-list-item-label-min, 8.75rem), 5rem); }
}
@container cbui-list-row (max-width: 18rem) {
    .cbui-list-item-badges > :nth-child(n+2) { display: none; }
    .cbui-list-item:has(> .cbui-list-item-badges) > .cbui-list-item-body { min-width: min(var(--cbui-list-item-label-min, 8.75rem), 2.5rem); }
}
@container cbui-list-row (max-width: 36rem) {
    .cbui-list-item-badges:has(> :nth-child(2)) { --cbui-badge-pad-x-current: 0.375rem; --cbui-badge-labels: inline; }
    .cbui-list-item-badges:has(> :nth-child(2)) > .cbui-badge > .cbui-badge-label,
    .cbui-list-item-badges:has(> :nth-child(2)) > * > .cbui-badge > .cbui-badge-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
@container cbui-list-row (max-width: 44rem) {
    .cbui-list-item-badges:has(> :nth-child(3)) { --cbui-badge-pad-x-current: 0.375rem; --cbui-badge-labels: inline; }
    .cbui-list-item-badges:has(> :nth-child(3)) > .cbui-badge > .cbui-badge-label,
    .cbui-list-item-badges:has(> :nth-child(3)) > * > .cbui-badge > .cbui-badge-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
.cbui-list-item-badge-tone-primary { background-color: color-mix(in srgb, var(--color-primary) 18%, transparent); color: var(--color-primary); }
.cbui-list-item-badge-tone-success { background-color: color-mix(in srgb, var(--color-success) 18%, transparent); color: var(--color-success); }
.cbui-list-item-badge-tone-warning { background-color: color-mix(in srgb, var(--color-warning) 18%, transparent); color: var(--color-warning); }
.cbui-list-item-badge-tone-danger  { background-color: color-mix(in srgb, var(--color-danger)  18%, transparent); color: var(--color-danger); }
.cbui-list-item-badge-tone-info    { background-color: color-mix(in srgb, var(--color-info)    18%, transparent); color: var(--color-info); }
.cbui-list-item-badge-tone-muted   { background-color: color-mix(in srgb, currentColor 8%, transparent); color: var(--color-text-secondary); }

.cbui-list-item-shortcut {
    flex-shrink: 0;
    font-family: var(--font-family-mono, ui-monospace, monospace);
    font-size: var(--font-size-md, 1rem);
    line-height: 1;
    font-weight: var(--font-weight-medium, 500);
    padding: 0.1875rem 0.375rem;
    border-radius: 0.25rem;
    background-color: color-mix(in srgb, currentColor 8%, transparent);
    color: var(--color-text-secondary);
    border: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
.cbui-list-item-badge + .cbui-list-item-shortcut,
.cbui-list-item-check + .cbui-list-item-icon-end { margin-inline-start: 0.375rem; }
/* When nothing else pushed to the trailing edge, the check / end-icon does. */
.cbui-list-item:not(:has(.cbui-list-item-secondary)):not(:has(.cbui-list-item-badge)):not(:has(.cbui-list-item-shortcut)) .cbui-list-item-check,
.cbui-list-item:not(:has(.cbui-list-item-secondary)):not(:has(.cbui-list-item-badge)):not(:has(.cbui-list-item-shortcut)):not(:has(.cbui-list-item-check)) .cbui-list-item-icon-end {
    margin-inline-start: auto;
}

/* ── Item tones — colour the row text + a hover wash in the same hue.
 * Mirrors dropdown.css's tone treatment exactly so a List menu and a
 * Dropdown menu read as the same family. */
.cbui-list-item-tone-danger  { color: var(--color-danger); }
.cbui-list-item-tone-warning { color: var(--color-warning); }
.cbui-list-item-tone-success { color: var(--color-success); }
.cbui-list-item-tone-primary { color: var(--color-primary); }
.cbui-list-item-tone-info    { color: var(--color-info); }
.cbui-list-item-tone-muted   { color: var(--color-text-secondary); }

.cbui-list-item-tone-danger  .cbui-list-item-icon,
.cbui-list-item-tone-warning .cbui-list-item-icon,
.cbui-list-item-tone-success .cbui-list-item-icon,
.cbui-list-item-tone-info    .cbui-list-item-icon { color: currentColor; }

.cbui-list-interactive .cbui-list-item-tone-danger:hover {
    background-color: color-mix(in srgb, var(--color-danger) 9%, transparent);
}
.cbui-list-interactive .cbui-list-item-tone-warning:hover {
    background-color: color-mix(in srgb, var(--color-warning) 9%, transparent);
}
.cbui-list-interactive .cbui-list-item-tone-success:hover {
    background-color: color-mix(in srgb, var(--color-success) 9%, transparent);
}
.cbui-list-interactive .cbui-list-item-tone-info:hover {
    background-color: color-mix(in srgb, var(--color-info) 9%, transparent);
}

/* ── Group label (section header inside a list) ──────────────────────────────
 * Same recipe as dropdown.css's .cbui-dropdown-label so a labelled List
 * groups items the way a Dropdown groups menu items. Lives in its own
 * <li class="cbui-list-row cbui-list-row-label"> so the structure stays
 * list-semantic and the divider rules can suppress dividers around it. */
.cbui-list-label {
    display: block;
    padding-inline: var(--cbui-list-item-px);
    /* The space above a group heading is what makes a group read as a group (Matthias 2026-10-28): 1rem above, the
       first label of the list keeps the small edge padding below. */
    padding-block: 1rem 0.1875rem;
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    line-height: 1.25;
    user-select: none;
    /* CB-3623: a group label never wraps; a long text ends in an ellipsis (the title attribute carries it in full). */
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* The first label inside the list gets less top padding (it already sits
   at the panel edge). */
.cbui-list-row-label:first-child .cbui-list-label {
    padding-block-start: 0.25rem;
}

/* Explicit horizontal separator between groups — a real <li> divider line.
   Visually aligns with the inter-row divider so groups feel continuous. */
.cbui-list-separator {
    display: block;
    height: 1px;
    margin-inline: var(--cbui-list-divider-inset);
    margin-block: 0.25rem;
    background-color: var(--color-border);
}

/* ── Dividers — drawn as a thin absolute-positioned line on the row's top.
 * Decoupled from the row's box: the bar's inset is independent of where the
 * row content sits, so in INSET mode the divider can be inset while every
 * item still starts at the same x (the previous border-block-start +
 * margin-inline approach shifted item 2+ inward by 6px — fixed by moving
 * the inset onto the bar itself). */
.cbui-list-with-dividers .cbui-list-row + .cbui-list-row::before {
    content: '';
    position: absolute;
    inset-block-start: 0;
    inset-inline: var(--cbui-list-divider-inset);
    height: 1px;
    background-color: var(--color-border);
    pointer-events: none;
}
/* Hide the divider above the active / selected / label / separator row
   AND the one directly below it (= the next row's top divider) so each
   such surface reads as one uncut block. Same convention as Dropdown's
   stacked-current treatment. These suppressions fire in ALL flavors —
   they reflect the row's intrinsic state, not interaction.
 *
 * ⭐ THE SUPPRESSION MUST FOLLOW THE SAME SIGNAL AS THE PAINT (2026-09-03).
 *
 * Until today the class selectors below stood alone — and the classes
 * `cbui-list-row-active` / `-selected` exist only when the `active` / `selected`
 * PROP was set at RENDER time. The selected background, however, is painted
 * from the `aria-selected="true"` ATTRIBUTE (§ Selected row, and the
 * ListDetail rules further down). In a ListDetail those two come apart the
 * moment the user clicks: `ui-component--list-detail` flips `aria-selected` on
 * the item at runtime and adds no class, so the row got its wash and the
 * divider went straight through it — exactly the case the rule exists for.
 *
 * Matthias, 2026-09-03: „Die horizontalen Linien müssen immer dann ausgeblendet
 * werden, wenn an exakt der Stelle (oben/unten) das aktive Item ist und einen
 * Hintergrund hat."
 *
 * So the attribute forms are listed alongside the class forms. Both stay: the
 * class covers a statically rendered `active`/`selected` row, the attribute
 * covers every row a controller marks later. Whichever paints, suppresses. */
.cbui-list-with-dividers .cbui-list-row-active::before,
.cbui-list-with-dividers .cbui-list-row-active + .cbui-list-row::before,
.cbui-list-with-dividers .cbui-list-row-selected::before,
.cbui-list-with-dividers .cbui-list-row-selected + .cbui-list-row::before,
.cbui-list-with-dividers .cbui-list-row:has(> .cbui-list-item[aria-selected="true"])::before,
.cbui-list-with-dividers .cbui-list-row:has(> .cbui-list-item[aria-selected="true"]) + .cbui-list-row::before,
.cbui-list-with-dividers .cbui-list-row:has(> .cbui-list-item[aria-current="true"])::before,
.cbui-list-with-dividers .cbui-list-row:has(> .cbui-list-item[aria-current="true"]) + .cbui-list-row::before,
.cbui-list-with-dividers .cbui-list-row-label::before,
.cbui-list-with-dividers .cbui-list-row-label + .cbui-list-row::before,
.cbui-list-with-dividers .cbui-list-row-separator::before,
.cbui-list-with-dividers .cbui-list-row-separator + .cbui-list-row::before {
    display: none;
}

/* Hovered rows get the same treatment — where there is a hover bg, there
 * must be no divider running through it. BUT: this rule fires only in
 * flavors that ACTUALLY paint a hover bg. Aero suppresses the hover bg
 * via the flavor, so if we hid the dividers here too the user would see
 * lines flicker on hover without anything taking their place ("die Linien
 * hovern, aber es gibt kein Hover"). The flavor list is explicit (not a
 * `:not([data-flavor="aero"])` ancestor-negation, which is hard to write
 * reliably for the storybook's per-panel scoping). `:focus-visible` is
 * NOT in here either — the focus ring is an inset outline, no bg to
 * compensate for the missing divider. */
:is([data-flavor="clean"], [data-flavor="soft"], [data-flavor="sharp"], [data-flavor="glass"]) .cbui-list-interactive.cbui-list-with-dividers .cbui-list-row:has(.cbui-list-item:hover)::before,
:is([data-flavor="clean"], [data-flavor="soft"], [data-flavor="sharp"], [data-flavor="glass"]) .cbui-list-interactive.cbui-list-with-dividers .cbui-list-row:has(.cbui-list-item:hover) + .cbui-list-row::before {
    display: none;
}

/* ── Edge-corner radius — first / last item hugs the container's inner curve.
 * Mirrors dropdown.css's formula:  max(item-radius, container-radius - panel-py - 1px)
 * Only meaningful on the bordered container; flush / borderless lists have no
 * curve to hug. */
.cbui-list-bordered > .cbui-list-row:first-child > .cbui-list-item {
    border-start-start-radius: max(var(--cbui-list-item-radius), calc(var(--cbui-list-radius) - var(--cbui-list-panel-py) - 1px));
    border-start-end-radius:   max(var(--cbui-list-item-radius), calc(var(--cbui-list-radius) - var(--cbui-list-panel-py) - 1px));
}
.cbui-list-bordered > .cbui-list-row:last-child > .cbui-list-item {
    border-end-start-radius: max(var(--cbui-list-item-radius), calc(var(--cbui-list-radius) - var(--cbui-list-panel-py) - 1px));
    border-end-end-radius:   max(var(--cbui-list-item-radius), calc(var(--cbui-list-radius) - var(--cbui-list-panel-py) - 1px));
}

.a11y-reduced-motion .cbui-list-item { transition: none; }
.a11y-high-contrast .cbui-list-with-dividers .cbui-list-row + .cbui-list-row::before {
    height: 2px;
    background-color: var(--color-border-strong);
}

/* ── Long press on a selectable item (CB-1947) ───────────────────────────────
 * The selection scope opens the context menu on a touch held ~500 ms. The browser's own
 * long-press reactions must stay out of the way on exactly those items: iOS' link/image callout
 * and the text-selection handles. Coarse pointers only, items only — page text elsewhere keeps
 * its selection, and a rename field inside an item stays editable. */
@media (pointer: coarse) {
    [data-cbui-selection-id] {
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        user-select: none;
    }
    [data-cbui-selection-id] :is(input, textarea, [contenteditable]) {
        -webkit-user-select: text;
        user-select: text;
    }
}

/* ── Action bar with permanent icons plus an always present "…" (CB-3517) ─────
 * Host class of a `ui-component--list-actions` box in `mode="menu"`. The gap between the icons is
 * one token; the box's end padding pulls the distance to the window edge level with that gap. Inside a
 * menubar Tabnav the trail already insets by `--cbui-tabnav-menubar-inset` (declared only there), so the
 * difference is what is left to add; outside one the whole gap is added. */
.cbui-actions-bar {
    --cbui-actions-bar-gap: var(--actions-bar-gap, var(--cbui-action-gap, 0.625rem));
    --cbui-actions-bar-edge: max(0px, calc(var(--cbui-actions-bar-gap) - var(--cbui-tabnav-menubar-inset, 0px)));
    display: inline-flex;
    align-items: center;
    gap: var(--cbui-actions-bar-gap);
    padding-inline-end: var(--cbui-actions-bar-edge);
    min-width: 0;
}

/* cbui/components/list-detail */
/* cbui-list-detail — in-window "list + detail" primitive.
 *
 * Structure:
 *   .cbui-list-detail            ← ROOT (flex column)
 *     .cbui-list-detail-toolbar  ← stable top strip: narrow-mode Back + placement switch.
 *                                   Always at the top → the placement control never moves
 *                                   when you flip placement (unlike a per-pane bar). Back is
 *                                   NEVER gated on showToolbar (CB-668) — with showToolbar=
 *                                   false the toolbar carries a `-minimal` modifier and ships
 *                                   ONLY Back, still visible below collapseAt (see the
 *                                   showToolbar=false rules further down).
 *     .cbui-list-detail-panes    ← the responsive flex layout
 *       .cbui-list-detail-list
 *       .cbui-list-detail-detail
 *
 * Host (`.cbui-list-detail-host`, CB-1828): a consumer wraps its ListDetail
 * in an element carrying this class to get a full-height chain down to the
 * component for free — see the "Full height" block near the end of this
 * file. The WorkspaceShell's own window body opts in this way.
 *
 * Placement = the LIST's position: auto | left | right | top | bottom.
 * The layout is driven by TWO width thresholds, computed in JS (so both stay
 * prop-configurable) and reflected as state classes on the root:
 *
 *   is-single  width < collapseAt (T2, 400px) → single pane: the list, or after
 *              a selection (.is-detail-active) the detail + Back. EVERY mode.
 *   is-stack   'auto' & collapseAt ≤ width < stackAt (T1, 720px) → list-over-detail.
 *              ('auto' with no is-stack, i.e. width ≥ stackAt → side-by-side.)
 *
 * A MANUAL top/bottom ALWAYS halves (50/50, draggable) — no height fallback.
 * All directions map onto one flex-direction on `panes`; DOM order stays
 * list-first (row/column-reverse only flip the VISUAL order) so tab order is
 * always list → detail. The list ↔ detail split is draggable via the divider:
 * a px width side-by-side (--cbui-ld-hsize), a grow ratio stacked (--cbui-ld-vsize).
 */

.cbui-list-detail {
    /* ── Jede Instanz besitzt ihre EIGENE Größe ──────────────────────────────
       Beide Werte werden hier auf der Wurzel deklariert, nicht nur als
       `var(…, 320px)`-Ersatzwert weiter unten benutzt. Der Unterschied ist der
       ganze Punkt: ein Ersatzwert greift nur, wenn die Eigenschaft NIRGENDS in
       der Vererbungskette steht — und Custom Properties vererben.

       Eine ListDetail kann in der Detail-Hälfte einer anderen liegen (die
       Versionsliste eines Values ist genau das). Der Zieh-Vorgang schreibt
       `--cbui-ld-hsize` INLINE auf die Wurzel der äußeren Instanz; ohne eine
       eigene Deklaration erbte die innere diesen Wert und wurde beim Ziehen am
       äußeren Griff stillschweigend mitgezogen. Matthias, 2026-08-14: „Wenn ich
       auf ein Menu Item … klicke und dann die Breite des äußeren ListDetail
       ändere, ändert sich die des inneren mit."

       Eine Deklaration AM ELEMENT schlägt einen geerbten Wert, also stoppt sie
       die Vererbung an jeder Instanz-Grenze. Das eigene Ziehen funktioniert
       unverändert: eine Inline-Deklaration schlägt wiederum diese Regel hier.
       (Dasselbe Muster wie bei `--cbui-list-item-active-bg`, das list.css aus
       demselben Grund auf `.cbui-list` selbst deklariert.) */
    --cbui-ld-hsize: 320px;
    --cbui-ld-vsize: 50;

    container-type: inline-size;
    container-name: cbui-list-detail;

    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    overflow: hidden;
    position: relative;
    background-color: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-tabnav-fade-bg: var(--color-bg-canvas, var(--color-bg-base));
    color: var(--color-text);
}

/* ── Stable top toolbar ────────────────────────────────────────────────── */
.cbui-list-detail-toolbar {
    flex: none;
    display: flex;
    align-items: center;
    gap: 0.375rem;
    min-height: 2.5rem;
    padding-inline: 0.5rem;
    border-block-end: 1px solid var(--color-border);
    background-color: var(--color-surface, var(--color-bg-elevated));
    --cbui-tabnav-fade-bg: var(--color-surface, var(--color-bg-elevated));
}
.cbui-list-detail-toolbar-spacer { flex: 1 1 auto; }
.cbui-list-detail-back { display: none; } /* single-pane + detail-active only */

/* ── Panes ─────────────────────────────────────────────────────────────── */
.cbui-list-detail-panes {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}
/* Direction per placement. `auto` (no is-stack) = side-by-side, like `left`.
 *
 * ⚠ EVERY placement rule below addresses its OWN panes with the child
 * combinator, never a descendant one (CB-381). A ListDetail can be nested inside
 * another ListDetail's detail pane — Values does exactly that: the value detail
 * hosts the Versionen list. With `.cbui-list-detail-top .cbui-list-detail-panes`
 * the OUTER instance's placement class also matched the INNER instance's parts,
 * so the inner pane set got two placements at once and the later rule in this
 * file won — regardless of what the inner was actually set to. The visible
 * symptom Matthias reported: switch the Versionen list to „Liste oben/unten" and
 * the drag handle keeps `width: 9px` from the outer's horizontal rule while
 * picking up `height: 9px` from its own, collapsing to a 9×9 square parked at the
 * left edge instead of a full-width horizontal bar between the panes.
 *
 * The markup path is fixed and short (root > panes > list|divider|detail, and
 * divider > grip), so the child combinator costs nothing and makes the leak
 * structurally impossible. */
.cbui-list-detail-left   > .cbui-list-detail-panes,
.cbui-list-detail-auto   > .cbui-list-detail-panes { flex-direction: row; }
.cbui-list-detail-right  > .cbui-list-detail-panes { flex-direction: row-reverse; }
.cbui-list-detail-top    > .cbui-list-detail-panes { flex-direction: column; }
.cbui-list-detail-bottom > .cbui-list-detail-panes { flex-direction: column-reverse; }
.cbui-list-detail-auto.is-stack > .cbui-list-detail-panes { flex-direction: column; }

/* ── List pane ─────────────────────────────────────────────────────────── */
.cbui-list-detail-list {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    /* Clean surface — NOT a grey sidebar (a deliberate macOS-like choice; the
       divider does the separating, not a tinted fill). */
    background-color: var(--color-surface, var(--color-bg-elevated));
    --cbui-tabnav-fade-bg: var(--color-surface, var(--color-bg-elevated));
}
/* CB-2633: a list-only navigation is running. The pane stays in place (no layout
   shift), reads as busy and takes no clicks until the new list is drawn. */
.cbui-list-detail.is-list-pending > .cbui-list-detail-panes > .cbui-list-detail-list,
.cbui-list-detail-list[aria-busy="true"] {
    opacity: var(--cbui-ld-pending-opacity, 0.55);
    pointer-events: none;
}
/* Horizontal (side-by-side): the list gets a px width (draggable), the detail
   fills the rest. The separating line is drawn by the divider, not a list border. */
.cbui-list-detail-left  > .cbui-list-detail-panes > .cbui-list-detail-list,
.cbui-list-detail-right > .cbui-list-detail-panes > .cbui-list-detail-list,
.cbui-list-detail-auto  > .cbui-list-detail-panes > .cbui-list-detail-list { flex: 0 0 var(--cbui-ld-hsize, 320px); }
/* Vertical (stacked): the two panes split by flex-GROW ratio — 50/50 by default,
   draggable via --cbui-ld-vsize (0–100, the list's share). Grow ratios (not a %
   flex-basis) so the split resolves reliably even when the panes' height is only
   flex-derived — a % basis there degrades to content-height (list won't halve). */
.cbui-list-detail-top    > .cbui-list-detail-panes > .cbui-list-detail-list,
.cbui-list-detail-bottom > .cbui-list-detail-panes > .cbui-list-detail-list,
.cbui-list-detail-auto.is-stack > .cbui-list-detail-panes > .cbui-list-detail-list {
    flex-grow: var(--cbui-ld-vsize, 50);
    flex-shrink: 1;
    flex-basis: 0;
    max-height: none;
}
.cbui-list-detail-top    > .cbui-list-detail-panes > .cbui-list-detail-detail,
.cbui-list-detail-bottom > .cbui-list-detail-panes > .cbui-list-detail-detail,
.cbui-list-detail-auto.is-stack > .cbui-list-detail-panes > .cbui-list-detail-detail {
    flex-grow: calc(100 - var(--cbui-ld-vsize, 50));
    flex-shrink: 1;
    flex-basis: 0;
}

/* Rows carry NO hover chrome — in EVERY flavor. Some flavors tint the hover
   (clean); Aero already mutes it. The [data-flavor] variant + the extra classes
   out-specify the flavor-level hover rules (which are (0,4,0)); :not([aria-selected])
   leaves the selected row's fill untouched even while it's hovered. */
.cbui-list-detail-list .cbui-list-item:hover:not([aria-selected="true"], [aria-current="true"]),
.cbui-list-detail-list .cbui-list-item:active:not([aria-selected="true"], [aria-current="true"]),
[data-flavor] .cbui-list-detail-list .cbui-list-interactive .cbui-list-item:hover:not([aria-selected="true"], [aria-current="true"]),
[data-flavor] .cbui-list-detail-list .cbui-list-interactive .cbui-list-item:active:not([aria-selected="true"], [aria-current="true"]) {
    background-color: transparent;
}
/* Selected row = a primary-derived fill, computed straight from --color-primary
   so it is self-contained (no dependency on the ListGroup token's scope firing)
   and adapts to each theme's primary — the workspace-2026 brand blue, etc.
   Explicit dark step (26%, matching CBUI's list active recipe) for BOTH dark
   channels: [data-scheme="dark"] (Storybook scopes) and .dark (production shell),
   so the brand colour reads on a dark surface either way. Restated for :hover so
   it survives the hover-kill above. */
.cbui-list-detail-list .cbui-list-item:is([aria-selected="true"], [aria-current="true"]),
.cbui-list-detail-list .cbui-list-item:is([aria-selected="true"], [aria-current="true"]):hover {
    background-color: color-mix(in srgb, var(--color-primary) 14%, transparent);
}
[data-scheme="dark"] .cbui-list-detail-list .cbui-list-item:is([aria-selected="true"], [aria-current="true"]),
[data-scheme="dark"] .cbui-list-detail-list .cbui-list-item:is([aria-selected="true"], [aria-current="true"]):hover,
.dark .cbui-list-detail-list .cbui-list-item:is([aria-selected="true"], [aria-current="true"]),
.dark .cbui-list-detail-list .cbui-list-item:is([aria-selected="true"], [aria-current="true"]):hover {
    background-color: color-mix(in srgb, var(--color-primary) 26%, transparent);
}


/* ── Plane: which pane is „in front" ───────────────────────────────────────
 * `plane="recessed-list"` (Matthias, 2026-09-04):
 *
 *   „Der ‚äußere' Detail-Teil ist der Hauptteil in einem Bringer/Window. […]
 *    Dieser gesamte Bereich sollte optisch sehr weit ‚vorne' sein. […] Im
 *    Umkehrschluss bedeutet das, dass ich es gerne so hätte, dass das was über
 *    dem äußeren ListDetail ist und der List-Teil des ListDetail irgendwie
 *    optisch weiter im Hintergrund ist. […] Der Hintergrund des inneren
 *    ListDetail […] soll davon nicht betroffen sein."
 *
 * The two planes are already in the theme and need no new colour: `--color-surface`
 * is white / zinc-900 (FRONT) and `--color-bg-canvas` is zinc-50 / zinc-950
 * (BEHIND). So „ein leichtes helles Grau" in light mode and its dark counterpart
 * fall out of the tokens, and every theme brings its own pair.
 *
 * Deliberately OPT-IN, not the default: the rule is about the OUTER ListDetail of
 * a window. A ListDetail nested inside that detail pane must stay all-front, or
 * the recession stacks and the inner list looks disabled. Since the inner one
 * simply does not carry the class, nesting is safe by construction — no
 * `:not(.cbui-list-detail-recessed *)` gymnastics, and no dependency on which of
 * the two is parsed last.
 *
 * ⚠ The gutter belongs to NEITHER plane — see the divider block below. Matthias
 * asked for it explicitly („muss sich von beiden abheben"), and with two
 * different planes on the two sides that is also the only choice that survives:
 * tinted to one of them, the divider disappears on that side and looks like the
 * other pane starts 9px late. */
.cbui-list-detail-recessed {
    background-color: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-tabnav-fade-bg: var(--color-bg-canvas, var(--color-bg-base));
}
.cbui-list-detail-recessed > .cbui-list-detail-panes > .cbui-list-detail-list,
.cbui-list-detail-recessed > .cbui-list-detail-toolbar {
    background-color: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-tabnav-fade-bg: var(--color-bg-canvas, var(--color-bg-base));
}
.cbui-list-detail-recessed > .cbui-list-detail-panes > .cbui-list-detail-detail {
    background-color: var(--color-surface, var(--color-bg-elevated));
    --cbui-tabnav-fade-bg: var(--color-surface, var(--color-bg-elevated));
}
/* `plane="recessed-detail"` (B4, Matthias 2026-10-29): the inversion of the plane above.
 * The LIST pane is the front surface (the document being worked on, e.g. a playbook),
 * the DETAIL pane is the narrow side panel on the canvas plane. Own class, so the
 * `recessed-list` rules stay byte-identical; the bars above the layout stay untinted
 * (they sit with the white list). */
.cbui-list-detail-recessed-detail {
    background-color: var(--color-surface, var(--color-bg-elevated));
}
.cbui-list-detail-recessed-detail > .cbui-list-detail-panes > .cbui-list-detail-list {
    background-color: var(--color-surface, var(--color-bg-elevated));
    --cbui-tabnav-fade-bg: var(--color-surface, var(--color-bg-elevated));
}
.cbui-list-detail-recessed-detail > .cbui-list-detail-panes > .cbui-list-detail-detail {
    background-color: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-tabnav-fade-bg: var(--color-bg-canvas, var(--color-bg-base));
}
/* CB-1965 / Punkt A (Matthias, 2026-09-04): whatever sits ABOVE the outer recessed
 * ListDetail inside the same window root — the main menubar and the filter band —
 * steps back WITH its list: the same plane, so bar + list read as one background
 * and the detail pane as the one sheet in front. The plane comes from the theme
 * tokens (`--color-bg-canvas`), not from a special colour, and it is decided HERE
 * once instead of in every Bringer.
 *
 * `--bar-bg` is read by the bar (bar.css) and inherits through a `display: contents`
 * form; the filter band declares its own `--cbui-filter-area-bg` on the element
 * itself, so that one is set on the element. Specificity is 0 (`:where`) on purpose:
 * a Bringer that sets `--bar-bg` directly on its bar still wins. */
:where(:has(> .cbui-list-detail-recessed) > :not(.cbui-list-detail)) {
    --bar-bg: var(--color-bg-canvas, var(--color-bg-base));
}
:where(:has(> .cbui-list-detail-recessed) > :not(.cbui-list-detail)) .cbui-filter-area,
:where(:has(> .cbui-list-detail-recessed) > .cbui-filter-area) {
    --cbui-filter-area-bg: var(--color-bg-canvas, var(--color-bg-base));
}
/* CBUI rule 16 (2026-10-29, corrected) — the INNER layer. `ListDetail level="inner"`
 * (class `cbui-list-detail-inner`, which also carries `-recessed`, so the inner LIST
 * pane is canvas and the sibling rule above already tints bars beside it) sits inside
 * a detail pane. The host detail (the outer resource head, Tabnav bar, menubar) is NOT
 * tinted: it stays on the front surface. What is tinted is the top of the inner
 * layout's own DETAIL pane: its resource head, its action menubar and a Tabnav
 * menubar row, found as a direct child of the pane or of up to three wrapper levels
 * (turbo-frame / div) below it. The inner detail BODY stays `--color-surface`
 * (white / dark 38). Rows deeper in the body are not touched (not children of those
 * wrappers) and a layout inside an inner one stays flat (no third level). Tokens only:
 * light, dark and every flavor follow, radii are not touched. */
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > .cbui-resource-header,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > .cbui-bar,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > .cbui-tabnav > .cbui-tabnav-strip-wrapper,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > .cbui-resource-header,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > .cbui-bar,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > .cbui-tabnav > .cbui-tabnav-strip-wrapper,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-resource-header,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-bar,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-tabnav > .cbui-tabnav-strip-wrapper,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-resource-header,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-bar,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-tabnav > .cbui-tabnav-strip-wrapper {
    --bar-bg: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-tabnav-bg: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-tabnav-fade-bg: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-resource-header-sticky-bg: var(--color-bg-canvas, var(--color-bg-base));
}
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > .cbui-resource-header,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > .cbui-resource-header,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-resource-header,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-resource-header {
    background-color: var(--color-bg-canvas, var(--color-bg-base));
}
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > .cbui-filter-area,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > .cbui-filter-area,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-filter-area,
.cbui-list-detail-inner > .cbui-list-detail-panes > .cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-filter-area {
    --cbui-filter-area-bg: var(--color-bg-canvas, var(--color-bg-base));
}
/* Head zone sticks — ALWAYS, in both levels and both plane directions (Matthias 2026-10-29: „Resource-Header
 * und Menubar müssen IMMER sticky sein, auch im inneren ListDetail"). The DETAIL PANE is the scroller
 * (`overflow-y: auto` above); the head zone is whatever sits at its top: the ResourceHeader (sticky by default,
 * `resource-header.css`, publishes its height as --cbui-resource-header-sticky-offset), then the action menubar
 * (here) or the underline Tabnav (`tabnav.css`). Direct child of the pane or up to three wrapper levels below it
 * (turbo-frame / div), the same reach as the tint rules above. Colour: the opaque layer of the zone — the front
 * surface, or the canvas tint when rule 16 sets --bar-bg — so scrolled content never shows through; the z-index
 * stays below popovers and the load skeleton (z 2). The offset is reset on every pane: an inner pane must not
 * inherit the OUTER head's height (the head that sets it on a wrapper still wins). */
.cbui-list-detail-detail { --cbui-resource-header-sticky-offset: 0px; }
.cbui-list-detail-detail > .cbui-bar:not(.cbui-bar-toolbar-inline),
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > .cbui-bar:not(.cbui-bar-toolbar-inline),
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-bar:not(.cbui-bar-toolbar-inline),
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-bar:not(.cbui-bar-toolbar-inline) {
    position: sticky;
    top: var(--cbui-resource-header-sticky-offset, 0px);
    z-index: calc(var(--cbui-z-page, 0) + 1);
    background-color: var(--bar-bg, var(--color-surface, var(--color-bg-elevated)));
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}
/* A sticky head sticks only inside its parent's box. A Bringer wrapper that is `height: 100%` of the pane while
 * its content is taller (a run's timeline) ends that box early and the head scrolls away after one pane height.
 * The wrapper that holds the head therefore never ends before its content does. */
.cbui-list-detail-detail > :is(turbo-frame, div, section, article):has(> .cbui-resource-header),
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article):has(> .cbui-resource-header),
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article):has(> .cbui-resource-header) {
    min-height: fit-content;
}
/* A Bringer wrapper around the bar (a controller `div` that holds only the bar) is the sticky element then: a
 * sticky bar inside a parent of its own height has nowhere to stick. */
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(div, section):has(> .cbui-bar:not(.cbui-bar-toolbar-inline):only-child),
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(div, section):has(> .cbui-bar:not(.cbui-bar-toolbar-inline):only-child),
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(div, section):has(> .cbui-bar:not(.cbui-bar-toolbar-inline):only-child) {
    position: sticky;
    top: var(--cbui-resource-header-sticky-offset, 0px);
    z-index: calc(var(--cbui-z-page, 0) + 1);
}
/* The filter band (rule 17) is the third part of the head zone: it sticks under the menubar. Its `top` is stacked by
 * list_detail_controller.js (heights of the sticky parts above it); opaque, so the layer tint (--cbui-filter-area-bg,
 * set by the rules above) shows and scrolled rows never do. Detail pane and list pane alike. */
.cbui-list-detail-detail > .cbui-filter-area,
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > .cbui-filter-area,
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-filter-area,
.cbui-list-detail-detail > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-filter-area,
.cbui-list-detail-list > .cbui-filter-area,
.cbui-list-detail-list > :is(turbo-frame, div, section, article) > .cbui-filter-area,
.cbui-list-detail-list > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-filter-area,
.cbui-list-detail-list > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > :is(turbo-frame, div, section, article) > .cbui-filter-area {
    position: sticky;
    top: var(--cbui-resource-header-sticky-offset, 0px);
    z-index: calc(var(--cbui-z-page, 0) + 1);
}
/* Stacked / single-pane: there is no „left and right" any more, so there is no
   front-and-behind either — one pane fills the width and a recession would just
   tint whatever happens to be showing. Both panes go back to the front plane. */
.cbui-list-detail-recessed.is-stack > .cbui-list-detail-panes > .cbui-list-detail-list,
.cbui-list-detail-recessed.is-single > .cbui-list-detail-panes > .cbui-list-detail-list {
    background-color: var(--color-surface, var(--color-bg-elevated));
    --cbui-tabnav-fade-bg: var(--color-surface, var(--color-bg-elevated));
}

/* ── Draggable divider ─────────────────────────────────────────────────────
 * A full-bleed separator between the panes: a 1px line spanning the whole shared
 * edge (no padding) with a centred grip. It IS the separator (the list carries no
 * border). A wide-ish hit zone makes it easy to grab; the line + grip tint on
 * hover / drag. Dragging it resizes the list (--cbui-ld-hsize / --cbui-ld-vsize),
 * persisted per window by the controller. Hidden single-pane.
 */
/* The gutter is its OWN step, distinct from BOTH panes (Matthias, 2026-09-04,
   in two goes):

     „Im Darkmode sehen die Anfasser zwischen List+Detail nicht ok aus. Da ist
      der Hintergrund zu dunkel, sodass es fast wie ein Fremdkörper wirkt. Das
      muss generell im UI / Theme gefixt werden."
     „Der Anfasser/Trenner zwischen List und Detail muss sich von beiden abheben."

   It was `transparent`, so the 9px strip showed the ListDetail ROOT, which is
   `--color-bg-canvas`. In light that is zinc-50 next to white — invisible. In
   dark it is zinc-950 between two zinc-900 panes: a black band down the middle
   of the surface, which is what reads as a foreign body.

   `--color-border` is the token that does it, and it is the right one by meaning
   as well: it is the LINE colour of every theme — the step that has to be
   legible on a surface AND on the canvas, because cards sit on both. So it is
   the one value already guaranteed to differ from both planes, in both schemes,
   in every theme.

   ⚠ Two BACKGROUND tokens were tried first and both failed, for the same
   reason: they are tuned to sit NEXT to the surfaces, not between them.
   Measured in the active theme — surface 255, recessed list 240 —
   `--color-bg-subtle` and `--color-border-muted` both resolve to 245, which is
   five values away from the list. Five values is not a difference.
   `--color-border` resolves to 229 and reads against both.

   `--cbui-ld-gutter` remains the seam for a consumer that wants a different
   step, so nobody has to override `background` and lose the fallback chain. */
.cbui-list-detail-divider {
    flex: 0 0 auto;
    align-self: stretch;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1; /* grip above pane content, below the load skeleton (z 2) */
    background: var(--cbui-ld-gutter, var(--color-border, var(--color-border-strong)));
    touch-action: none;
}
/* The 1px line is now the HOVER/DRAG signal, not the separator: the toned strip
   above already separates, and a second darker line inside it at rest reads as a
   seam within a seam. At rest it therefore matches the strip; on hover it tints
   (rule further down) and the affordance appears exactly when it is needed. */
.cbui-list-detail-divider::before {
    content: "";
    position: absolute;
    background: transparent;
    transition: background 0.12s ease;
}
/* The grip reads AGAINST the toned strip, so it takes the next step up in
   contrast rather than the border colour the strip itself now carries. */
.cbui-list-detail-divider-grip {
    position: relative;
    border-radius: var(--radius-full, 999px);
    background: var(--color-text-tertiary, var(--color-border-strong));
    transition: background 0.12s ease;
}
.cbui-list-detail-divider:hover::before,
.cbui-list-detail.is-dragging > .cbui-list-detail-panes > .cbui-list-detail-divider::before {
    background: color-mix(in srgb, var(--color-primary) 55%, var(--color-border));
}
.cbui-list-detail-divider:hover > .cbui-list-detail-divider-grip,
.cbui-list-detail.is-dragging > .cbui-list-detail-panes > .cbui-list-detail-divider > .cbui-list-detail-divider-grip {
    background: var(--color-primary);
}

/* Horizontal panes → a vertical divider (grab left-right).
 *
 * The `width`/`height` pair below is exactly why these selectors MUST stay
 * instance-scoped (CB-381): the two blocks set opposite axes, and a divider that
 * matched both — as a nested ListDetail's divider did — ended up 9px on BOTH,
 * i.e. a square. Neither block resets the other's axis, because within one
 * instance only one of them can ever apply. */
.cbui-list-detail-left  > .cbui-list-detail-panes > .cbui-list-detail-divider,
.cbui-list-detail-right > .cbui-list-detail-panes > .cbui-list-detail-divider,
.cbui-list-detail-auto:not(.is-stack) > .cbui-list-detail-panes > .cbui-list-detail-divider {
    width: 9px;
    cursor: col-resize;
}
.cbui-list-detail-left  > .cbui-list-detail-panes > .cbui-list-detail-divider::before,
.cbui-list-detail-right > .cbui-list-detail-panes > .cbui-list-detail-divider::before,
.cbui-list-detail-auto:not(.is-stack) > .cbui-list-detail-panes > .cbui-list-detail-divider::before {
    inset-block: 0;
    inset-inline-start: 50%;
    width: 1px;
    transform: translateX(-50%);
}
.cbui-list-detail-left  > .cbui-list-detail-panes > .cbui-list-detail-divider > .cbui-list-detail-divider-grip,
.cbui-list-detail-right > .cbui-list-detail-panes > .cbui-list-detail-divider > .cbui-list-detail-divider-grip,
.cbui-list-detail-auto:not(.is-stack) > .cbui-list-detail-panes > .cbui-list-detail-divider > .cbui-list-detail-divider-grip {
    width: 8px;
    height: 2.25rem;
}

/* Vertical panes → a horizontal divider (grab up-down). */
.cbui-list-detail-top    > .cbui-list-detail-panes > .cbui-list-detail-divider,
.cbui-list-detail-bottom > .cbui-list-detail-panes > .cbui-list-detail-divider,
.cbui-list-detail-auto.is-stack > .cbui-list-detail-panes > .cbui-list-detail-divider {
    height: 9px;
    cursor: row-resize;
}
.cbui-list-detail-top    > .cbui-list-detail-panes > .cbui-list-detail-divider::before,
.cbui-list-detail-bottom > .cbui-list-detail-panes > .cbui-list-detail-divider::before,
.cbui-list-detail-auto.is-stack > .cbui-list-detail-panes > .cbui-list-detail-divider::before {
    inset-inline: 0;
    inset-block-start: 50%;
    height: 1px;
    transform: translateY(-50%);
}
.cbui-list-detail-top    > .cbui-list-detail-panes > .cbui-list-detail-divider > .cbui-list-detail-divider-grip,
.cbui-list-detail-bottom > .cbui-list-detail-panes > .cbui-list-detail-divider > .cbui-list-detail-divider-grip,
.cbui-list-detail-auto.is-stack > .cbui-list-detail-panes > .cbui-list-detail-divider > .cbui-list-detail-divider-grip {
    height: 8px;
    width: 2.25rem;
}

/* During a drag: no text selection, instant resize. */
.cbui-list-detail.is-dragging { user-select: none; }

/* ── Detail pane ───────────────────────────────────────────────────────── */
.cbui-list-detail-detail {
    position: relative; /* anchor for the load skeleton overlay */
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    background-color: var(--color-surface, var(--color-bg-elevated));
    --cbui-tabnav-fade-bg: var(--color-surface, var(--color-bg-elevated));
}
.cbui-list-detail-detail > turbo-frame { display: block; }

/* ── Load skeleton (E) — an opaque overlay over the detail pane while the new
 *    value's frame loads, so the previous content doesn't linger. Inserted +
 *    cleared by the controller (never innerHTML on a <turbo-frame src>). ─────── */
.cbui-list-detail-skeleton {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: var(--spacing-md, 0.75rem);
    overflow: hidden;
    background-color: var(--color-surface, var(--color-bg-elevated));
}
.cbui-list-detail-skeleton[hidden] { display: none; }

/* ── Load error (CB-1940) — the same overlay slot as the skeleton, shown when the detail
 *    frame fails to load. Holds a CBUI EmptyState + Button cloned from the component's
 *    `loadError` template; this rule only places it. ──────────────────────────────── */
.cbui-list-detail-error {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--spacing-md, 0.75rem);
    background-color: var(--color-surface, var(--color-bg-elevated));
}
.cbui-list-detail-error[hidden] { display: none; }

/* ── Inspector layout (S53.3) ──────────────────────────────────────────────
 * `layout="inspector"`: the LIST is wide and primary (flex), the DETAIL is
 * an inspector with a draggable, persisted px width (--cbui-ld-dsize), and
 * hides — together with the divider — while no row is selected.
 *
 * The custom property is declared on `.cbui-list-detail-inspector` itself,
 * not just consumed as a `var(…, 380px)` fallback further down — the same
 * instance-boundary reasoning as `--cbui-ld-hsize` on `.cbui-list-detail`
 * above: a nested inspector must not inherit the outer instance's dragged
 * width through CSS custom-property inheritance.
 *
 * `:not(.is-single)` is load-bearing, not decoration (CB-381 sibling trap):
 * below collapseAt the panes are a COLUMN, and a bare `flex: 0 0 380px`
 * there would set the detail's flex-basis on the CROSS axis, i.e. a 380px
 * HEIGHT — not a width — for the single-pane detail view. The specificity
 * this buys, (0,4,0), also wins outright over the per-placement sizing
 * rules above ((0,3,0)) regardless of source order — deliberate, so the
 * inspector form does not depend on staying textually after them.
 *
 * The empty/no-selection rule keeps the LIST on the canvas plane while the
 * detail is hidden: no new selector is needed for that — the inspector
 * never gets `is-stack` (see the controller), so the existing
 * `.cbui-list-detail-recessed > .cbui-list-detail-panes > .cbui-list-detail-list`
 * rule above already applies unchanged. This is a DELIBERATE choice
 * (LEITFADEN "Fallen"), not a fallback: "Detail ausgeblendet" in the
 * inspector is its own state, not a further single-pane collapse — W1 (a
 * still-open decision elsewhere in the track) may later flip exactly the
 * rule below to put the list on the FRONT plane instead while empty.
 */
.cbui-list-detail-inspector { --cbui-ld-dsize: 380px; }
/* CB-1926/D3: a manual top/bottom placement sizes the inspector by
 * --cbui-ld-vsize instead (the same grow-ratio rule the 'split' layout's own
 * top/bottom already uses, further up in this file) — excluded here via
 * `:not(:where(...))`, which MUST keep this selector's specificity exactly
 * where it was (0,4,0): a plain `:not(.cbui-list-detail-top):not(.cbui-
 * list-detail-bottom)` would raise it to (0,6,0) and beat the focus-mode
 * override `.is-detail-expanded ... { flex: 1 1 auto }` (0,5,0), silently
 * breaking focus mode for auto/left/right (traced in the CB-1926 plan). */
.cbui-list-detail-inspector:not(.is-single):not(:where(.cbui-list-detail-top, .cbui-list-detail-bottom)) > .cbui-list-detail-panes > .cbui-list-detail-list { flex: 1 1 auto; }
.cbui-list-detail-inspector:not(.is-single):not(:where(.cbui-list-detail-top, .cbui-list-detail-bottom)) > .cbui-list-detail-panes > .cbui-list-detail-detail { flex: 0 0 var(--cbui-ld-dsize, 380px); }
/* `:not(.cbui-list-detail-visibility-toggle)` (CB-1901) — this rule is the
 * SELECTION-mode hide rule only. In toggle mode visibility is governed by
 * `is-inspector-shown` instead (the block further down); without this
 * exclusion a toggle-mode instance shown-but-nothing-selected would still be
 * hidden here, because it never gets `is-detail-active` from a selection. */
.cbui-list-detail-inspector:not(.is-single):not(.is-detail-active):not(.cbui-list-detail-visibility-toggle) > .cbui-list-detail-panes > .cbui-list-detail-detail,
.cbui-list-detail-inspector:not(.is-single):not(.is-detail-active):not(.cbui-list-detail-visibility-toggle) > .cbui-list-detail-panes > .cbui-list-detail-divider {
    display: none;
}

/* ── detailVisibility="toggle" (S53.3 geändert, CB-1901) ─────────────────────
 * Visibility is governed by `is-inspector-shown` (set/cleared by the switch,
 * NEVER by a selection) instead of `is-detail-active`. `is-detail-active`
 * keeps its plain meaning here — "the detail pane has content" — so it still
 * drives which CHILD of the detail pane shows (the empty state vs. the real
 * content), just not whether the pane itself is visible.
 *
 * Specificity: the hide rule below is (0,6,0) — one more class than the
 * selection-mode hide rule above (0,5,0 once `:not(.cbui-list-detail-
 * visibility-toggle)` is counted) — and the two empty/content visibility
 * rules are (0,5,0)/(0,4,0), both beating the (0,4,0)/(0,3,0) sizing rules
 * above regardless of source order, the same discipline the inspector block
 * itself documents.
 *
 * `:not(.is-detail-expanded)` is load-bearing (decision 4): Focus (S53.14)
 * must be able to show the detail even while the toggle preference is
 * "hidden" — `expandDetail()` adds `is-detail-active`+`is-detail-expanded`
 * unconditionally, and this exemption lets the expanded rule further below
 * win without any JS touching the persisted toggle state.
 *
 * `:not(.is-single)` stays required for the identical CB-381 cross-axis
 * reason as every other side-by-side rule in this file: below collapseAt the
 * panes are a column, and this must never apply there — S53.13 keeps the
 * single-pane + Back behaviour completely unchanged. */
.cbui-list-detail-inspector.cbui-list-detail-visibility-toggle:not(.is-single):not(.is-inspector-shown):not(.is-detail-expanded) > .cbui-list-detail-panes > .cbui-list-detail-detail,
.cbui-list-detail-inspector.cbui-list-detail-visibility-toggle:not(.is-single):not(.is-inspector-shown):not(.is-detail-expanded) > .cbui-list-detail-panes > .cbui-list-detail-divider {
    display: none;
}
/* The empty state and the real content never show at once: `is-detail-active`
 * decides which one the detail pane carries, independent of whether the pane
 * itself is visible right now. */
.cbui-list-detail-visibility-toggle.is-detail-active > .cbui-list-detail-panes > .cbui-list-detail-detail > .cbui-list-detail-empty { display: none; }
/* CB-2957: a detail that has content without a selection keeps it visible. */
.cbui-list-detail-visibility-toggle:not(.is-detail-active):not(.cbui-list-detail-detail-without-selection) > .cbui-list-detail-panes > .cbui-list-detail-detail > :not(.cbui-list-detail-empty):not(.cbui-list-detail-skeleton) { display: none; }
.cbui-list-detail-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    padding: var(--spacing-md, 0.75rem);
}

/* ── Focus state (S53.14): `is-detail-expanded` ─────────────────────────────
 * The detail takes (almost) the whole width; the list + divider are hidden
 * entirely (reachable again via `restoreDetail()`/`closeDetail()`). A pure
 * CSS override on top of the sizing rules above - the detail's flex-basis is
 * overridden to fill the row instead of `var(--cbui-ld-dsize)`, so the
 * persisted width is NEVER read, written or clamped while expanded, and
 * `restoreDetail()` needs no JS restore step: removing the class alone puts
 * the detail straight back at the size --cbui-ld-dsize already holds.
 *
 * Five classes on the selector (0,5,0) - one more than the sizing rules
 * above (0,4,0) - so this wins regardless of source order, the same
 * discipline as the CB-381 sibling-trap comment above. `:not(.is-single)`
 * stays required for the identical cross-axis reason: below collapseAt the
 * panes are a column and this must not touch the single-pane layout, which
 * already shows the detail at full width on its own (see below). */
.cbui-list-detail-inspector.is-detail-expanded:not(.is-single) > .cbui-list-detail-panes > .cbui-list-detail-list,
.cbui-list-detail-inspector.is-detail-expanded:not(.is-single) > .cbui-list-detail-panes > .cbui-list-detail-divider {
    display: none;
}
.cbui-list-detail-inspector.is-detail-expanded:not(.is-single) > .cbui-list-detail-panes > .cbui-list-detail-detail {
    flex: 1 1 auto;
}

/* ── Single-pane collapse (width < collapseAt / T2) — the forced smartphone
 *    mode for EVERY placement. Placed AFTER the per-placement rules so its
 *    (0,2,0) selectors win by source order over the same-specificity sizing.
 *    Child-combinator scoped like the placement rules (CB-381): as descendant
 *    selectors, an OUTER instance going single-pane hid the INNER instance's
 *    detail pane and divider too — the nested list would have shown its rows
 *    with no detail and no way to get one. ─── */
.cbui-list-detail.is-single > .cbui-list-detail-panes { flex-direction: column; }
.cbui-list-detail.is-single > .cbui-list-detail-panes > .cbui-list-detail-list {
    flex: 1 1 auto;
    max-height: none;
    border: 0;
}
.cbui-list-detail.is-single > .cbui-list-detail-panes > .cbui-list-detail-detail { display: none; }
.cbui-list-detail.is-single > .cbui-list-detail-panes > .cbui-list-detail-divider { display: none; }
.cbui-list-detail.is-single.is-detail-active > .cbui-list-detail-panes > .cbui-list-detail-list { display: none; }
.cbui-list-detail.is-single.is-detail-active > .cbui-list-detail-panes > .cbui-list-detail-detail { display: block; }

/* Toolbar collapses to nothing in the list view (placement is meaningless
   single-pane); it reappears only to carry Back once a row is chosen. */
.cbui-list-detail.is-single > .cbui-list-detail-toolbar { display: none; }
.cbui-list-detail.is-single.is-detail-active > .cbui-list-detail-toolbar { display: flex; }
.cbui-list-detail.is-single.is-detail-active > .cbui-list-detail-toolbar > .cbui-list-detail-back { display: inline-flex; }
.cbui-list-detail.is-single > .cbui-list-detail-toolbar > .cbui-list-detail-toolbar-placement { display: none; }

/* ── showToolbar=false: Back-only minimal bar (CB-668) ────────────────────
 * A consumer that suppresses the built-in toolbar (WorkspaceShell hoists the
 * placement switch into the window header — Values does this in all three
 * views) still renders THIS bar in the DOM, carrying only Back — see the
 * component's Twig. Unlike the full toolbar above (visible by default, hidden
 * only single-pane-without-selection), the minimal bar is the other way
 * round: hidden by default at every width, shown ONLY in the collapsed
 * single-pane + detail-active state (the `.is-single.is-detail-active >
 * .cbui-list-detail-back` rule right above still fires identically for the
 * inner button). Without this the base `.cbui-list-detail-toolbar { display:
 * flex }` would show an empty, placement-less bar at every width whenever
 * showToolbar=false — not what a consumer suppressing the toolbar wants. */
.cbui-list-detail-toolbar.cbui-list-detail-toolbar-minimal { display: none; }
.cbui-list-detail.is-single.is-detail-active > .cbui-list-detail-toolbar.cbui-list-detail-toolbar-minimal { display: flex; }

/* ── ListDetailBackControl (§ 1a) — the EXTERNAL Back, hoisted into a
 * Bringer's own toolbar row instead of this component's ────────────────────
 * Its Twig sets the native `hidden` attribute as the authored default and
 * `list_detail_back_control_controller.js` flips `el.hidden` at runtime — no
 * `display` is set here on purpose. An unconditional `display` on this class
 * would OUTRANK the native `[hidden]` UA rule (author styles beat UA styles),
 * which is a documented CBUI anti-pattern (see chip-stack.css's „explicit
 * display beats [hidden]"). This rule only reasserts what `[hidden]` already
 * does, at author-stylesheet specificity, so a later unconditional `display`
 * rule added to this class for other reasons cannot silently un-hide it. */
.cbui-list-detail-back-control[hidden] { display: none; }

/* ── ListDetailInspectorToggle — hidden below collapseAt (CB-1912, S53.13) ──
 * Same anti-pattern guard as the back control above: the controller sets the
 * native `hidden` attribute when `is-single`, and this rule only reasserts
 * what `[hidden]` already does, at author-stylesheet specificity, so a
 * component display rule elsewhere cannot silently un-hide it. */
.cbui-list-detail-inspector-toggle[hidden] { display: none; }

/* ── ListDetailShowDetailControl — hidden unless below collapseAt (CB-2957) ──
 * Same guard as the back control: an author rule must not un-hide it. */
.cbui-list-detail-show-detail-control[hidden] { display: none; }

/* ── ListDetailPlacementSwitch (CB-1926/D2) ─────────────────────────────── */
.cbui-list-detail-placement-switch { display: inline-flex; align-items: center; }

/* CB-1937: the placement switch an inner ListDetail hoists into its Tabnav trail. */
.cbui-list-detail-hoisted-placement { display: inline-flex; align-items: center; }
.cbui-list-detail-hoisted-placement[hidden] { display: none; }
.cbui-list-detail-hoisted-controls { display: inline-flex; align-items: center; gap: var(--spacing-1, 0.25rem); }
.cbui-list-detail-hoisted-controls[hidden] { display: none; }

/* ── Full height: the host contract (CB-1828) ─────────────────────────────
 * `.cbui-list-detail-host` is a class a CONSUMER (the WorkspaceShell's
 * `.cbui-bringer-window-body`, or any other page) puts on the element that
 * wraps a ListDetail, to opt into a definite height chain down to it — so
 * that an ordinary wrapper `<div>` between the host and the component no
 * longer breaks the chain (CB-1637's "so nicht" case). Every plain wrapper
 * in between becomes a flex column that passes the height through; the
 * ListDetail itself gets `flex: 1 1 auto; min-height: 0` to fill it.
 *
 * `@layer components` + `:where(…)` (specificity 0), same reasoning as the
 * window-chain fix in workspace-shell's bringer-window.css: existing rules —
 * layered or not — must keep winning, this is only the default for the gap.
 *
 * `:not(.cbui-list-detail, .cbui-list-detail *, [hidden], template)` keeps
 * the rule OUT of:
 *   - the component's own internals (CB-381: a ListDetail nested in this
 *     one's detail pane — Values' Versionen list — would otherwise also
 *     match, turning `.cbui-list-detail-panes` into a column);
 *   - `[hidden]` elements — an unconditional `display` beats the native
 *     `[hidden]` UA rule, which would re-show a hidden Tabnav panel that
 *     happens to contain a ListDetail (the Agent window's versions tab). */
@layer components {
    :where(.cbui-list-detail-host:has(.cbui-list-detail)) {
        display: flex;
        flex-direction: column;
    }
    :where(.cbui-list-detail-host :not(.cbui-list-detail, .cbui-list-detail *, [hidden], template):has(.cbui-list-detail)) {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
    }
    :where(.cbui-list-detail-host .cbui-list-detail:not(.cbui-list-detail *)) {
        flex: 1 1 auto;
        min-height: 0;
    }
}

/* cbui/components/filter */
/* cbui-filter — filter bar above a list / table.
 *
 * Composition primitives:
 *   .cbui-filter           outer container (flex row, wraps)
 *   .cbui-filter-chip      active-filter chip (FilterChip — dimension chip
 *                          with toggle / edit / remove zones)
 *   .cbui-filter-search    search input (FilterSearch)
 *   .cbui-filter-clear     "Clear all" trailing button (hidden when bar is empty)
 *   .cbui-filter-actions   trailing slot (sort dropdown, view toggle)
 */

@layer components {

  /* ────────────────────────────────────────────────────────────────────── */
  /*  Bar container                                                         */
  /* ────────────────────────────────────────────────────────────────────── */
  .cbui-filter {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 0.5rem 0.5rem;
      padding-block: 0.5rem;
      color: var(--color-text);
  }

  .cbui-filter-controls {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 0.5rem;
      flex: 1 1 auto;
      min-width: 0;
  }

  /* Stacked layout — vertical column. Used for the Advanced builder where
     each chip is a [label op value] compound and reads better one per line.
     Also the right choice for a filters sidebar / drawer. The clear-all
     and actions slots float to the start under the column. */
  .cbui-filter-layout-stacked {
      flex-direction: column;
      align-items: stretch;
  }
  .cbui-filter-layout-stacked .cbui-filter-controls {
      flex-direction: column;
      align-items: stretch;
      gap: 0.5rem;
  }
  /* Chip-hosts inside a stacked layout don't stretch — they hug their
     content at the leading edge so the column reads tidy. */
  .cbui-filter-layout-stacked .cbui-filter-controls > .cbui-filter-chip-host,
  .cbui-filter-layout-stacked .cbui-filter-controls > .cbui-filter-chip {
      align-self: flex-start;
  }
  .cbui-filter-layout-stacked .cbui-filter-actions {
      margin-inline-start: 0;
      align-self: flex-start;
  }

  .cbui-filter-actions {
      display: inline-flex;
      align-items: center;
      gap: 0.5rem;
      flex-shrink: 0;
      margin-inline-start: auto;
  }

  .cbui-filter-clear {
      display: inline-flex;
      align-items: center;
      gap: 0.375rem;
      padding: 0.25rem 0.5rem;
      border-radius: var(--radius-md, 0.5rem);
      background-color: transparent;
      border: 0;
      color: var(--color-text-secondary);
      font-size: var(--font-size-md, 1rem);
      cursor: pointer;
      transition: background-color 150ms ease, color 150ms ease;
  }
  .cbui-filter-clear:hover {
      background-color: color-mix(in srgb, currentColor 8%, transparent);
      color: var(--color-text);
  }
  .cbui-filter-clear:focus-visible {
      outline: 2px solid var(--color-focus-ring);
      outline-offset: 2px;
  }

  /* Collapsible region — show / hide is driven by the
     data-cb-collapsed="true|false" attribute, the same attribute a CB-22
     SplitButton (`for=`) trigger mutates. The visual is ours. */
  .cbui-filter-collapsible[data-cb-collapsed="true"] {
      display: none;
  }

  /* Footer row for clearAllPlacement="footer" — spans the full width on its
     own line, clear button trailing. Collapses to nothing when its clear
     button is hidden (empty bar) so it never adds idle chrome. */
  .cbui-filter-footer {
      flex: none;
      width: 100%;
      display: flex;
      justify-content: flex-end;
      margin-top: 0.25rem;
  }
  .cbui-filter-footer:has(> .cbui-filter-clear[hidden]) {
      display: none;
  }

  /* ────────────────────────────────────────────────────────────────────── */
  /*  FilterChip — the dimension chip with three click zones.               */
  /*                                                                        */
  /*  Anatomy:                                                              */
  /*    .cbui-filter-chip                outer pill, tone-aware             */
  /*      .cbui-filter-chip-toggle       toggle zone (icon + label)         */
  /*        .cbui-filter-chip-icon         leading icon                     */
  /*        .cbui-filter-chip-label        label cell ("Status:")           */
  /*      .cbui-popover-wrapper           wraps the edit zone (when         */
  /*                                       editable=true)                   */
  /*        .cbui-filter-chip-edit         editor trigger button            */
  /*          .cbui-filter-chip-value        value cell ("Aktiv")           */
  /*          .cbui-filter-chip-caret        chevron                        */
  /*      .cbui-filter-chip-value           static value cell (when         */
  /*        .cbui-filter-chip-value-static    editable=false)               */
  /*      .cbui-filter-chip-remove        × button                          */
  /* ────────────────────────────────────────────────────────────────────── */

  /* ── Host wrapper — column container for pill + error message. ───────
     The chip pill is one inline-flex row; the error message is rendered
     BELOW the pill (sibling, not nested in the dropdown panel) so it's
     ALWAYS visible — independent of editor open/closed state. */
  .cbui-filter-chip-host {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
    min-width: 0;
  }

  .cbui-filter-chip {
    --cbui-filter-chip-tone: var(--color-text);
    --cbui-filter-chip-bg:   color-mix(in srgb, var(--cbui-filter-chip-tone)  8%, transparent);
    --cbui-filter-chip-border: color-mix(in srgb, var(--cbui-filter-chip-tone) 30%, transparent);
    --cbui-filter-chip-text: var(--color-text);
    --cbui-filter-chip-divider: color-mix(in srgb, var(--cbui-filter-chip-tone) 22%, transparent);

    /* The chip is the dropdown / popover panels' positioning context.
       Both Dropdown wrappers AND the Popover wrapper inside the chip
       render with `position: static` (override below), so their panels
       anchor at the CHIP'S left edge — every editor opens at the same
       x-coordinate (the label position), regardless of which cell was
       clicked. The whole chip is the referent; the menu belongs to the
       chip, not to a single segment. */
    position: relative;

    display: inline-flex;
    align-items: stretch;
    flex-shrink: 0;
    border: 1px solid var(--cbui-filter-chip-border);
    border-radius: 9999px;
    background-color: var(--cbui-filter-chip-bg);
    color: var(--cbui-filter-chip-text);
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    line-height: 1.2;
    white-space: nowrap;
  }

  /* Drop the per-Dropdown / Popover positioning context inside the chip
     so panels anchor at the chip itself, not at the segment trigger.
     For align-start, panel `left: 0` then refers to the chip's leading
     edge. The Popover wrapper used by Simple mode follows the same
     contract. */
  .cbui-filter-chip > .cbui-dropdown,
  .cbui-filter-chip > .cbui-popover-wrapper {
    position: static;
  }
  /* The Popover panel uses `.cbui-popover-panel` (not -panel-align-start).
     Force its leading edge to align with the chip's `left: 0`. */
  .cbui-filter-chip > .cbui-popover-wrapper > .cbui-popover-panel {
    left: 0;
    /* Popover renders below the chip via `top: calc(100% + 0.3rem)` from
       its own CSS — that still works since the chip is now the positioning
       context. */
  }

  /* ── Dropdown width inside FilterChip ─────────────────────────────────
     Panels by default size to their content (no minimum forced — the
     global Dropdown's min-width: var(--cbui-dd-min-w-sm) would force
     10rem on every panel which is too much for narrow operator lists).
     When the host sets `--cbui-filter-chip-dropdown-w` (via the
     `dropdownWidth` prop), panels lock to that width. */
  .cbui-filter-chip-host .cbui-dropdown-panel,
  .cbui-filter-chip > .cbui-popover-wrapper > .cbui-popover-panel {
    min-width: 0;
  }
  .cbui-filter-chip-host[style*="--cbui-filter-chip-dropdown-w"] .cbui-dropdown-panel,
  .cbui-filter-chip-host[style*="--cbui-filter-chip-dropdown-w"] .cbui-popover-panel {
    width: var(--cbui-filter-chip-dropdown-w);
  }

  /* ── Zone buttons — share defaults ──────────────────────────────────── */
  .cbui-filter-chip-toggle,
  .cbui-filter-chip-edit,
  .cbui-filter-chip-operator-trigger,
  .cbui-filter-chip-combined-trigger,
  .cbui-filter-chip-remove {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    background-color: transparent;
    border: 0;
    color: inherit;
    font: inherit;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease;
  }

  /* Toggle zone — icon + label. Rounded LEFT corners regardless of size
     (pill ends always stay pill — full-radius is intrinsic to the chip). */
  .cbui-filter-chip-toggle {
    padding: 0.3125rem 0.5rem 0.3125rem 0.625rem;
    border-radius: 9999px 0 0 9999px;
  }
  .cbui-filter-chip-icon-only {
    padding-inline: 0.5rem;
    cursor: default;
  }
  .cbui-filter-chip-icon {
    display: inline-flex;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
  }
  .cbui-filter-chip-label {
    opacity: 0.75;
    font-weight: var(--font-weight-medium, 500);
  }

  /* Editor zones — Popover-wrapper (simple mode) AND Dropdown-wrapper
     (conditional / combined mode) both render flush inside the chip with
     a hairline divider on the leading edge. */
  .cbui-filter-chip .cbui-popover-wrapper,
  .cbui-filter-chip > .cbui-dropdown {
    display: inline-flex;
    align-items: stretch;
    border-inline-start: 1px solid var(--cbui-filter-chip-divider);
  }
  /* Dropdown's own trigger wrapper needs to stretch too so the seam reads
     as one continuous pill row. */
  .cbui-filter-chip > .cbui-dropdown > .cbui-dropdown-trigger {
    display: inline-flex;
    align-items: stretch;
  }

  /* Unified dropdown panel padding inside FilterChip — every panel section
     (DropdownLabel, items, FormRow-block, raw input-block) sits in the
     same horizontal gutter. Values match the size="sm" panel defaults
     of .cbui-dropdown so the panel still reads as a normal Dropdown but
     the chip's content authors don't have to inline padding everywhere.
     Inset rules apply to the panel's direct children only — nested
     dropdown items (DropdownItem) already carry their own padding. */
  .cbui-filter-chip .cbui-dropdown-panel > .cbui-dropdown-label,
  .cbui-filter-chip .cbui-dropdown-panel > .cbui-dropdown-separator {
    /* These are container-managed elements; let them keep their own
       gutter. */
  }
  .cbui-filter-chip .cbui-dropdown-panel > div:not([class*="cbui-dropdown-"]) {
    /* Free-floating content blocks (Pills wrapper, FormRow wrapper,
       Input wrapper) — give them the same horizontal gutter as
       DropdownItem / DropdownLabel. */
    padding: 0.375rem 0.625rem;
  }
  /* Tighten when block ends the panel — keep bottom flush with the
     panel's natural padding. */
  .cbui-filter-chip .cbui-dropdown-panel > div:not([class*="cbui-dropdown-"]):last-child {
    padding-bottom: 0.5rem;
  }
  /* Adjacent free blocks need a tighter vertical rhythm — drop the
     trailing padding on the prior block to avoid double-spacing. */
  .cbui-filter-chip .cbui-dropdown-panel > div:not([class*="cbui-dropdown-"]) + div:not([class*="cbui-dropdown-"]) {
    padding-top: 0;
  }

  /* Edit / Operator / Combined trigger buttons — all share padding + weight
     so neighbours read as equal cells. */
  .cbui-filter-chip-edit,
  .cbui-filter-chip-operator-trigger,
  .cbui-filter-chip-combined-trigger {
    padding: 0.3125rem 0.5rem;
    font-weight: var(--font-weight-semibold, 600);
  }
  .cbui-filter-chip-combined-trigger { padding-inline: 0.625rem; }

  .cbui-filter-chip-edit:focus-visible,
  .cbui-filter-chip-operator-trigger:focus-visible,
  .cbui-filter-chip-combined-trigger:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -2px;
    z-index: 1;
  }

  .cbui-filter-chip-value {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
  }
  .cbui-filter-chip-value-static {
    padding: 0.3125rem 0.625rem;
    border-inline-start: 1px solid var(--cbui-filter-chip-divider);
    font-weight: var(--font-weight-semibold, 600);
  }

  /* Operator text — the "ist", "zwischen", "≥" label. Lower contrast than
     the value, but readable. Combined mode bolds it slightly more so the
     reader can parse the "op value" composition at a glance. */
  .cbui-filter-chip-operator-text {
    color: var(--color-text);
    font-weight: var(--font-weight-semibold, 600);
  }
  /* Combined-trigger separator between operator and value halves — a
     hairline vertical divider so the reader can parse the two pieces at
     a glance. Same colour as the chip's segment divider so the visual
     language is consistent. Refreshes live as the controller swaps the
     adjacent text content. */
  .cbui-filter-chip-combined-sep {
    display: inline-block;
    width: 1px;
    height: 0.85em;
    background-color: var(--cbui-filter-chip-divider);
    margin-inline: 0.375rem;
    vertical-align: middle;
  }
  /* ── Preset pills ────────────────────────────────────────────────────
     A preset is a button inside the value-editor that, on click, sets
     the chip's operator AND value at once. Only one preset can be
     active at a time (radio). Visually identical to choice-group chips
     but driven by buttons (not labels with radio inputs) because the
     side-effect is a chip-controller action, not a form-input change.

     Container: `[data-cbui-filter-chip-presets]` flex-wraps the pills.
     Pill: `.cbui-filter-chip-preset-pill`. Active state: the controller
     adds `.cbui-filter-chip-preset-active` on the chosen pill.

     Behaviour:
       - Click a preset → controller applies operator + value to chip.
       - Click another preset → previous deactivates, new one activates.
       - Modify the editor directly (e.g. type into NumberInput, toggle a
         value-pill) → no preset is implied active; the controller does
         not add `-active`.
   */
  [data-cbui-filter-chip-presets] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
  }
  .cbui-filter-chip-preset-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.3125rem 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 9999px;
    background-color: transparent;
    color: var(--color-text-secondary);
    font: inherit;
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    line-height: 1.2;
    cursor: pointer;
    transition: background-color 150ms ease, color 150ms ease;
    white-space: nowrap;
  }
  .cbui-filter-chip-preset-pill:hover {
    background-color: color-mix(in srgb, currentColor 8%, transparent);
    color: var(--color-text);
  }
  .cbui-filter-chip-preset-pill:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
  }
  .cbui-filter-chip-preset-pill.cbui-filter-chip-preset-active {
    background-color: color-mix(in srgb, var(--color-primary) 16%, transparent);
    border-color: color-mix(in srgb, var(--color-primary) 50%, transparent);
    color: var(--color-primary);
  }

  /* ── Validation surface ──────────────────────────────────────────────
     Lives as a SIBLING of the chip pill inside the host wrapper —
     ALWAYS visible, not nested in the dropdown panel. The host's
     column layout positions it directly under the pill. The chip's
     border tints danger via `.cbui-filter-chip-invalid` so the
     invalid state is visible even when the editor never opens. */
  .cbui-filter-chip-error {
    display: inline-flex;
    align-items: flex-start;
    gap: 0.375rem;
    padding: 0.3125rem 0.625rem;
    max-width: 100%;
    background-color: color-mix(in srgb, var(--color-danger) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--color-danger) 35%, transparent);
    border-radius: var(--radius-sm, 0.375rem);
    color: var(--color-danger);
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    line-height: 1.2;
    white-space: normal;
  }
  /* Tier-1 (CB-12c): the container ships `hidden` until a client constraint
     fails. An explicit `display` beats the UA `[hidden]` rule, so restore it
     here (class+attr 0,2,0 wins over the bare class 0,1,0). */
  .cbui-filter-chip-error[hidden] {
    display: none;
  }
  .cbui-filter-chip-error-icon {
    flex-shrink: 0;
    width: 1rem;
    height: 1rem;
    margin-top: 0.0625rem;
  }
  .cbui-filter-chip-error-message {
    min-width: 0;
    flex: 1 1 auto;
  }
  /* When the chip is invalid, the chip's tone goes danger so the border
     + value reads as a warning state. */
  .cbui-filter-chip-invalid {
    --cbui-filter-chip-tone: var(--color-danger);
  }
  .cbui-filter-chip-invalid .cbui-filter-chip-value,
  .cbui-filter-chip-invalid .cbui-filter-chip-combined-value {
    color: var(--color-danger);
  }

  /* Static fallbacks — used by display-only chips (e.g. FilterGroup demo
     conditions). Same padding + dividers as the interactive triggers so
     the chrome reads identical whether the operator is editable or not. */
  .cbui-filter-chip-operator-static,
  .cbui-filter-chip-combined-static {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.3125rem 0.5rem;
    border-inline-start: 1px solid var(--cbui-filter-chip-divider);
    font-weight: var(--font-weight-semibold, 600);
    cursor: default;
  }
  .cbui-filter-chip-combined-static { padding-inline: 0.625rem; }
  .cbui-filter-chip-combined-trigger .cbui-filter-chip-operator-text {
    margin-inline-end: 0.25rem;
  }
  .cbui-filter-chip-combined-value {
    /* Value half of the combined trigger inherits the chip's tone (set
       via .cbui-filter-chip-tone-X) — so primary chips render the value
       in primary, success in success, etc. */
    color: var(--cbui-filter-chip-tone);
  }

  .cbui-filter-chip-caret {
    flex-shrink: 0;
    opacity: 0.5;
  }
  .cbui-filter-chip-placeholder {
    opacity: 0.5;
    font-weight: var(--font-weight-medium, 500);
    font-style: italic;
  }

  /* Editor card inside the chip's popover — give the editor a sane minimum
     width so chip-rows / DateInput-pairs / NumberInput-pairs have room to
     breathe. Bound by max-width so very large enums wrap or scroll inside
     the card rather than running off-screen. The card's own padding gives
     a tidy inner gutter. */
  .cbui-filter-chip-editor {
    min-width: 14rem;
    max-width: 22rem;
  }

  /* Remove zone — × button. */
  .cbui-filter-chip-remove {
    padding: 0 0.5rem;
    border-inline-start: 1px solid var(--cbui-filter-chip-divider);
    border-radius: 0 9999px 9999px 0;
    opacity: 0.7;
  }
  .cbui-filter-chip-remove:hover {
    background-color: color-mix(in srgb, var(--cbui-filter-chip-tone) 14%, transparent);
    opacity: 1;
  }
  .cbui-filter-chip-remove:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -1px;
    z-index: 1;
  }

  /* ── Hover states for clickable zones. ───────────────────────────────── */
  .cbui-filter-chip-toggle[data-action]:hover,
  .cbui-filter-chip-edit:hover,
  .cbui-filter-chip-operator-trigger:hover,
  .cbui-filter-chip-combined-trigger:hover {
    background-color: color-mix(in srgb, var(--cbui-filter-chip-tone) 10%, transparent);
  }

  /* ── Toggle-zone focus. ──────────────────────────────────────────────── */
  .cbui-filter-chip-toggle[data-action]:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
  }

  /* ── Tone variants — recolour the chip's CSS variable. ───────────────── */
  .cbui-filter-chip-tone-primary { --cbui-filter-chip-tone: var(--color-primary); }
  .cbui-filter-chip-tone-success { --cbui-filter-chip-tone: var(--color-success); }
  .cbui-filter-chip-tone-warning { --cbui-filter-chip-tone: var(--color-warning); }
  .cbui-filter-chip-tone-danger  { --cbui-filter-chip-tone: var(--color-danger);  }
  .cbui-filter-chip-tone-info    { --cbui-filter-chip-tone: var(--color-info);    }
  .cbui-filter-chip-tone-accent  { --cbui-filter-chip-tone: var(--color-accent);  }
  .cbui-filter-chip-tone-muted   { --cbui-filter-chip-tone: var(--color-muted);   }
  .cbui-filter-chip-tone-neutral { --cbui-filter-chip-tone: var(--color-text-secondary); }

  /* Tone text colour — primary/accent/info/success/warning/danger get the
     tone colour on the value text. */
  .cbui-filter-chip-tone-primary,
  .cbui-filter-chip-tone-success,
  .cbui-filter-chip-tone-warning,
  .cbui-filter-chip-tone-danger,
  .cbui-filter-chip-tone-info,
  .cbui-filter-chip-tone-accent {
    color: var(--cbui-filter-chip-tone);
  }

  /* ── Sizes. ─────────────────────────────────────────────────────────── */
  .cbui-filter-chip-size-sm {
    font-size: var(--font-size-md, 1rem);
  }
  .cbui-filter-chip-size-sm .cbui-filter-chip-toggle,
  .cbui-filter-chip-size-sm .cbui-filter-chip-edit,
  .cbui-filter-chip-size-sm .cbui-filter-chip-operator-trigger,
  .cbui-filter-chip-size-sm .cbui-filter-chip-combined-trigger,
  .cbui-filter-chip-size-sm .cbui-filter-chip-value-static {
    padding-block: 0.0625rem;
  }
  .cbui-filter-chip-size-lg {
    font-size: var(--font-size-md, 1rem);
  }
  .cbui-filter-chip-size-lg .cbui-filter-chip-toggle,
  .cbui-filter-chip-size-lg .cbui-filter-chip-edit,
  .cbui-filter-chip-size-lg .cbui-filter-chip-operator-trigger,
  .cbui-filter-chip-size-lg .cbui-filter-chip-combined-trigger,
  .cbui-filter-chip-size-lg .cbui-filter-chip-value-static {
    padding-block: 0.4375rem;
  }

  /* ── Disabled state — filter is temporarily inactive. The chip stays in
     the bar but renders dimmed with a strikethrough on the value.
     NO `opacity` is used here: opacity on the wrapper would propagate to
     the floating popover panel and make the editor see-through. Instead
     we dim via explicit colour-mix on text + neutralise the bg/border. ─ */
  .cbui-filter-chip-disabled {
    --cbui-filter-chip-tone:    var(--color-text-secondary);
    --cbui-filter-chip-bg:      transparent;
    --cbui-filter-chip-border:  var(--color-border);
    --cbui-filter-chip-divider: var(--color-border);
    color: color-mix(in srgb, var(--color-text) 50%, transparent);
  }
  .cbui-filter-chip-disabled .cbui-filter-chip-value,
  .cbui-filter-chip-disabled .cbui-filter-chip-operator-text,
  .cbui-filter-chip-disabled .cbui-filter-chip-combined-value {
    text-decoration: line-through;
    text-decoration-thickness: 1px;
    text-decoration-color: color-mix(in srgb, currentColor 50%, transparent);
  }

  /* ── "+ Filter" / "+ Add" use-case — when label is missing AND value is
     missing AND editable=true, the chip degenerates to a placeholder. The
     same chip can serve as an "Add filter" affordance — pair it with the
     +border-dashed Button modifier in the consumer for a fully-dashed
     placeholder. Pattern-level, not component-level. ──────────────────── */
  .cbui-filter-chip-no-label.cbui-filter-chip-no-value {
    background-color: transparent;
    border-style: dashed;
  }

  /* ── A11y — reduced motion + high contrast. ─────────────────────────── */
  .a11y-reduced-motion .cbui-filter-chip-toggle,
  .a11y-reduced-motion .cbui-filter-chip-edit,
  .a11y-reduced-motion .cbui-filter-chip-remove {
    transition: none;
  }
  .a11y-high-contrast .cbui-filter-chip {
    border-width: 2px;
  }
  .a11y-high-contrast .cbui-filter-chip .cbui-popover-wrapper,
  .a11y-high-contrast .cbui-filter-chip-value-static,
  .a11y-high-contrast .cbui-filter-chip-remove {
    border-inline-start-width: 2px;
  }

  /* ────────────────────────────────────────────────────────────────────── */
  /*  FilterSearch — search input (unchanged from the previous version).    */
  /* ────────────────────────────────────────────────────────────────────── */
  .cbui-filter-search {
      --cbui-filter-search-py: 0.375rem;
      --cbui-filter-search-icon-size: 1rem;

      position: relative;
      display: inline-flex;
      align-items: center;
      min-width: 220px;
      max-width: 100%;
      background-color: var(--color-surface, var(--color-bg-elevated));
      border: 1px solid var(--color-border);
      border-radius: var(--input-radius, var(--radius-md, 0.5rem));
      transition: border-color 150ms ease, box-shadow 150ms ease;
  }
  .cbui-filter-search:focus-within {
      border-color: var(--color-focus-ring);
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-focus-ring) 25%, transparent);
  }
  .cbui-filter-search-icon {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: var(--cbui-filter-search-icon-size);
      height: var(--cbui-filter-search-icon-size);
      margin-inline-start: 0.625rem;
      color: var(--color-text-secondary);
      flex-shrink: 0;
  }
  .cbui-filter-search-input {
      flex: 1 1 auto;
      min-width: 0;
      padding: var(--cbui-filter-search-py) 0.625rem;
      background: transparent;
      border: 0;
      color: var(--color-text);
      font-size: var(--font-size-md, 1rem);
      outline: none;
  }
  .cbui-filter-search-input::placeholder { color: var(--color-text-tertiary, var(--color-text-secondary)); }
  .cbui-filter-search-input::-webkit-search-cancel-button { display: none; }
  .cbui-filter-search-clear {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 1.5rem;
      height: 1.5rem;
      margin-inline-end: 0.375rem;
      background: transparent;
      border: 0;
      border-radius: 9999px;
      color: var(--color-text-secondary);
      cursor: pointer;
      flex-shrink: 0;
  }
  .cbui-filter-search-clear:hover {
      background-color: color-mix(in srgb, currentColor 10%, transparent);
      color: var(--color-text);
  }
  .cbui-filter-search-size-sm { --cbui-filter-search-py: 0.25rem; }
  .cbui-filter-search-size-lg { --cbui-filter-search-py: 0.5rem; }

  .a11y-reduced-motion .cbui-filter-search,
  .a11y-reduced-motion .cbui-filter-search-clear { transition: none; }

  /* ────────────────────────────────────────────────────────────────────── */
  /*  FilterGroup — UND/ODER container for nested filter expressions.       */
  /*                                                                        */
  /*  Three frame variants:                                                 */
  /*    rail      → 2px border-inline-start + indent (default — subtle)     */
  /*    outlined  → full colored border around the body                     */
  /*    tinted    → outlined + subtle tone-mixed background                 */
  /*                                                                        */
  /*  Anatomy:                                                              */
  /*    .cbui-filter-group                outer container                   */
  /*      .cbui-filter-group-header       row with operator toggle + actions*/
  /*        .cbui-filter-group-intro-prefix    "Wenn"                       */
  /*        (RadioGroup variant=segmented)     UND/ODER toggle              */
  /*        .cbui-filter-group-intro-suffix    "… aller folgenden …"        */
  /*        .cbui-filter-group-header-actions  Gruppe auflösen, etc.        */
  /*      .cbui-filter-group-body         stack of children (pills + groups)*/
  /*      .cbui-filter-group-footer       + Bedingung / + Gruppe            */
  /* ────────────────────────────────────────────────────────────────────── */

  .cbui-filter-group {
    --cbui-filter-group-tone: var(--color-border);
    display: grid;
    gap: 0;
  }

  /* Per-tone CSS variable lift — outlined / tinted recolour the frame. */
  .cbui-filter-group-tone-neutral { --cbui-filter-group-tone: var(--color-border); }
  .cbui-filter-group-tone-primary { --cbui-filter-group-tone: var(--color-primary); }
  .cbui-filter-group-tone-success { --cbui-filter-group-tone: var(--color-success); }
  .cbui-filter-group-tone-warning { --cbui-filter-group-tone: var(--color-warning); }
  .cbui-filter-group-tone-danger  { --cbui-filter-group-tone: var(--color-danger);  }
  .cbui-filter-group-tone-info    { --cbui-filter-group-tone: var(--color-info);    }
  .cbui-filter-group-tone-accent  { --cbui-filter-group-tone: var(--color-accent);  }

  .cbui-filter-group-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 0.5rem 0.875rem;
  }
  .cbui-filter-group-header-main {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    flex: 1 1 auto;
    min-width: 0;
  }
  .cbui-filter-group-header-actions {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    margin-inline-start: auto;
    flex-shrink: 0;
  }
  .cbui-filter-group-intro-prefix,
  .cbui-filter-group-intro-suffix {
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    color: var(--color-text-secondary);
  }

  .cbui-filter-group-remove {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.25rem 0.5rem;
    background: transparent;
    border: 0;
    color: var(--color-text-secondary);
    font-size: var(--font-size-md, 1rem);
    cursor: pointer;
    border-radius: var(--radius-md, 0.5rem);
    transition: background-color 120ms ease, color 120ms ease;
  }
  .cbui-filter-group-remove:hover {
    background-color: color-mix(in srgb, currentColor 8%, transparent);
    color: var(--color-text);
  }
  .cbui-filter-group-remove:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
  }

  .cbui-filter-group-body {
    display: grid;
    gap: 0.5rem;
    align-items: start;
  }
  /* Chips inside the body line up at start (so they don't stretch). */
  .cbui-filter-group-body > .cbui-filter-chip-host,
  .cbui-filter-group-body > .cbui-filter-chip {
    justify-self: start;
  }

  .cbui-filter-group-footer {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-block-start: 0.25rem;
  }

  /* Frame: rail (default) — subtle vertical line on the inline-start of the
     BODY ONLY. The header (operator toggle + group-remove) sits above the
     rail; no header bg in this variant — it's the quiet option. */
  .cbui-filter-group-frame-rail > .cbui-filter-group-body {
    padding: 0.5rem 0 0.5rem 1rem;
    border-inline-start: 2px solid var(--cbui-filter-group-tone);
    margin-inline-start: 0.5rem;
  }

  /* Frames: outlined + tinted — ONE continuous frame wraps header + body.
     Border colour AND header background use the IDENTICAL computed colour:
     `--cbui-filter-group-frame` is a solid tint of the tone mixed against
     the page background, NOT an alpha-on-transparent. This guarantees the
     border line and the header band read as the same paint, regardless of
     what sits behind the group (page bg, nested body wash, etc.).

     Tone variants modulate the frame colour via `--cbui-filter-group-tone`;
     the frame colour itself is computed once on the group root and used
     for BOTH the border and the header bg. */
  .cbui-filter-group-frame-outlined,
  .cbui-filter-group-frame-tinted {
    /* Solid tint of the tone — mixed with the page bg for a visible-but-
       calm wash that works on light AND dark themes. The fixed alpha
       (22% tone + 78% bg) was tuned against both Light and Dark to read
       as a "named-band" colour rather than the disquieting neutral gray
       that a transparent-mix produces.

       NOTE: This uses --color-bg-base as the destination because the
       frame is drawn directly on the page surface (not inside another
       tinted region). Nested groups inside `tinted` keep the same
       formula because the additional body-wash they sit on is itself
       subtle enough that visual continuity is preserved. */
    --cbui-filter-group-frame: color-mix(in srgb, var(--cbui-filter-group-tone) 22%, var(--color-bg-base));
    border: 1px solid var(--cbui-filter-group-frame);
    border-radius: var(--radius-md, 0.5rem);
    overflow: hidden;
  }
  .cbui-filter-group-frame-outlined > .cbui-filter-group-header,
  .cbui-filter-group-frame-tinted > .cbui-filter-group-header {
    /* IDENTICAL colour to the border — same RGB. */
    background-color: var(--cbui-filter-group-frame);
    border-block-end: 1px solid var(--cbui-filter-group-frame);
  }
  .cbui-filter-group-frame-outlined > .cbui-filter-group-body {
    padding: 0.75rem 1rem;
  }
  .cbui-filter-group-frame-tinted > .cbui-filter-group-body {
    padding: 0.75rem 1rem;
    /* Body wash is a softer tint of the same tone — distinctly weaker
       than the header band so the conditions stay readable. */
    background-color: color-mix(in srgb, var(--cbui-filter-group-tone) 8%, var(--color-bg-base));
  }

  /* ── Nested logic-toggle (ButtonGroup) contrast lift ────────────────
     The ButtonGroup that hosts the Alle / Mindestens-eine toggle uses
     `--color-border` by default. Inside a tinted FilterGroup header
     band (especially at depths 1+) that border barely reads against
     the band — bump it to `--color-border-strong` so the toggle is
     clearly identifiable as a control. */
  .cbui-filter-group-frame-outlined > .cbui-filter-group-header .cbui-button-group-attached,
  .cbui-filter-group-frame-tinted > .cbui-filter-group-header .cbui-button-group-attached {
    --cbui-bg-frame-color: var(--color-border-strong);
    --cbui-bg-divider-color: var(--color-border-strong);
  }
  /* Same lift for rail (header sits on the page bg, but tone-cycled rails
     still benefit from the stronger border at depths 1+). */
  .cbui-filter-group-frame-rail .cbui-filter-group-header .cbui-button-group-attached {
    --cbui-bg-frame-color: var(--color-border-strong);
    --cbui-bg-divider-color: var(--color-border-strong);
  }

  /* Nested groups — slight visual breathing room. */
  .cbui-filter-group .cbui-filter-group {
    margin-block-start: 0.125rem;
  }

  .a11y-reduced-motion .cbui-filter-group-remove { transition: none; }
  .a11y-high-contrast .cbui-filter-group-frame-outlined,
  .a11y-high-contrast .cbui-filter-group-frame-tinted {
    border-width: 3px;
  }
  .a11y-high-contrast .cbui-filter-group-frame-outlined > .cbui-filter-group-header,
  .a11y-high-contrast .cbui-filter-group-frame-tinted > .cbui-filter-group-header {
    border-block-end-width: 3px;
  }
  .a11y-high-contrast .cbui-filter-group-frame-rail > .cbui-filter-group-body {
    border-inline-start-width: 3px;
  }
}

/* cbui/components/filter-area */
/* cbui-filter-area — the surface chrome around a Filter (CB-24).
 *
 * Filter (CB-23) is behaviour-only; FilterArea is the titled, padded <section>
 * region that hosts it. A QUIET surface container (like Card flat/outlined) —
 * it uses the --color-bg-* surface tokens, NOT a loud solid tone-surface, so
 * Aero reserves its plastic emboss for loud surfaces only and leaves this box
 * flat (same loud-vs-quiet rule as Card flat/outlined in variants/aero.css).
 *
 * Anatomy:
 *   .cbui-filter-area            outer <section role="region">
 *     .cbui-filter-area-header     optional title band
 *       .cbui-filter-area-title      <h2>
 *     .cbui-filter-area-body       holds the <Filter>
 *     .cbui-filter-area-footer     auto PendingChangesBar OR the footer slot
 *
 * Surface axis (prop `surface`, NOT `tone` — decision D14):
 *   subtle   → --color-bg-subtle    (default — a calm tinted box)
 *   base     → --color-bg-base      (matches the page; relies on the border)
 *   elevated → --color-bg-elevated  (a lifted tile; gets a soft shadow)
 */

@layer components {

  .cbui-filter-area {
    --cbui-filter-area-bg:      var(--color-bg-subtle);
    --cbui-filter-area-border:  var(--color-border);
    --cbui-filter-area-pad-x:   0.75rem;
    --cbui-filter-area-pad-y:   0.625rem;
    --cbui-filter-area-gap:     0.5rem;

    display: flex;
    flex-direction: column;
    gap: var(--cbui-filter-area-gap);
    /* NO overflow:hidden — it would clip the chip editor dropdowns / popovers
       that escape the box. The footer carries its own rounded bottom corners. */
    background-color: var(--cbui-filter-area-bg);
    border: 1px solid var(--cbui-filter-area-border);
    border-radius: var(--radius-md, 0.5rem);
    color: var(--color-text);
    /* No vertical padding on the section itself — the BODY owns all vertical
       rhythm (equal padding-block) so the space above and below the chips is
       always symmetric. The header band and the full-width footer band supply
       their own block padding. Inline (horizontal) padding stays on the section
       so non-banded children (the body, the header title) are inset. */
    padding: 0 var(--cbui-filter-area-pad-x);
  }

  /* ── Flush band — drop the floating-panel chrome (radius + top/side borders)
     and keep ONLY a bottom hairline, so the area docks flush under a sticky
     Toolbar in a bodyFlush window and reads as a continuation of the page
     rather than a rounded dropdown-like box. Background + body padding stay. */
  .cbui-filter-area-flush {
    border: 0;
    border-bottom: 1px solid var(--cbui-filter-area-border);
    border-radius: 0;
  }
  /* A flush area's footer band must not round its bottom corners either —
     it sits on a square edge, not a radius. */
  .cbui-filter-area-flush .cbui-filter-area-footer > .cbui-pending-changes-bar,
  .cbui-filter-area-flush .cbui-filter-area-footer > .cbui-pending-changes-bar > .cbui-bar {
    border-radius: 0;
  }

  /* ── Surface axis ─────────────────────────────────────────────────────── */
  .cbui-filter-area-surface-subtle   { --cbui-filter-area-bg: var(--color-bg-subtle); }
  .cbui-filter-area-surface-base     { --cbui-filter-area-bg: var(--color-bg-base); }
  .cbui-filter-area-surface-elevated {
    --cbui-filter-area-bg: var(--color-bg-elevated);
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.04), 0 4px 12px -6px rgb(0 0 0 / 0.10);
  }

  /* ── Density tiers ────────────────────────────────────────────────────── */
  .cbui-filter-area-density-compact {
    --cbui-filter-area-pad-x: 0.625rem;
    --cbui-filter-area-pad-y: 0.4375rem;
    --cbui-filter-area-gap:   0.375rem;
  }
  .cbui-filter-area-density-comfortable {
    --cbui-filter-area-pad-x: 0.75rem;
    --cbui-filter-area-pad-y: 0.625rem;
    --cbui-filter-area-gap:   0.5rem;
  }
  .cbui-filter-area-density-spacious {
    --cbui-filter-area-pad-x: 1rem;
    --cbui-filter-area-pad-y: 0.875rem;
    --cbui-filter-area-gap:   0.75rem;
  }

  /* ── Header / title ───────────────────────────────────────────────────── */
  .cbui-filter-area-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    min-width: 0;
    /* The section no longer supplies top padding; the header owns its own top
       band so the title is inset from the box top edge. */
    padding-top: var(--cbui-filter-area-pad-y);
  }
  .cbui-filter-area-title {
    margin: 0;
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--color-text);
    line-height: 1.3;
  }

  /* ── Body — the host for the inner <Filter>. Let the Filter's own
     padding-block collapse so the area's padding owns the vertical rhythm.
     The body carries an EQUAL top/bottom padding (--cbui-filter-area-pad-y) so
     the space above and below the chips is symmetric regardless of whether a
     header or footer is present. The section itself contributes zero vertical
     padding (see below) — all vertical rhythm flows through this one token so
     the two edges can never drift apart. */
  .cbui-filter-area-body {
    padding-block: var(--cbui-filter-area-pad-y);
  }
  .cbui-filter-area-body > .cbui-filter {
    padding-block: 0;
  }

  /* ── Dividers — 'auto' / true render a single hairline between the header
     and the body (and between the body and the footer). 'auto' == this. */
  .cbui-filter-area-dividers .cbui-filter-area-header {
    padding-bottom: var(--cbui-filter-area-pad-y);
    border-bottom: 1px solid var(--cbui-filter-area-border);
    margin-bottom: calc(-1 * var(--cbui-filter-area-gap) + 0.25rem);
  }
  .cbui-filter-area-dividers .cbui-filter-area-footer {
    padding-top: var(--cbui-filter-area-pad-y);
    border-top: 1px solid var(--cbui-filter-area-border);
    margin-top: calc(-1 * var(--cbui-filter-area-gap) + 0.25rem);
  }

  /* ── Footer — the apply / discard band (auto PendingChangesBar) or the
     caller's own footer. Pull it flush to the area's inline edges so a full-
     width band reads as docked to the bottom of the box. The negative inline
     margins cancel the area's horizontal padding. The section has NO vertical
     padding (the body owns all vertical rhythm), so the footer already sits on
     the box's bottom edge — no negative bottom margin is needed. */
  .cbui-filter-area-footer {
    margin-inline: calc(-1 * var(--cbui-filter-area-pad-x));
  }
  /* `[hidden]` winning rule: ui-component--filter-area sets the footer wrapper
     [hidden] when every element child is hidden/absent (e.g. the auto
     PendingChangesBar at 0 pending, or after Verwerfen). Author specificity
     beats the UA sheet so the empty band collapses entirely instead of showing
     an empty box. */
  .cbui-filter-area-footer[hidden] {
    display: none;
  }
  /* The composed Toolbar inside the auto-footer rounds its bottom corners to
     match the area. (The bar paints a solid surface; round it so it doesn't
     poke square corners past the area's radius.) */
  .cbui-filter-area-footer > .cbui-pending-changes-bar > .cbui-bar,
  .cbui-filter-area-footer > .cbui-pending-changes-bar {
    border-bottom-left-radius: var(--radius-md, 0.5rem);
    border-bottom-right-radius: var(--radius-md, 0.5rem);
  }
  .cbui-filter-area-footer > .cbui-pending-changes-bar > .cbui-bar {
    overflow: hidden;
  }

  /* ── Collapsible region — show/hide is driven by data-cb-collapsed (the same
     attribute a CB-22 SplitButton `for=` trigger mutates). FilterArea owns the
     collapse (decision D1): the WHOLE box hides, not just the inner controls. */
  .cbui-filter-area[data-cb-collapsed="true"] {
    display: none;
  }
  /* `[hidden]` winning rule: the area sets an explicit `display:flex`, so a
     UA `[hidden]` would otherwise lose. Author specificity beats UA — make a
     hidden area collapse even if some consumer toggles `hidden` directly. */
  .cbui-filter-area[hidden] {
    display: none;
  }

  /* ── Dark mode — bg-subtle / bg-base can be near-indistinguishable from the
     page in dark; the border carries the box. The elevated surface needs the
     surface token to read ABOVE the page, and its light-tuned shadow is too
     weak in dark — replace it with a deeper, darker cast. Cover BOTH the
     html.dark chrome selector AND the .cbui-scheme-dark Storybook scope. */
  html.dark .cbui-filter-area-surface-elevated,
  .cbui-scheme-dark .cbui-filter-area-surface-elevated {
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.40), 0 6px 18px -8px rgb(0 0 0 / 0.55);
  }

  /* ── A11y — collapse transitions respect reduced motion. The collapse is a
     display toggle (no height animation), so there is nothing to disable for
     motion here; the rule is kept as a hook + documents the intent. The inner
     Filter / chips carry their own reduced-motion handling. */
  @media (prefers-reduced-motion: reduce) {
    .cbui-filter-area { transition: none; }
  }
  .a11y-reduced-motion .cbui-filter-area { transition: none; }

  .a11y-high-contrast .cbui-filter-area,
  .a11y-high-contrast .cbui-filter-area-dividers .cbui-filter-area-header,
  .a11y-high-contrast .cbui-filter-area-dividers .cbui-filter-area-footer {
    border-color: var(--color-border-strong);
    border-width: 2px;
  }
}

/* cbui/components/reveal-region */
/* ==========================================================================
 * reveal-region — default hidden class for the controller's `class` hide mode.
 *
 * The ui-component--reveal-region controller (CB-34) hides its region one of
 * two ways: the `hidden` attribute (default, instant) or by toggling a class
 * (`hide: 'class'`). This is the default class it toggles — an instant hide.
 * Point `hiddenClass` at your own class to fade / slide instead (see the
 * Storybook "class fade" demo).
 * ========================================================================== */
@layer components {
    .cbui-reveal-hidden { display: none; }
}

/* cbui/components/pending-changes-bar */
/* cbui-pending-changes-bar — the apply / discard band for a deferred Filter
 * (CB-25). It COMPOSES <twig:UiComponent:Toolbar> for its chrome (no duplicated
 * bar recipe) and adds only: the wrapper hide behaviour, the badge styling, the
 * block-direction stacked layout, and the slide-in entrance.
 *
 * Colour/contrast is owned entirely by the composed children — a SOLID warning
 * Badge (white-on-amber) and OUTLINE buttons — so the band reads on every
 * Toolbar tone in light AND dark. Nothing here hardcodes a colour.
 *
 * Anatomy:
 *   .cbui-pending-changes-bar              wrapper ([hidden] when count 0)
 *     .cbui-bar (Toolbar)                  the composed band
 *       .cbui-pending-changes-bar-badge      warning "n Änderungen ausstehend"
 *     .cbui-pending-changes-bar-block-actions  (block direction only)
 */

@layer components {

  .cbui-pending-changes-bar {
    display: block;
  }

  /* Auto-hide — the controller sets `hidden` when pendingCount === 0. The
     wrapper declares an explicit `display`, so a bare UA `[hidden]` rule would
     lose to it. Author specificity beats UA: this rule wins and the bar truly
     collapses. */
  .cbui-pending-changes-bar[hidden] {
    display: none;
  }

  /* Badge wrapper — keeps the warning badge aligned with the toolbar baseline. */
  .cbui-pending-changes-bar-badge {
    display: inline-flex;
    align-items: center;
    min-width: 0;
  }

  /* Optional `extra` slot — a quiet action before the standard pair. */
  .cbui-pending-changes-bar-extra {
    display: inline-flex;
    align-items: center;
  }

  /* ── Block direction — the toolbar carries the badge row, then the buttons
     stack full-width beneath it. Used in narrow columns / drawers / mobile. */
  .cbui-pending-changes-bar-block-actions {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem var(--cbui-bar-px, 1rem) var(--cbui-bar-py, 0.625rem);
  }
  .cbui-pending-changes-bar-block .cbui-pending-changes-bar-extra {
    display: flex;
    width: 100%;
  }
  /* In block direction the composed toolbar is just the badge row; drop its
     trailing block-padding so the actions sit tight beneath it. */
  .cbui-pending-changes-bar-block > .cbui-bar {
    --bar-padding-y: 0.4375rem;
  }

  /* ── Slide-in entrance — when the bar un-hides (count goes 0 → n) it slides
     up a touch + fades in, so the apply / discard band announces itself. CSS-
     gated by prefers-reduced-motion AND the a11y class. The controller toggles
     the `hidden` attribute; the animation fires on the show transition because
     a freshly-shown element re-runs its animation. */
  @media (prefers-reduced-motion: no-preference) {
    .cbui-pending-changes-bar:not([hidden]) {
      animation: cbui-pending-changes-bar-in 180ms ease-out;
    }
  }
  /* The a11y class is the authoritative scope-level switch — when set, force
     no animation regardless of the media query above. */
  .a11y-reduced-motion .cbui-pending-changes-bar:not([hidden]) {
    animation: none;
  }

  @keyframes cbui-pending-changes-bar-in {
    from { opacity: 0; transform: translateY(0.375rem); }
    to   { opacity: 1; transform: translateY(0); }
  }
}

/* cbui/components/stack */
/* ==========================================================================
 * Stack — composition-only flex layout primitive (CB-1690)
 *
 * Layout + gap only. Holds any children; owns no content. No wrapper element
 * (unlike Grid, whose host div exists for the container-query breakpoints
 * Stack does not need) — the children render directly inside the root tag.
 *
 * Not for: a page head (Header), an action bar (Toolbar), a list of rows
 * (ListGroup) — those own more than layout and have their own slots.
 * ========================================================================== */
@layer components {
    .cbui-stack {
        display: flex;
        flex-direction: column;
        gap: 1rem;
    }

    /* ── direction ─────────────────────────────────────────────────────────── */
    .cbui-stack-column { flex-direction: column; }
    .cbui-stack-row    { flex-direction: row; }
    .cbui-stack-wrap   { flex-wrap: wrap; }

    /* fill: take the rest of the height of a flex-column parent; scroll: scroll the content inside */
    .cbui-stack-fill   { flex: 1 1 auto; min-height: 0; }
    .cbui-stack-scroll { overflow: auto; }
    /* A Turbo frame directly inside a fill Stack passes the height on. It keeps a box of its own: a lazy frame with
       display: contents is never in view, so it never loads. */
    .cbui-stack-fill > turbo-frame { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

    /* A Stack rendered as a list element must not carry list chrome. */
    :is(ul, ol).cbui-stack {
        list-style: none;
        margin: 0;
        padding: 0;
    }

    /* ── gap scale (explicit rems; matches Grid's scale) ──────────────────── */
    .cbui-stack-gap-none { gap: 0; }
    .cbui-stack-gap-xs   { gap: 0.5rem; }
    .cbui-stack-gap-sm   { gap: 0.75rem; }
    .cbui-stack-gap-md   { gap: 1rem; }
    .cbui-stack-gap-lg   { gap: 1.5rem; }
    .cbui-stack-gap-xl   { gap: 2rem; }
    .cbui-stack-gap-raw  { gap: var(--cbui-stack-gap); }

    /* ── pad (CB-2238): the same inset on all four sides, for a detail panel without a tab strip above it ── */
    .cbui-stack.cbui-stack-pad-sm { padding: 0.75rem; }
    .cbui-stack.cbui-stack-pad-md { padding: 1rem; }
    .cbui-stack.cbui-stack-pad-lg { padding: 1.5rem; }

    /* ── alignment (cross axis) ───────────────────────────────────────────── */
    .cbui-stack-align-start    { align-items: flex-start; }
    .cbui-stack-align-center   { align-items: center; }
    .cbui-stack-align-end      { align-items: flex-end; }
    .cbui-stack-align-stretch  { align-items: stretch; }
    .cbui-stack-align-baseline { align-items: baseline; }

    /* ── justification (main axis) ────────────────────────────────────────── */
    .cbui-stack-justify-start   { justify-content: flex-start; }
    .cbui-stack-justify-center  { justify-content: center; }
    .cbui-stack-justify-end     { justify-content: flex-end; }
    .cbui-stack-justify-between { justify-content: space-between; }

    /* ── sticky: pinned to the top or bottom edge of the scrolling pane (a bar that stays in view over a long list) ── */
    /* measure: a centred reading width for focus layouts (one card at a time) */
    .cbui-stack-measure    { width: 100%; margin-inline: auto; }
    .cbui-stack-measure-sm { max-width: 36rem; }
    .cbui-stack-measure-md { max-width: 48rem; }
    .cbui-stack-measure-lg { max-width: 64rem; }

    .cbui-stack-sticky        { position: sticky; z-index: 2; background-color: var(--color-surface, var(--color-bg-elevated)); }
    .cbui-stack-sticky-top    { top: 0; }
    .cbui-stack-sticky-bottom { bottom: 0; }
}

/* Matthias 2026-10-29: a row Stack whose direct children are action buttons is an action group — its buttons stand the
   one action gap apart (10px), whatever the flavor's `sm` / `md` step is. */
.cbui-stack-row:has(> :is(.cbui-button, .cbui-dropdown) ~ :is(.cbui-button, .cbui-dropdown)) {
    gap: var(--cbui-action-gap, 0.625rem);
}

/* cbui/components/slugger */
/* cbui-slugger — visual coupling of source + slug fields.
 *
 * The connector is a short vertical rail between the two FormRows.
 * The pill that sits ON the rail toggles auto vs manual mode. When
 * auto is on, the rail uses the primary tone; manual goes neutral.
 *
 * Tokens:
 *   --cbui-slugger-rail-color    accent colour of the rail / pill border
 *   --cbui-slugger-pill-bg       background of the pill
 *   --cbui-slugger-pill-fg       text colour of the pill
 *
 * Both states write their values to these tokens — the same CSS rule
 * applies regardless of synced / unsynced. The actual swap happens via
 * `.cbui-slugger-synced` / `.cbui-slugger-unsynced` modifier classes
 * toggled by the Stimulus controller.
 */

@layer components {
  .cbui-slugger {
    --cbui-slugger-rail-color: color-mix(in srgb, var(--color-primary) 55%, var(--color-border));
    --cbui-slugger-pill-bg:    color-mix(in srgb, var(--color-primary) 12%, var(--color-bg-base));
    --cbui-slugger-pill-fg:    var(--color-primary);
    --cbui-slugger-rail-w:     2px;
    --cbui-slugger-pad-y:      0.5rem;
    --cbui-slugger-indent:     1rem;

    display: flex;
    flex-direction: column;
    gap: 0;
  }

  .cbui-slugger-source,
  .cbui-slugger-slug { display: contents; }

  .cbui-slugger-connector {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding-inline-start: var(--cbui-slugger-indent);
    padding-block: var(--cbui-slugger-pad-y);
    border-inline-start: var(--cbui-slugger-rail-w) solid var(--cbui-slugger-rail-color);
    margin-inline-start: var(--cbui-slugger-indent);
    transition: border-color 200ms ease;
  }

  .cbui-slugger-align-center .cbui-slugger-connector {
    justify-content: center;
    padding-inline-start: 0;
    margin-inline-start: 0;
    border-inline-start: 0;
    /* Centre line via background-gradient — the connector becomes a
       short vertical hairline running through the centre of the
       container; the pill sits on top of it. */
    position: relative;
  }
  .cbui-slugger-align-center .cbui-slugger-connector::before {
    content: '';
    position: absolute;
    inset-block: 0;
    inset-inline-start: 50%;
    transform: translateX(-50%);
    width: var(--cbui-slugger-rail-w);
    background-color: var(--cbui-slugger-rail-color);
    transition: background-color 200ms ease;
  }

  /* Right alignment — mirror image of `left`, plus a CONTINUOUS rail
     that runs from the top of the source field all the way down past
     the connector to the bottom of the slug field. The line is
     rendered on a host-level `::before` pseudo-element so it spans
     the full slugger height regardless of how many rows the source /
     slug sub-components produce. The pill sits ABOVE the rail (z-index)
     so it visually "interrupts" the line without breaking continuity. */
  .cbui-slugger-align-right {
    position: relative;
  }
  .cbui-slugger-align-right::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    /* Align the rail with the connector's right padding so the line
       sits exactly where the per-row border would land if it were on
       the connector itself. */
    right: var(--cbui-slugger-indent);
    width: var(--cbui-slugger-rail-w);
    background-color: var(--cbui-slugger-rail-color);
    transition: background-color 200ms ease;
    pointer-events: none;
    z-index: 0;
  }
  .cbui-slugger-align-right .cbui-slugger-connector {
    justify-content: flex-end;
    padding-inline-start: 0;
    padding-inline-end: var(--cbui-slugger-indent);
    margin-inline-start: 0;
    margin-inline-end: var(--cbui-slugger-indent);
    /* No border on the connector itself — the rail comes from the
       host's `::before` pseudo and spans the entire slugger height. */
    border-inline-start: 0;
    border-inline-end: 0;
    position: relative;
    z-index: 1;
  }

  .cbui-slugger-pill {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding-block: 0.125rem;
    padding-inline: 0.625rem;
    border-radius: 9999px;
    border: 1px solid var(--cbui-slugger-rail-color);
    background-color: var(--cbui-slugger-pill-bg);
    color: var(--cbui-slugger-pill-fg);
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    position: relative;
    z-index: 1;     /* sits above the centre rail */
    transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
  }

  .cbui-slugger-pill:hover {
    background-color: color-mix(in srgb, var(--cbui-slugger-rail-color) 18%, var(--color-bg-base));
  }

  /* Disabled (slug locked, CB-1179): same look as .cbui-button[disabled] - dimmed, not-allowed.
     No pointer-events:none here, the title (why it is disabled) must stay hoverable. */
  .cbui-slugger-pill:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
  .cbui-slugger-pill:disabled:hover {
    background-color: var(--cbui-slugger-pill-bg);
  }

  .cbui-slugger-pill:focus-visible {
    outline: 2px solid var(--color-focus-ring, var(--color-primary));
    outline-offset: 2px;
  }

  /* CB-1179: the lock button — same pill shape as the sync pill, but
     deliberately damped (muted text, subtle surface) so it does not
     compete with the sync pill for attention while a slug is locked. */
  .cbui-slugger-lock {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding-block: 0.125rem;
    padding-inline: 0.625rem;
    border-radius: 9999px;
    border: 1px solid var(--color-border);
    background-color: var(--color-bg-subtle);
    color: var(--color-text-muted);
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    position: relative;
    z-index: 1;
  }
  .cbui-slugger-lock[hidden] { display: none; }

  .cbui-slugger-lock:hover {
    background-color: color-mix(in srgb, var(--color-border) 25%, var(--color-bg-base));
  }

  .cbui-slugger-lock:focus-visible {
    outline: 2px solid var(--color-focus-ring, var(--color-primary));
    outline-offset: 2px;
  }

  /* Unsynced mode — neutral colours. */
  .cbui-slugger-unsynced {
    --cbui-slugger-rail-color: var(--color-border);
    --cbui-slugger-pill-bg:    var(--color-bg-subtle);
    --cbui-slugger-pill-fg:    var(--color-text-secondary);
  }

  .cbui-slugger-glyph {
    display: block;
    width: 0.75rem;
    height: 0.75rem;
    background-color: currentColor;
    -webkit-mask: no-repeat center / contain;
            mask: no-repeat center / contain;
  }
  .cbui-slugger-glyph[hidden] { display: none; }

  /* down-arrow (auto = "flows from source") */
  .cbui-slugger-glyph-linked {
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 2v10M4 8l4 4 4-4' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 2v10M4 8l4 4 4-4' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  }
  /* link-break (manual = "decoupled") */
  .cbui-slugger-glyph-unlinked {
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M6 9.5a2.5 2.5 0 0 1 0-3.5l1-1M10 6.5a2.5 2.5 0 0 1 0 3.5l-1 1' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round'/><line x1='2.5' y1='13.5' x2='13.5' y2='2.5' stroke='black' stroke-width='1.6' stroke-linecap='round'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M6 9.5a2.5 2.5 0 0 1 0-3.5l1-1M10 6.5a2.5 2.5 0 0 1 0 3.5l-1 1' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round'/><line x1='2.5' y1='13.5' x2='13.5' y2='2.5' stroke='black' stroke-width='1.6' stroke-linecap='round'/></svg>");
  }

  /* Reduced motion — drop the rail-colour transition. */
  .a11y-reduced-motion .cbui-slugger-connector,
  .a11y-reduced-motion .cbui-slugger-connector::before,
  .a11y-reduced-motion .cbui-slugger-pill {
    transition: none;
  }
}

/* cbui/components/grid */
/* ==========================================================================
 * Grid — composition-only responsive tile grid (CB-37)
 *
 * Layout + gap only. Holds any tiles (Card, StatTile, plain blocks); owns no
 * content. Mirrors ListGroup as a thin layout primitive.
 *
 * Three column modes (see Grid.html.twig):
 *   reflow  — repeat(auto-fill|auto-fit, minmax(min(100%, --cbui-grid-min), 1fr)); the min(100%, …) keeps one column
 *             narrower than the minimum from overflowing a narrow container, so no caller has to clamp it
 *   fixed   — repeat(N, minmax(0, 1fr)) via a static .cbui-grid-cN class
 *   stepped — fixed N that steps down at the standard container breakpoints
 *             (44rem / 26rem). The host wrapper carries the size container.
 *
 * Note: the column count in repeat() cannot be a custom property (var() is
 * invalid as the repeat count, and auto-fill/auto-fit must be literal), so the
 * counts are static classes (cN / mdN / smN), not a single --cols property.
 * ========================================================================== */
@layer components {
    /* Host wrapper: transparent by default; a real size container when stepped. */
    .cbui-grid-host { display: contents; }
    .cbui-grid-host-stepped {
        display: block;
        container: cbui-grid / inline-size;
    }

    .cbui-grid {
        display: grid;
        gap: var(--cbui-grid-gap, 1rem);
    }

    /* ── reflow (cols=null) ───────────────────────────────────────────────── */
    .cbui-grid-reflow.cbui-grid-fill {
        grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--cbui-grid-min, 15rem)), 1fr));
    }
    .cbui-grid-reflow.cbui-grid-fit {
        grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--cbui-grid-min, 15rem)), 1fr));
    }

    /* ── reflow tile-size presets (CB-1906) — override --cbui-grid-min via a
     * class instead of the inline minColWidth style, so a caller (e.g. the
     * Files "Symbole" icon-size switch) can pick a preset without inventing a
     * raw length. Must sit ahead of no other rule setting --cbui-grid-min
     * with higher specificity; the inline style branch is skipped in
     * Grid.html.twig whenever tileSize is set, so this class always wins. */
    .cbui-grid-tile-sm { --cbui-grid-min: min(100%, 8.5rem); }
    .cbui-grid-tile-md { --cbui-grid-min: min(100%, 11rem); }
    .cbui-grid-tile-lg { --cbui-grid-min: min(100%, 14.5rem); }

    /* lazyRender (CB-1941): off-screen tiles skip layout and paint. The block size is only
       an estimate until a tile has been rendered once (`auto` then remembers the real one);
       the per-size values are the measured Files tile heights at default density
       (9.85 / 11.49 / 16.10rem, rounded to 0.25rem).
       Not on <tr> (size containment has no effect there) and not meant for Tree rows. */
    .cbui-grid-lazy-render { --cbui-grid-item-estimate: 12rem; }
    .cbui-grid-lazy-render.cbui-grid-tile-sm { --cbui-grid-item-estimate: 9.75rem; }
    .cbui-grid-lazy-render.cbui-grid-tile-md { --cbui-grid-item-estimate: 11.5rem; }
    .cbui-grid-lazy-render.cbui-grid-tile-lg { --cbui-grid-item-estimate: 16rem; }
    .cbui-grid-lazy-render > * {
      content-visibility: auto;
      contain-intrinsic-block-size: auto var(--cbui-grid-item-estimate);
    }

    /* windowed (CB-1941): a block of far-away tiles is collapsed into ONE full-width spacer item
       with an explicit height (set inline by the grid-window controller); its tiles stay in the
       document, in order, but take no layout or style work. */
    .cbui-grid-window-spacer { grid-column: 1 / -1; }
    .cbui-grid-window-spacer > * { display: none !important; }
    .cbui-grid-lazy-render > .cbui-grid-window-spacer {
      content-visibility: visible;
      contain-intrinsic-block-size: none;
    }

    /* ── fixed / stepped base counts ──────────────────────────────────────── */
    .cbui-grid-c1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
    .cbui-grid-c2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .cbui-grid-c3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .cbui-grid-c4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .cbui-grid-c5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
    .cbui-grid-c6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }

    /* ── stepped: step down at the standard CBUI container breakpoints ─────── */
    @container cbui-grid (max-width: 44rem) {
        .cbui-grid-md1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
        .cbui-grid-md2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
        .cbui-grid-md3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
        .cbui-grid-md4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
        .cbui-grid-md5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
        .cbui-grid-md6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    }
    @container cbui-grid (max-width: 26rem) {
        .cbui-grid-sm1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
        .cbui-grid-sm2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
        .cbui-grid-sm3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
        .cbui-grid-sm4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
        .cbui-grid-sm5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
        .cbui-grid-sm6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    }

    /* ── gap scale (explicit rems; self-contained, matches existing grids) ─── */
    .cbui-grid-gap-none { gap: 0; }
    .cbui-grid-gap-xs   { gap: 0.5rem; }
    .cbui-grid-gap-sm   { gap: 0.75rem; }
    .cbui-grid-gap-md   { gap: 1rem; }
    .cbui-grid-gap-lg   { gap: 1.5rem; }
    .cbui-grid-gap-xl   { gap: 2rem; }
    .cbui-grid-gap-raw  { gap: var(--cbui-grid-gap); }

    /* ── alignment (block axis) ───────────────────────────────────────────── */
    .cbui-grid-align-start   { align-items: start; }
    .cbui-grid-align-center  { align-items: center; }
    .cbui-grid-align-end     { align-items: end; }
    .cbui-grid-align-stretch { align-items: stretch; }

    /* ── justification (inline axis) ──────────────────────────────────────── */
    .cbui-grid-justify-start   { justify-items: start; }
    .cbui-grid-justify-center  { justify-items: center; }
    .cbui-grid-justify-end     { justify-items: end; }
    .cbui-grid-justify-stretch { justify-items: stretch; }

    /* ── row height ───────────────────────────────────────────────────────────
     * equal: grid-auto-rows: 1fr — in an auto-height grid the fr fraction
     *        resolves against the max row base size, so every row track (and,
     *        with the default stretch align, every tile) equalises to the
     *        tallest. fixed: a literal track height for media / poster tiles. */
    .cbui-grid-rows-equal { grid-auto-rows: 1fr; }
    .cbui-grid-rows-fixed { grid-auto-rows: var(--cbui-grid-row-h); }

    /* ── dense packing — backfill gaps left by spanning tiles ─────────────── */
    .cbui-grid-dense { grid-auto-flow: row dense; }

    /* ── Tile utilities — applied to a CHILD tile, not the Grid ──────────────
     * Column / row spans for hero tiles; aspect-ratio for media tiles. A span
     * is clamped only at a one-column step (see the reset below). In a step
     * with several columns a span wider than the column count still opens
     * implicit tracks. */
    .cbui-grid-span-2    { grid-column: span 2; }
    .cbui-grid-span-3    { grid-column: span 3; }
    .cbui-grid-span-4    { grid-column: span 4; }
    .cbui-grid-span-full { grid-column: 1 / -1; }
    .cbui-grid-row-span-2 { grid-row: span 2; }
    .cbui-grid-row-span-3 { grid-row: span 3; }

    /* One column (CB-2975): a spanning tile takes the single track. Without this, span-N opens implicit
       auto tracks beside it, the tile widens and its siblings shrink. */
    .cbui-grid-c1 > :is(.cbui-grid-span-2, .cbui-grid-span-3, .cbui-grid-span-4) { grid-column: auto; }
    @container cbui-grid (max-width: 44rem) {
        .cbui-grid-md1 > :is(.cbui-grid-span-2, .cbui-grid-span-3, .cbui-grid-span-4) { grid-column: auto; }
    }
    @container cbui-grid (max-width: 26rem) {
        .cbui-grid-sm1 > :is(.cbui-grid-span-2, .cbui-grid-span-3, .cbui-grid-span-4) { grid-column: auto; }
    }

    .cbui-grid-aspect-square   { aspect-ratio: 1 / 1; }
    .cbui-grid-aspect-video    { aspect-ratio: 16 / 9; }
    .cbui-grid-aspect-wide     { aspect-ratio: 21 / 9; }
    .cbui-grid-aspect-portrait { aspect-ratio: 3 / 4; }

  /* lines: a hairline grid. The container is the line colour, the gap and the 1px padding let it show between and around the
     tiles. Declared after the gap classes with a compound selector so it wins over every gap token. */
  .cbui-grid.cbui-grid-lines {
    --cbui-grid-gap: 1px;
    gap: 1px;
    padding: 1px;
    background-color: var(--color-border);
  }
}

/* cbui/components/stat-tile */
/* cbui-stat-tile — a KPI / metric tile for dashboards (UiData).
 *
 * StatTile is a semantic skin over Card: the surface (flat / outlined /
 * elevated), radius, shadow, Aero plastic and clickable behaviour live in
 * cbui-card. StatTile is rendered into a *flush* Card body (no Card padding)
 * and owns its own size-scaled padding, fonts, icon and layout — so a smaller
 * `size` yields a genuinely smaller tile (padding + height + width all shrink),
 * not just a smaller number.
 *
 * Two orthogonal sizing axes:
 *   size    — scales the whole tile (padding, fonts, icon, min-height).
 *   density — a multiplier on the inter-row gap only; the tile footprint stays
 *             as `size` dictates. Content is TOP-aligned (Matthias 2026-10-29, dashboard
 *             look): in an equal-height row the labels and values of all tiles share one
 *             line even when one tile carries an extra caption or trend.
 *
 * `tone` colours the value + icon chip via --cbui-stat-tile-tone; the surface
 * stays neutral (a coloured number, not a coloured tile).
 */

@layer components {

  .cbui-stat-tile {
    --cbui-stat-tile-tone: var(--color-text);

    /* Size-scaled tokens (md defaults; per-size overrides below). */
    --cbui-stat-tile-pad: 1rem;
    --cbui-stat-tile-min-h: 5.5rem;
    --cbui-stat-tile-value-size: 1.875rem;
    --cbui-stat-tile-label-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-detail-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-icon-size: 1.75rem;
    --cbui-stat-tile-gap-base: 0.375rem;

    /* Density multiplier on the row gap (comfortable default). */
    --cbui-stat-tile-density-mult: 1;

    box-sizing: border-box;
    height: 100%;        /* fill the flush Card body so equal-height rows hold */
    min-height: var(--cbui-stat-tile-min-h);
    padding: var(--cbui-stat-tile-pad);

    display: flex;
    flex-direction: column;
    justify-content: flex-start;   /* top-aligned: label and value lines match across a row */
    gap: calc(var(--cbui-stat-tile-gap-base) * var(--cbui-stat-tile-density-mult));
    min-width: 0;
  }

  /* Label over the tile (`labelPlacement="above"`): the tile hugs its value. */
  .cbui-stat-tile-stack {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    min-width: 0;
  }
  .cbui-stat-tile-stack > .cbui-stat-tile-label {
    padding-inline-start: 0.5rem;
  }
  .cbui-stat-tile.cbui-stat-tile-label-above {
    min-height: 0;
  }

  /* ── Head: label + optional icon chip ──────────────────────────────────── */
  .cbui-stat-tile-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
    min-width: 0;
  }
  .cbui-stat-tile-label {
    font-size: var(--cbui-stat-tile-label-size);
    font-weight: var(--font-weight-medium, 500);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-secondary);
    min-width: 0;
  }
  .cbui-stat-tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: var(--cbui-stat-tile-icon-size);
    height: var(--cbui-stat-tile-icon-size);
    border-radius: 9999px;
    color: var(--cbui-stat-tile-tone);
    background-color: color-mix(in srgb, var(--cbui-stat-tile-tone) 12%, transparent);
  }
  /* neutral / muted: a quiet glyph, but the chip must stay visible on a flat
     (bg-subtle) tile too — so tint with the text colour, not the surface. */
  .cbui-stat-tile-tone-neutral .cbui-stat-tile-icon,
  .cbui-stat-tile-tone-muted .cbui-stat-tile-icon {
    color: var(--color-text-secondary);
    background-color: color-mix(in srgb, var(--color-text) 8%, transparent);
  }
  .cbui-stat-tile-icon-glyph {
    width: calc(var(--cbui-stat-tile-icon-size) * 0.58);
    height: calc(var(--cbui-stat-tile-icon-size) * 0.58);
  }

  /* ── Value ─────────────────────────────────────────────────────────────── */
  .cbui-stat-tile-value {
    color: var(--cbui-stat-tile-tone);
    line-height: 1.1;
    min-width: 0;
  }
  .cbui-stat-tile-metric .cbui-stat-tile-value {
    font-size: var(--cbui-stat-tile-value-size);
    font-weight: var(--font-weight-bold, 700);
    letter-spacing: -0.01em;
  }
  .cbui-stat-tile-text .cbui-stat-tile-value {
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* mono: a code read character by character (number preview, key). Tabular digits keep the width steady while it
     changes live. In a narrow tile the type shrinks with the tile (container units) before it wraps, and it wraps only
     at a break opportunity such as a hyphen, never inside a run of digits ("0002/1" + "0" reads as two numbers). */
  .cbui-stat-tile-mono {
    container-type: inline-size;
  }
  .cbui-stat-tile-mono .cbui-stat-tile-value {
    font-family: var(--font-family-mono, ui-monospace, monospace);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
    font-size: clamp(var(--font-size-md, 1rem), 8cqi, var(--cbui-stat-tile-value-size));
    overflow-wrap: break-word;
  }

  /* ── Delta / trend ─────────────────────────────────────────────────────── */
  .cbui-stat-tile-delta {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    color: var(--color-text-secondary);
  }
  .cbui-stat-tile-delta-glyph { width: 0.875rem; height: 0.875rem; flex: none; }
  .cbui-stat-tile-trend-up   { color: var(--color-success); }
  .cbui-stat-tile-trend-down { color: var(--color-danger); }
  .cbui-stat-tile-trend-flat { color: var(--color-text-secondary); }

  /* ── Detail ────────────────────────────────────────────────────────────── */
  .cbui-stat-tile-detail {
    font-size: var(--cbui-stat-tile-detail-size);
    color: var(--color-text-tertiary, var(--color-text-secondary));
    min-width: 0;
  }

  /* ── Tones (colour the value + icon chip) ──────────────────────────────── */
  .cbui-stat-tile-tone-primary { --cbui-stat-tile-tone: var(--color-primary); }
  .cbui-stat-tile-tone-accent  { --cbui-stat-tile-tone: var(--color-accent); }
  .cbui-stat-tile-tone-success { --cbui-stat-tile-tone: var(--color-success); }
  .cbui-stat-tile-tone-danger  { --cbui-stat-tile-tone: var(--color-danger); }
  .cbui-stat-tile-tone-warning { --cbui-stat-tile-tone: var(--color-warning); }
  .cbui-stat-tile-tone-info    { --cbui-stat-tile-tone: var(--color-info); }
  .cbui-stat-tile-tone-muted   { --cbui-stat-tile-tone: var(--color-text-secondary); }

  /* ── Sizes — scale padding, fonts, icon, gap-base and the min-height floor. ── */
  .cbui-stat-tile-size-xs {
    --cbui-stat-tile-pad: 0.625rem;
    --cbui-stat-tile-min-h: 4rem;
    --cbui-stat-tile-value-size: 1.25rem;
    --cbui-stat-tile-label-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-detail-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-icon-size: 1.4rem;
    --cbui-stat-tile-gap-base: 0.25rem;
  }
  .cbui-stat-tile-size-sm {
    --cbui-stat-tile-pad: 0.8125rem;
    --cbui-stat-tile-min-h: 4.75rem;
    --cbui-stat-tile-value-size: 1.5rem;
    --cbui-stat-tile-label-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-detail-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-icon-size: 1.55rem;
    --cbui-stat-tile-gap-base: 0.3125rem;
  }
  .cbui-stat-tile-size-md {
    --cbui-stat-tile-pad: 1rem;
    --cbui-stat-tile-min-h: 5.5rem;
    --cbui-stat-tile-value-size: 1.875rem;
    --cbui-stat-tile-label-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-detail-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-icon-size: 1.75rem;
    --cbui-stat-tile-gap-base: 0.375rem;
  }
  .cbui-stat-tile-size-lg {
    --cbui-stat-tile-pad: 1.375rem;
    --cbui-stat-tile-min-h: 7rem;
    --cbui-stat-tile-value-size: 2.5rem;
    --cbui-stat-tile-label-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-detail-size: var(--font-size-md, 1rem);
    --cbui-stat-tile-icon-size: 2.1rem;
    --cbui-stat-tile-gap-base: 0.5rem;
  }

  /* ── Density — multiplier on the row gap (orthogonal to size). ── */
  .cbui-stat-tile-density-compact     { --cbui-stat-tile-density-mult: 0.35; }
  .cbui-stat-tile-density-comfortable { --cbui-stat-tile-density-mult: 1; }
  .cbui-stat-tile-density-spacious    { --cbui-stat-tile-density-mult: 2; }

  /* ── Center align ──────────────────────────────────────────────────────── */
  .cbui-stat-tile-align-center { align-items: center; text-align: center; }
  .cbui-stat-tile-align-center .cbui-stat-tile-head {
    flex-direction: column-reverse;
    align-items: center;
    justify-content: center;
  }
  .cbui-stat-tile-align-center .cbui-stat-tile-delta { align-self: center; }

  /* ── High contrast ─────────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-stat-tile-tone-neutral .cbui-stat-tile-icon,
  .a11y-high-contrast .cbui-stat-tile-tone-muted .cbui-stat-tile-icon {
    border: 1px solid var(--color-border-strong, var(--color-border));
  }
}

/* cbui/components/stepper */
/* cbui-stepper — linear progression indicator.
 *
 * A Stepper visualises an ordered sequence of steps with per-step state
 * (upcoming · current · complete · error). Unlike a Tabnav (lateral choice
 * between equal-weight views), a Stepper carries direction — the connector
 * between two steps tells the user "this comes after that".
 *
 * Anatomy:
 *   .cbui-stepper                  outer container (positioning, tokens)
 *   .cbui-stepper-list             role=list; the actual sequence (ol)
 *   .cbui-stepper-step             role=listitem; one step (li)
 *     .cbui-stepper-step-trigger     wraps indicator+body when clickable (<a>/<button>)
 *     .cbui-stepper-step-indicator   circle/dot/icon; carries status colour
 *       .cbui-stepper-step-number    1, 2, 3 …
 *       .cbui-stepper-step-glyph     check / x / icon override
 *     .cbui-stepper-step-body
 *       .cbui-stepper-step-label
 *       .cbui-stepper-step-optional  small "(Optional)" marker
 *       .cbui-stepper-step-description
 *
 * The connector between two steps is rendered as the ::after pseudo of the
 * step (it sits on the trailing edge / below the indicator). Its colour is
 * "complete-coloured" when the *preceding* step is already complete — the
 * universal convention: a finished step has filled the line that follows it.
 *
 * Variants:    numbered (default) · dot · panel · plain
 * Modifiers:   bg-fill · bg-blur · shadow-lift · border-pale
 * Connector:   solid (default) · dashed · dotted · none
 * Tones:       primary (default) · neutral · accent · success · danger · warning · info · muted
 * Status:      upcoming · current · complete · error
 *
 * Aero plastic rule (loud-only): only the *filled* indicator states get
 * plastic — complete, current, error (when status pushes the indicator
 * into the filled treatment). Upcoming stays quiet (outlined).
 *
 * Radius discipline: indicators are circular by default (radius-full); the
 * `panel` variant uses `--card-radius` per flavor so it reads as a card
 * relative to the surrounding content chrome.
 */

@layer components {
  /* ── Tokens (overridable by flavor) ───────────────────────────────────── */
  :where(.cbui-stepper) {
    --cbui-stepper-indicator-size:        var(--stepper-indicator-size, 1.75rem);
    --cbui-stepper-indicator-radius:      var(--stepper-indicator-radius, 9999px);
    --cbui-stepper-indicator-font-size:   var(--stepper-indicator-font-size, var(--font-size-md, 1rem));
    --cbui-stepper-indicator-font-weight: var(--stepper-indicator-font-weight, 600);
    --cbui-stepper-indicator-glyph-size:  var(--stepper-indicator-glyph-size, 1rem);

    --cbui-stepper-connector-thickness:   var(--stepper-connector-thickness, 2px);
    --cbui-stepper-connector-gap:         var(--stepper-connector-gap, 0.5rem);

    --cbui-stepper-label-gap:             var(--stepper-label-gap, 0.625rem);
    --cbui-stepper-row-gap:               var(--stepper-row-gap, 0.625rem);
    --cbui-stepper-step-gap:              var(--stepper-step-gap, 0.5rem);

    /* Vertical indicator offset — used by variants where the indicator is
       smaller than one line of label text (dot). The token is consumed by
       BOTH the indicator's margin-block-start AND the connector's top/bottom
       so the connector ends with equal gaps to the dot above and below. */
    --cbui-stepper-indicator-margin-top:  0px;

    --cbui-stepper-px:                    var(--stepper-padding-x, 0.75rem);
    --cbui-stepper-py:                    var(--stepper-padding-y, 0.75rem);
    --cbui-stepper-radius:                var(--stepper-radius, var(--card-radius, 0.75rem));
    --cbui-stepper-panel-padding:         var(--stepper-panel-padding, 0.875rem);
    --cbui-stepper-panel-radius:          var(--stepper-panel-radius, var(--card-radius, 0.75rem));

    --cbui-stepper-font-size:             var(--stepper-font-size, var(--font-size-md, 1rem));

    /* Tone — drives the complete + current colour. Default primary. */
    --cbui-stepper-tone-color:            var(--color-primary);
    --cbui-stepper-tone-contrast:         var(--color-primary-contrast, white);

    /* Status palette — derived from tone + neutrals.
       upcoming = quiet outlined chip
       current  = outlined chip with tone-coloured border + halo
       complete = tone-filled chip
       error    = danger-filled chip (overrides tone) */
    --cbui-stepper-upcoming-bg:           transparent;
    --cbui-stepper-upcoming-border:       var(--color-border);
    --cbui-stepper-upcoming-text:         var(--color-text-tertiary);

    --cbui-stepper-current-bg:            var(--color-bg-base);
    /* Border + text share the SAME colour token so the frame around a
     * current step and the text inside read as one unified shape. Dark
     * mode overrides the text token with a white-mixed brighter tone
     * (see below); the border automatically inherits the same lift. */
    --cbui-stepper-current-text:          var(--cbui-stepper-tone-color);
    --cbui-stepper-current-border:        var(--cbui-stepper-current-text);
    /* Current "glow" — diffuse Schein only. The visible ring is carried
     * by the indicator's `border-width: 2px` + tone-coloured border (set
     * on .cbui-stepper-step-current .cbui-stepper-step-indicator); stacking
     * an additional 2px solid box-shadow on top of that border doubled
     * the perceived ring thickness. Box-shadow now only renders the soft
     * outer halo. Semi-transparent is OK here because a diffuse glow blends
     * with the backdrop by design — and any minor colour shift in the
     * fading edge is hidden by the gradient. */
    --cbui-stepper-current-ring:
      0 0 14px 1px color-mix(in srgb, var(--cbui-stepper-tone-color) 40%, transparent);
    /* Active panel surface — tone on transparent (renders over whatever's
       behind, same recipe as Tabnav/Nav active states). 9% in light is
       subtle but visible; 16% in dark (set below) keeps it readable on
       dark page bg. Previously 12% / 18% on `--color-bg-elevated` which
       layered an elevated-mix UNDER the tone tint and made active panels
       read as "loud" cards. */
    --cbui-stepper-current-panel-bg:      color-mix(in srgb, var(--cbui-stepper-tone-color) 9%, transparent);
    --cbui-stepper-error-panel-bg:        color-mix(in srgb, var(--color-danger) 9%, transparent);

    --cbui-stepper-complete-bg:           var(--cbui-stepper-tone-color);
    --cbui-stepper-complete-border:       var(--cbui-stepper-tone-color);
    --cbui-stepper-complete-text:         var(--cbui-stepper-tone-contrast);

    /* Error indicator = a danger RING, not a filled chip. The circle keeps
       its size + position; only the treatment changes: a faint danger-tinted
       interior, a 2px danger border (the "ring"), and a danger-toned glyph.
       This reads as a validation warning rather than a destructive/filled
       state, and pairs with the ph:warning glyph + the sr-only "Fehler:". */
    --cbui-stepper-error-bg:              color-mix(in srgb, var(--color-danger) 12%, transparent);
    --cbui-stepper-error-border:          var(--color-danger);
    --cbui-stepper-error-text:            var(--color-danger);
    --cbui-stepper-error-ring:
      0 0 0 1px color-mix(in srgb, var(--color-danger) 35%, transparent);

    --cbui-stepper-connector-color:       color-mix(in srgb, var(--color-border) 80%, transparent);
    --cbui-stepper-connector-complete:    var(--cbui-stepper-tone-color);

    /* Container chrome — used by bg-fill / shadow-lift / border-pale modifiers */
    --cbui-stepper-bg:                    transparent;
    --cbui-stepper-border:                transparent;
    --cbui-stepper-shadow-lift:           0 4px 12px -2px rgb(0 0 0 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.04);
    --cbui-stepper-blur:                  12px;
    --cbui-stepper-saturation:            160%;

    /* Universal Button-style shade overlays for trigger hover */
    --cbui-shade-soft:   rgb(0 0 0 / 0.06);
    --cbui-shade-strong: rgb(0 0 0 / 0.12);

    position: relative;
    isolation: isolate;
    color: var(--color-text);
  }

  [data-scheme="dark"] :where(.cbui-stepper),
  .dark :where(.cbui-stepper) {
    --cbui-shade-soft:   rgb(255 255 255 / 0.08);
    --cbui-shade-strong: rgb(255 255 255 / 0.16);
    --cbui-stepper-shadow-lift: 0 8px 24px -4px rgb(0 0 0 / 0.4), 0 4px 8px -2px rgb(0 0 0 / 0.3);
    /* Dark mode — same recipe shape as light (diffuse glow only), bumped
     * blur + saturation for the darker backdrop. */
    --cbui-stepper-current-ring:
      0 0 18px 2px color-mix(in srgb, var(--cbui-stepper-tone-color) 50%, transparent);
    /* Brighten the current text colour in dark mode. The flat tone-colour
     * (e.g. gray for neutral, medium blue for primary) sits low against
     * the dark indicator interior and the dark label backdrop — too low
     * for comfortable reading. Mixing 60% white into the tone keeps the
     * tone identity but lifts brightness to a comfortable contrast.
     * Used by the indicator's number/glyph AND by the current step label
     * (see rule below). */
    --cbui-stepper-current-text:
      color-mix(in srgb, var(--cbui-stepper-tone-color) 40%, white);
    --cbui-stepper-current-panel-bg:  color-mix(in srgb, var(--cbui-stepper-tone-color) 16%, transparent);
    --cbui-stepper-error-panel-bg:    color-mix(in srgb, var(--color-danger) 16%, transparent);
  }

  /* ── Tone overrides ───────────────────────────────────────────────────── */
  .cbui-stepper-tone-neutral  { --cbui-stepper-tone-color: var(--color-text);
                                 --cbui-stepper-tone-contrast: var(--color-bg-base); }
  .cbui-stepper-tone-primary  { --cbui-stepper-tone-color: var(--color-primary);
                                 --cbui-stepper-tone-contrast: var(--color-primary-contrast, white); }
  .cbui-stepper-tone-accent   { --cbui-stepper-tone-color: var(--color-accent);
                                 --cbui-stepper-tone-contrast: var(--color-accent-contrast, white); }
  .cbui-stepper-tone-success  { --cbui-stepper-tone-color: var(--color-success);
                                 --cbui-stepper-tone-contrast: var(--color-success-contrast, white); }
  .cbui-stepper-tone-danger   { --cbui-stepper-tone-color: var(--color-danger);
                                 --cbui-stepper-tone-contrast: var(--color-danger-contrast, white); }
  .cbui-stepper-tone-warning  { --cbui-stepper-tone-color: var(--color-warning);
                                 --cbui-stepper-tone-contrast: var(--color-warning-contrast, white); }
  .cbui-stepper-tone-info     { --cbui-stepper-tone-color: var(--color-info);
                                 --cbui-stepper-tone-contrast: var(--color-info-contrast, white); }
  .cbui-stepper-tone-muted    { --cbui-stepper-tone-color: var(--color-text-secondary);
                                 --cbui-stepper-tone-contrast: var(--color-bg-base); }

  /* ── Container width ──────────────────────────────────────────────────── */
  .cbui-stepper-width-auto { display: inline-block; max-width: 100%; }
  .cbui-stepper-width-full { display: block; width: 100%; }

  /* ── Sizes ────────────────────────────────────────────────────────────── */
  .cbui-stepper-size-sm {
    --cbui-stepper-indicator-size:      1.5rem;
    --cbui-stepper-indicator-font-size: var(--font-size-md, 1rem);
    --cbui-stepper-indicator-glyph-size: 0.875rem;
    --cbui-stepper-font-size:           var(--font-size-md, 1rem);
    --cbui-stepper-label-gap:           0.5rem;
    --cbui-stepper-row-gap:             0.5rem;
  }
  .cbui-stepper-size-lg {
    --cbui-stepper-indicator-size:      2.25rem;
    --cbui-stepper-indicator-font-size: var(--font-size-md, 1rem);
    --cbui-stepper-indicator-glyph-size: 1.25rem;
    --cbui-stepper-font-size:           var(--font-size-md, 1rem);
    --cbui-stepper-label-gap:           0.75rem;
    --cbui-stepper-row-gap:             0.875rem;
  }
  .cbui-stepper-density-compact {
    --cbui-stepper-row-gap:             calc(var(--cbui-stepper-row-gap) * 0.5);
    --cbui-stepper-step-gap:            calc(var(--cbui-stepper-step-gap) * 0.5);
  }

  /* ── Container chrome — wraps the list when modifiers apply ──────────── */
  .cbui-stepper-chrome {
    background-color: var(--cbui-stepper-bg);
    border: 1px solid var(--cbui-stepper-border);
    border-radius: var(--cbui-stepper-radius);
  }
  .cbui-stepper-mod-bg-fill {
    --cbui-stepper-bg: color-mix(in srgb, var(--color-text) 4%, transparent);
  }
  .cbui-stepper-mod-bg-blur {
    --cbui-stepper-bg: color-mix(in srgb, var(--color-bg-elevated) 80%, transparent);
  }
  .cbui-stepper-mod-bg-blur .cbui-stepper-chrome {
    -webkit-backdrop-filter: blur(var(--cbui-stepper-blur)) saturate(var(--cbui-stepper-saturation));
    backdrop-filter: blur(var(--cbui-stepper-blur)) saturate(var(--cbui-stepper-saturation));
  }
  .cbui-stepper-mod-border-pale {
    --cbui-stepper-border: var(--cbui-stepper-connector-color);
  }
  .cbui-stepper-mod-shadow-lift .cbui-stepper-chrome {
    box-shadow: var(--cbui-stepper-shadow-lift);
  }
  /* When chrome modifiers apply, pad the chrome around the list. */
  :is(.cbui-stepper-mod-bg-fill, .cbui-stepper-mod-bg-blur, .cbui-stepper-mod-border-pale, .cbui-stepper-mod-shadow-lift) .cbui-stepper-chrome {
    padding-inline: var(--cbui-stepper-px);
    padding-block:  var(--cbui-stepper-py);
  }

  /* ── List ─────────────────────────────────────────────────────────────── */
  .cbui-stepper-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    min-width: 0;
  }

  /* ── Orientation: horizontal ──────────────────────────────────────────── */
  .cbui-stepper-orientation-horizontal .cbui-stepper-list {
    flex-direction: row;
    align-items: flex-start;
    gap: 0;  /* steps stretch to fill; connector lives in the gap mathematically */
  }

  .cbui-stepper-orientation-horizontal .cbui-stepper-step {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    position: relative;
    gap: var(--cbui-stepper-label-gap);
  }

  /* Alignment (horizontal only). justify is the default look (flex:1) — for
     start/center/end we collapse steps to content width and align the row. */
  .cbui-stepper-align-start   .cbui-stepper-list { justify-content: flex-start; }
  .cbui-stepper-align-center  .cbui-stepper-list { justify-content: center; }
  .cbui-stepper-align-end     .cbui-stepper-list { justify-content: flex-end; }
  :is(.cbui-stepper-align-start, .cbui-stepper-align-center, .cbui-stepper-align-end) .cbui-stepper-step {
    flex: 0 0 auto;
    min-width: 8rem;  /* enough room for label + connector to read */
  }

  /* Connector (horizontal): ::after pseudo from this step's indicator-right
     to the next step's indicator-left. Maths: indicator centre is at 50%
     of step. Next step is 100% to the right. So the connector spans from
     (50% + indicator-half + gap) to ((next 50%) - indicator-half - gap) =
     to (150% - indicator-half - gap). In CSS right-from-right-edge that's
     -50% + indicator-half + gap. */
  .cbui-stepper-orientation-horizontal .cbui-stepper-step:not(:last-child)::after {
    content: "";
    position: absolute;
    height: var(--cbui-stepper-connector-thickness);
    top: calc(var(--cbui-stepper-indicator-size) / 2 - var(--cbui-stepper-connector-thickness) / 2);
    left:  calc(50%  + var(--cbui-stepper-indicator-size) / 2 + var(--cbui-stepper-connector-gap));
    right: calc(-50% + var(--cbui-stepper-indicator-size) / 2 + var(--cbui-stepper-connector-gap));
    background: var(--cbui-stepper-connector-color);
    pointer-events: none;
    z-index: 0;
    transition: background-color 200ms ease;
  }

  /* ── Orientation: vertical ────────────────────────────────────────────── */
  .cbui-stepper-orientation-vertical .cbui-stepper-list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }
  .cbui-stepper-orientation-vertical .cbui-stepper-step {
    display: grid;
    grid-template-columns: var(--cbui-stepper-indicator-size) 1fr;
    column-gap: var(--cbui-stepper-label-gap);
    row-gap: 0;
    align-items: start;
    position: relative;
    padding-bottom: var(--cbui-stepper-row-gap);
  }
  .cbui-stepper-orientation-vertical .cbui-stepper-step:last-child {
    padding-bottom: 0;
  }
  .cbui-stepper-orientation-vertical .cbui-stepper-step:not(:last-child)::after {
    content: "";
    position: absolute;
    width: var(--cbui-stepper-connector-thickness);
    left: calc(var(--cbui-stepper-indicator-size) / 2 - var(--cbui-stepper-connector-thickness) / 2);
    /* Top + bottom both shifted by the indicator-margin-top so the gap to
       the dot above and the gap to the next dot below are equal. */
    top:    calc(var(--cbui-stepper-indicator-margin-top) + var(--cbui-stepper-indicator-size) + var(--cbui-stepper-connector-gap));
    bottom: calc(var(--cbui-stepper-connector-gap) - var(--cbui-stepper-indicator-margin-top));
    background: var(--cbui-stepper-connector-color);
    pointer-events: none;
    z-index: 0;
    transition: background-color 200ms ease;
  }

  /* ── Connector style modifiers ────────────────────────────────────────── */
  .cbui-stepper-connector-dashed .cbui-stepper-step:not(:last-child)::after {
    background: repeating-linear-gradient(
      to right,
      var(--cbui-stepper-connector-color) 0 6px,
      transparent 6px 10px
    );
  }
  .cbui-stepper-orientation-vertical.cbui-stepper-connector-dashed .cbui-stepper-step:not(:last-child)::after {
    background: repeating-linear-gradient(
      to bottom,
      var(--cbui-stepper-connector-color) 0 6px,
      transparent 6px 10px
    );
  }
  .cbui-stepper-connector-dotted .cbui-stepper-step:not(:last-child)::after {
    background: repeating-linear-gradient(
      to right,
      var(--cbui-stepper-connector-color) 0 2px,
      transparent 2px 6px
    );
  }
  .cbui-stepper-orientation-vertical.cbui-stepper-connector-dotted .cbui-stepper-step:not(:last-child)::after {
    background: repeating-linear-gradient(
      to bottom,
      var(--cbui-stepper-connector-color) 0 2px,
      transparent 2px 6px
    );
  }
  .cbui-stepper-connector-none .cbui-stepper-step:not(:last-child)::after {
    display: none;
  }

  /* Connector "filled" when the preceding step is complete — universal
     stepper convention. Applies to all connector styles. */
  .cbui-stepper-step-complete:not(:last-child)::after,
  .cbui-stepper-connector-dashed .cbui-stepper-step-complete:not(:last-child)::after,
  .cbui-stepper-connector-dotted .cbui-stepper-step-complete:not(:last-child)::after {
    background: var(--cbui-stepper-connector-complete);
  }
  .cbui-stepper-connector-dashed .cbui-stepper-step-complete:not(:last-child)::after {
    background: repeating-linear-gradient(
      to right,
      var(--cbui-stepper-connector-complete) 0 6px,
      transparent 6px 10px
    );
  }
  .cbui-stepper-orientation-vertical.cbui-stepper-connector-dashed .cbui-stepper-step-complete:not(:last-child)::after {
    background: repeating-linear-gradient(
      to bottom,
      var(--cbui-stepper-connector-complete) 0 6px,
      transparent 6px 10px
    );
  }
  .cbui-stepper-connector-dotted .cbui-stepper-step-complete:not(:last-child)::after {
    background: repeating-linear-gradient(
      to right,
      var(--cbui-stepper-connector-complete) 0 2px,
      transparent 2px 6px
    );
  }
  .cbui-stepper-orientation-vertical.cbui-stepper-connector-dotted .cbui-stepper-step-complete:not(:last-child)::after {
    background: repeating-linear-gradient(
      to bottom,
      var(--cbui-stepper-connector-complete) 0 2px,
      transparent 2px 6px
    );
  }

  /* ── Trigger (clickable wrap around indicator+body) ──────────────────── */
  .cbui-stepper-step-trigger {
    display: contents;
    color: inherit;
    text-decoration: none;
    cursor: pointer;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    text-align: inherit;
  }
  /* The actual hover/focus moves to the indicator+body via :hover on parent. */
  .cbui-stepper-step-trigger:focus { outline: none; }
  .cbui-stepper-step-trigger:focus-visible ~ * ,
  .cbui-stepper-step-trigger:focus-visible {
    /* nothing — focus ring lives on the indicator below */
  }

  /* ── Indicator ────────────────────────────────────────────────────────── */
  .cbui-stepper-step-indicator {
    flex-shrink: 0;
    width: var(--cbui-stepper-indicator-size);
    height: var(--cbui-stepper-indicator-size);
    border-radius: var(--cbui-stepper-indicator-radius);
    background: var(--cbui-stepper-upcoming-bg);
    border: 1px solid var(--cbui-stepper-upcoming-border);
    color: var(--cbui-stepper-upcoming-text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--cbui-stepper-indicator-font-size);
    font-weight: var(--cbui-stepper-indicator-font-weight);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    position: relative;
    z-index: 1;
    transition:
      background-color 200ms ease,
      border-color    200ms ease,
      color           200ms ease,
      box-shadow      200ms ease,
      transform       150ms ease;
  }

  .cbui-stepper-step-glyph {
    width:  var(--cbui-stepper-indicator-glyph-size);
    height: var(--cbui-stepper-indicator-glyph-size);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .cbui-stepper-step-glyph svg {
    width: 100%;
    height: 100%;
  }
  .cbui-stepper-step-number {
    line-height: 1;
  }

  /* Visually-hidden accessible text (e.g. the "Fehler:" prefix on an error
     step). Same canonical clip recipe as .cbui-spinner-label — readable by
     screen readers, invisible on screen, no layout impact. */
  .cbui-stepper-step-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  /* Status — current. Multiple cues stack for "you are here": thicker
   * border, tone-coloured fill in number/glyph, scale-up, and a halo ring.
   * The label below also goes tone-coloured + bold. */
  .cbui-stepper-step-current .cbui-stepper-step-indicator {
    background: var(--cbui-stepper-current-bg);
    border-color: var(--cbui-stepper-current-border);
    border-width: 2px;
    color: var(--cbui-stepper-current-text);
    box-shadow: var(--cbui-stepper-current-ring);
    transform: scale(1.06);
  }
  /* Status — complete */
  .cbui-stepper-step-complete .cbui-stepper-step-indicator {
    background: var(--cbui-stepper-complete-bg);
    border-color: var(--cbui-stepper-complete-border);
    color: var(--cbui-stepper-complete-text);
  }
  /* Status — error. A danger RING: faint danger-tinted interior + a 2px
     danger border (same border-width as `current`, so the ring reads as a
     deliberate state, not a filled chip) + a danger-toned warning glyph,
     with a soft outer halo to lift it off the surface. */
  .cbui-stepper-step-error .cbui-stepper-step-indicator {
    background: var(--cbui-stepper-error-bg);
    border-color: var(--cbui-stepper-error-border);
    border-width: 2px;
    color: var(--cbui-stepper-error-text);
    box-shadow: var(--cbui-stepper-error-ring);
  }
  /* Status — upcoming uses base styles. */

  /* ── Body ─────────────────────────────────────────────────────────────── */
  .cbui-stepper-step-body {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    font-size: var(--cbui-stepper-font-size);
    line-height: 1.3;
    min-width: 0;
  }
  .cbui-stepper-orientation-horizontal .cbui-stepper-step-body {
    align-items: center;
    padding-inline: 0.25rem;
  }

  .cbui-stepper-step-label {
    font-weight: 500;
    color: var(--color-text-secondary);
    overflow-wrap: anywhere;
  }
  .cbui-stepper-step-current .cbui-stepper-step-label {
    /* Same token as the indicator's foreground — keeps label + number
     * visually paired (matching colour shift in dark mode). */
    color: var(--cbui-stepper-current-text);
    font-weight: 700;
  }
  .cbui-stepper-step-complete .cbui-stepper-step-label {
    color: var(--color-text);
  }
  .cbui-stepper-step-error .cbui-stepper-step-label {
    color: var(--color-danger);
    font-weight: 600;
  }
  .cbui-stepper-step-disabled .cbui-stepper-step-label {
    color: var(--color-text-tertiary);
  }

  .cbui-stepper-step-optional {
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-tertiary);
    font-style: italic;
    margin-inline-start: 0.375rem;
    font-weight: 400;
  }

  .cbui-stepper-step-description {
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
    font-weight: 400;
    line-height: 1.4;
  }
  .cbui-stepper-orientation-horizontal .cbui-stepper-step-description {
    /* Two-line clamp keeps the strip a predictable height. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* ── Interactive (trigger hover) ──────────────────────────────────────── */
  .cbui-stepper-step-interactive { cursor: pointer; }
  /* The trigger uses display:contents, so :hover on the step itself works
     only when the trigger is the actual <a>/<button> child being hovered.
     We use :has() to bubble hover state up — modern browsers all support it.
     Current steps are EXCLUDED from hover affordances — they're already
     visually loud, and adding a lift + colour flip on top would (a) collide
     with the current-step `scale(1.06)` (transform property overwrites,
     does not stack) and (b) flip the tone-coloured label back to base text,
     reading as a visual stutter on hover. */
  .cbui-stepper-step-interactive:not(.cbui-stepper-step-current):has(.cbui-stepper-step-trigger:hover) .cbui-stepper-step-indicator {
    background-image: linear-gradient(var(--cbui-shade-soft), var(--cbui-shade-soft));
    transform: translateY(-1px);
  }
  .cbui-stepper-step-interactive:not(.cbui-stepper-step-current):has(.cbui-stepper-step-trigger:hover) .cbui-stepper-step-label {
    color: var(--color-text);
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
  }

  /* Focus ring on the indicator (the trigger has display:contents and no
     box of its own — focus must visualise on the visible indicator). */
  .cbui-stepper-step-interactive:has(.cbui-stepper-step-trigger:focus-visible) .cbui-stepper-step-indicator {
    box-shadow:
      0 0 0 2px var(--color-bg-base),
      0 0 0 4px var(--color-focus-ring, var(--cbui-stepper-tone-color));
  }
  /* When the focused step is current, stack the ring outside the existing halo. */
  .cbui-stepper-step-current.cbui-stepper-step-interactive:has(.cbui-stepper-step-trigger:focus-visible) .cbui-stepper-step-indicator {
    box-shadow:
      var(--cbui-stepper-current-ring),
      0 0 0 5px var(--color-bg-base),
      0 0 0 7px var(--color-focus-ring, var(--cbui-stepper-tone-color));
  }

  /* Disabled */
  .cbui-stepper-step-disabled { cursor: not-allowed; }
  .cbui-stepper-step-disabled .cbui-stepper-step-trigger { pointer-events: none; }
  .cbui-stepper-step-disabled .cbui-stepper-step-indicator { opacity: 0.6; }

  /* ── Variant: numbered (default) — base styles already cover this ─────── */

  /* ── Variant: dot — small filled dots, status via colour only ─────────── */
  .cbui-stepper-dot {
    --cbui-stepper-indicator-size:       0.625rem;
    --cbui-stepper-indicator-glyph-size: 0;
    --cbui-stepper-connector-gap:        0.375rem;
  }
  /* Vertical dot alignment: the dot is much smaller than one line of label
     text. We set the indicator-margin-top token; the indicator AND the
     connector both consume it so the gaps above and below the connector
     stay symmetric. */
  .cbui-stepper-orientation-vertical.cbui-stepper-dot {
    --cbui-stepper-indicator-margin-top: calc((var(--cbui-stepper-font-size) * 1.3 - var(--cbui-stepper-indicator-size)) / 2);
  }
  .cbui-stepper-orientation-vertical:not(.cbui-stepper-panel) .cbui-stepper-step-indicator {
    margin-block-start: var(--cbui-stepper-indicator-margin-top);
  }
  .cbui-stepper-dot .cbui-stepper-step-indicator {
    font-size: 0;
    border-width: 0;
    background: var(--color-text-tertiary);
  }
  .cbui-stepper-dot .cbui-stepper-step-number,
  .cbui-stepper-dot .cbui-stepper-step-glyph {
    display: none;
  }
  .cbui-stepper-dot .cbui-stepper-step-current .cbui-stepper-step-indicator {
    background: var(--cbui-stepper-tone-color);
    box-shadow: var(--cbui-stepper-current-ring);
    transform: scale(1.4);
  }
  .cbui-stepper-dot .cbui-stepper-step-complete .cbui-stepper-step-indicator {
    background: var(--cbui-stepper-tone-color);
  }
  .cbui-stepper-dot .cbui-stepper-step-error .cbui-stepper-step-indicator {
    /* Dot stays a filled danger dot — the ring treatment belongs to the
       chrome'd (numbered/default) indicator, not to the bare dot. */
    background: var(--color-danger);
    box-shadow: none;
  }
  .cbui-stepper-dot .cbui-stepper-step-upcoming .cbui-stepper-step-indicator {
    background: var(--color-text-tertiary);
    opacity: 0.6;
  }

  /* ── Variant: panel — each step is a card-like surface ──────────────────
   *
   * Background rule (universal, both light + dark): only the CURRENT panel
   * and ERROR panels carry a tinted background. Upcoming + complete panels
   * are just thin-bordered, transparent boxes — the indicator inside
   * already signals their state, so duplicating that with a tinted card
   * would compete with the active panel and muddy the hierarchy. In dark
   * mode especially, tinting every panel reads as "all panels are stacked
   * cards" which is the opposite of the "one active step" semantic.
   *
   * Border discipline: current + error use a 2px solid border (not 1px +
   * inset shadow). The previous inset-on-top-of-border combination created
   * subtle corner mis-alignment ("fransig"); a single 2px border renders
   * crisp corners in every flavor. `box-sizing: border-box` keeps the
   * outer dimensions stable so the layout doesn't shift when status changes.
   */
  .cbui-stepper-panel .cbui-stepper-step {
    box-sizing: border-box;
  }
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step {
    grid-template-columns: 1fr;
    border: 1px solid var(--color-border);
    border-radius: var(--cbui-stepper-panel-radius);
    padding: var(--cbui-stepper-panel-padding);
    margin-block-end: var(--cbui-stepper-row-gap);
    padding-bottom: var(--cbui-stepper-panel-padding);
  }
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step:last-child {
    margin-block-end: 0;
  }
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step:not(:last-child)::after {
    /* Connector sits in the inter-panel margin with 3px breathing room
     * above (so it doesn't touch this panel's bottom border) and below
     * (so it doesn't touch the next panel's top border). Without the
     * insets the line visually merges with the active panel's 2px border
     * — looking like the connector "lays on the panel edge". */
    left: calc(var(--cbui-stepper-panel-padding) + var(--cbui-stepper-indicator-size) / 2 - var(--cbui-stepper-connector-thickness) / 2);
    top: calc(100% + 3px);
    bottom: auto;
    height: calc(var(--cbui-stepper-row-gap) - 6px);
  }

  /* Panel head: indicator + body in one row */
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step > .cbui-stepper-step-trigger,
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step > .cbui-stepper-step-indicator {
    /* When trigger uses display:contents, indicator + body still grid-flow */
  }
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step {
    display: grid;
    grid-template-columns: var(--cbui-stepper-indicator-size) 1fr;
    column-gap: var(--cbui-stepper-label-gap);
    align-items: center;
  }
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step-body {
    align-items: flex-start;
  }
  /* Specificity bumped to 4 classes by compounding `.cbui-stepper-step`
   * with `.cbui-stepper-step-current` on the same element — this beats
   * the base panel rule (`.cbui-stepper-panel.cbui-stepper-orientation-*
   * .cbui-stepper-step` at 0,3,0) regardless of source order. Without
   * this, the base `border: 1px solid var(--color-border)` shorthand
   * outweighed our current-state override (which is positioned earlier
   * in the file than the horizontal base rule), leaving the panel border
   * stuck at the neutral default in dark mode. */
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step.cbui-stepper-step-current,
  .cbui-stepper-panel.cbui-stepper-orientation-horizontal .cbui-stepper-step.cbui-stepper-step-current {
    background: var(--cbui-stepper-current-panel-bg);
    border: 2px solid var(--cbui-stepper-current-border);
  }
  .cbui-stepper-panel.cbui-stepper-orientation-vertical .cbui-stepper-step.cbui-stepper-step-error,
  .cbui-stepper-panel.cbui-stepper-orientation-horizontal .cbui-stepper-step.cbui-stepper-step-error {
    background: var(--cbui-stepper-error-panel-bg);
    border: 2px solid var(--color-danger);
  }
  /* Active panel label: keep the base text colour, not the tone colour.
   * The panel already signals "current" through tinted background + tone-
   * coloured border; flipping the label to the tone colour layers tone-on-
   * tone and tanks contrast — especially in dark mode where the tinted bg
   * sits between the dark page and a saturated label, losing legibility. */
  .cbui-stepper-panel .cbui-stepper-step-current .cbui-stepper-step-label {
    color: var(--color-text);
  }

  /* Panel variant in horizontal — capsule per step in a row */
  .cbui-stepper-panel.cbui-stepper-orientation-horizontal .cbui-stepper-step {
    flex-direction: row;
    align-items: center;
    border: 1px solid var(--color-border);
    border-radius: var(--cbui-stepper-panel-radius);
    padding: var(--cbui-stepper-panel-padding);
    text-align: start;
    gap: var(--cbui-stepper-label-gap);
    margin-inline-end: var(--cbui-stepper-step-gap);
  }
  .cbui-stepper-panel.cbui-stepper-orientation-horizontal .cbui-stepper-step:last-child {
    margin-inline-end: 0;
  }
  .cbui-stepper-panel.cbui-stepper-orientation-horizontal .cbui-stepper-step-body {
    align-items: flex-start;
    text-align: start;
  }
  .cbui-stepper-panel.cbui-stepper-orientation-horizontal .cbui-stepper-step:not(:last-child)::after {
    /* Same breathing-room rule as vertical: 3px gap from this panel's right
     * border and 3px gap before the next panel's left border. */
    top: 50%;
    transform: translateY(-50%);
    left: calc(100% + 3px);
    right: auto;
    width: calc(var(--cbui-stepper-step-gap) - 6px);
  }
  /* ── Variant: plain — labels + track only, no indicator chrome ────────
   *
   * Horizontal: the track lives at the bottom edge of each step. Adjacent
   *   step ::afters form a single continuous bar; per-step status
   *   determines the segment colour. The current step's segment is
   *   slightly thicker so it reads as the "you are here" marker on a
   *   Tabnav-underline-style rail.
   *
   * Vertical: the rail is the inline-start border on each step; complete
   *   and current steps paint it tone-coloured so the rail "fills" from
   *   top down to the current step.
   *
   * Connector-style modifiers (dashed/dotted) are ignored for plain —
   * the rail metaphor is inherently solid. `connector="none"` removes
   * the track entirely.
   */
  .cbui-stepper-plain .cbui-stepper-step-indicator {
    display: none;
  }
  .cbui-stepper-plain .cbui-stepper-step-label {
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: var(--font-size-md, 1rem);
    font-weight: 600;
    color: var(--color-text-tertiary);
  }
  .cbui-stepper-plain .cbui-stepper-step-current .cbui-stepper-step-label,
  .cbui-stepper-plain .cbui-stepper-step-complete .cbui-stepper-step-label {
    color: var(--cbui-stepper-tone-color);
  }

  /* Plain horizontal: each step has a full-width track at the bottom.
     Override the base "between indicator centres" positioning — the plain
     layout doesn't have indicator centres to anchor between.

     `align-items: stretch` on the list is critical: the current step's
     label is rendered one size up (`sm` vs `xs`) which makes its body
     taller than the other steps' bodies. With the default
     `align-items: flex-start`, each step ends at a different y, and the
     bottom-aligned tracks end up on DIFFERENT vertical lines — visually
     offset between completed and current steps. Stretching makes all
     steps the same height (matching the tallest, i.e. the current), so
     the tracks sit on one continuous horizontal line. */
  .cbui-stepper-plain.cbui-stepper-orientation-horizontal .cbui-stepper-list {
    align-items: stretch;
  }
  .cbui-stepper-plain.cbui-stepper-orientation-horizontal .cbui-stepper-step {
    gap: 0;
    padding-block: 0.5rem;
    padding-inline: 0.5rem;
    justify-content: center;
  }
  .cbui-stepper-plain.cbui-stepper-orientation-horizontal .cbui-stepper-step::after {
    content: "";
    position: absolute;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--cbui-stepper-connector-thickness);
    background: var(--cbui-stepper-connector-color);
    pointer-events: none;
    z-index: 0;
    transition: background-color 200ms ease, height 150ms ease;
  }
  .cbui-stepper-plain.cbui-stepper-orientation-horizontal .cbui-stepper-step-complete::after {
    background: var(--cbui-stepper-tone-color);
  }
  .cbui-stepper-plain.cbui-stepper-orientation-horizontal .cbui-stepper-step-current {
    background: color-mix(in srgb, var(--cbui-stepper-tone-color) 9%, transparent);
    border-radius: var(--radius-sm, 0.25rem) var(--radius-sm, 0.25rem) 0 0;
  }
  .cbui-stepper-plain.cbui-stepper-orientation-horizontal .cbui-stepper-step-current::after {
    background: var(--cbui-stepper-tone-color);
    height: calc(var(--cbui-stepper-connector-thickness) + 2px);
  }
  .cbui-stepper-plain.cbui-stepper-orientation-horizontal .cbui-stepper-step-current .cbui-stepper-step-label {
    font-weight: var(--font-weight-bold, 700);
    letter-spacing: 0.05em;
  }
  /* connector="none" wins over the plain track. */
  .cbui-stepper-plain.cbui-stepper-connector-none .cbui-stepper-step::after {
    display: none;
  }

  /* Plain vertical: the rail is the inline-start border. */
  .cbui-stepper-plain.cbui-stepper-orientation-vertical .cbui-stepper-step {
    grid-template-columns: 1fr;
    padding-inline-start: 0.75rem;
    border-inline-start: var(--cbui-stepper-connector-thickness) solid var(--cbui-stepper-connector-color);
  }
  .cbui-stepper-plain.cbui-stepper-orientation-vertical .cbui-stepper-step:not(:last-child)::after {
    display: none;
  }
  .cbui-stepper-plain.cbui-stepper-orientation-vertical .cbui-stepper-step-complete,
  .cbui-stepper-plain.cbui-stepper-orientation-vertical .cbui-stepper-step-current {
    /* Rail colour — same token as the rest of the current chrome so the
     * vertical rail brightens in dark mode in step with everything else. */
    border-inline-start-color: var(--cbui-stepper-current-border);
  }
  .cbui-stepper-plain.cbui-stepper-orientation-vertical .cbui-stepper-step-current {
    /* Match the horizontal current treatment: thicker rail + tone-tinted
       background block + larger label. */
    border-inline-start-width: calc(var(--cbui-stepper-connector-thickness) + 2px);
    background: color-mix(in srgb, var(--cbui-stepper-tone-color) 9%, transparent);
    margin-inline-start: -1px;  /* keep rail visually flush after border-width bump */
    padding-block: 0.375rem;
    padding-inline-end: 0.5rem;
    border-radius: 0 var(--radius-sm, 0.25rem) var(--radius-sm, 0.25rem) 0;
  }
  .cbui-stepper-plain.cbui-stepper-orientation-vertical .cbui-stepper-step-current .cbui-stepper-step-label {
    font-weight: var(--font-weight-bold, 700);
    letter-spacing: 0.05em;
  }

  /* ── Reduced motion ──────────────────────────────────────────────────── */
  .a11y-reduced-motion .cbui-stepper-step-indicator,
  .a11y-reduced-motion .cbui-stepper-step:not(:last-child)::after {
    transition: none;
  }
  .a11y-reduced-motion .cbui-stepper-step-interactive:has(.cbui-stepper-step-trigger:hover) .cbui-stepper-step-indicator {
    transform: none;
  }
  .a11y-reduced-motion .cbui-stepper-dot .cbui-stepper-step-current .cbui-stepper-step-indicator {
    transform: none;
  }

  /* ── High contrast ──────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-stepper-step-indicator {
    border-width: 2px;
  }
  .a11y-high-contrast .cbui-stepper-step:not(:last-child)::after {
    --cbui-stepper-connector-thickness: 3px;
  }
}

/* cbui/components/nav */
/* cbui-nav — vertical navigation (sidebar / docs nav).
 *
 * Anatomy:
 *   .cbui-nav              outer <nav> (flex column, width 100% by default)
 *   .cbui-nav-header       optional leading slot (logo / search input / brand)
 *   .cbui-nav-body         scroll container for the items themselves
 *   .cbui-nav-footer       optional trailing slot (user / settings)
 *
 *   Items inside the body are <NavItem>, <NavSeparator direction="horizontal">,
 *   <NavGroup>, <NavTree> — the same NavItem class that powers the horizontal
 *   Navbar, so styling rules carry across.
 *
 * Variants (visual treatment):
 *   default — labelled sidebar, fluid width (100% of container).
 *   rail    — icon-only narrow sidebar (~3.5rem). Hides labels, descriptions,
 *             badges, shortcuts; keeps icons centered. Padding-x collapses to 0
 *             so the round/pill items feel centered in the rail.
 *
 * Modifiers (compose with +):
 *   bg-fill     — soft tinted background.
 *   bg-blur     — backdrop-filter.
 *   shadow-lift — static drop shadow.
 *
 * Tones (semantic colour for the bar background, not the items):
 *   neutral (default) + primary / accent / info / success / danger / warning / muted.
 *   Tone-coloured Navs are rare; reserve them for branded app shells or
 *   highlight-mode dashboards. Items still inherit the bar text-color so they
 *   read against the tone.
 *
 * Layout knobs:
 *   floating  — detached from edges (margin + radius + shadow). Pairs with
 *               the flavor's --nav-radius token.
 *   sticky    — position: sticky pinned to top.
 *   size      — sm / md / lg, cascades to NavItem children via CSS variable.
 *
 * Width:
 *   Nav is fluid by default — `width: 100%` and lets the parent container
 *   decide. The `rail` variant pins width to var(--nav-rail-width) so it
 *   collapses on its own. Authors who want a fixed sidebar wrap the Nav in
 *   their own container (e.g. `<aside style="width: 280px;"><Nav>…`).
 */

/* ── Default tokens (overridable by flavor) ───────────────────────────────── */
.cbui-nav {
    --cbui-nav-bg: var(--color-bg-elevated);
    --cbui-nav-text: var(--color-text);
    --cbui-nav-text-secondary: var(--color-text-secondary);
    --cbui-nav-border: var(--color-border);
    --cbui-nav-radius: var(--nav-radius, 0.75rem);
    --cbui-nav-px: var(--nav-padding-x, 0.5rem);
    --cbui-nav-py: var(--nav-padding-y, 0.5rem);
    --cbui-nav-gap: 0.125rem;
    --cbui-nav-section-gap: 0.5rem;
    --cbui-nav-floating-margin: 1rem;
    --cbui-nav-blur: 12px;
    --cbui-nav-saturation: 160%;
    --cbui-nav-bg-alpha: 0.78;
    --cbui-nav-rail-width: var(--nav-rail-width, 3.5rem);
    --cbui-nav-shadow-lift: 0 4px 12px -2px rgb(0 0 0 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.04);
    --cbui-nav-shadow-floating: 0 8px 24px -6px rgb(0 0 0 / 0.12), 0 2px 6px -2px rgb(0 0 0 / 0.06);

    /* ── Dropdown form-language tokens ───────────────────────────────────
     * Headers, footers, separators, item hover surfaces and section labels
     * all read from these — kept in lockstep with the Dropdown component so
     * a Nav and a Dropdown sit next to each other and feel like one system.
     * Header / footer surface — same intent as a card panel band.
     */
    --cbui-nav-hf-bg: var(--color-bg-subtle);
    /* Light-mode: matches the panel border (continuation of the frame).
       Dark-mode override below flips this to a darker-than-hf-bg colour so it
       reads as a depth groove instead of a bright ridge — same trick as the
       Dropdown panel. */
    --cbui-nav-hf-border: var(--cbui-nav-border);
    /* Section-label rhythm — Dropdown values verbatim. */
    --cbui-nav-label-fs: var(--font-size-md, 1rem);
    /* Item surfaces — Dropdown values verbatim. Light-mode hover sits on the
       neutral subtle gray; dark-mode flips to bg-muted (the Dropdown pattern)
       so hover stays readable on the receded panel. */
    --cbui-nav-item-surface-hover: var(--color-bg-subtle);
    --cbui-nav-item-surface-active: var(--color-bg-muted);
    /* Visual inset of the horizontal separator from the panel border —
       independent of nav-px so the separator gap reads uniform across modes. */
    --cbui-nav-sep-inset: 0.625rem;

    display: flex;
    flex-direction: column;
    width: 100%;
    color: var(--cbui-nav-text);
    background-color: var(--cbui-nav-bg);
    transition:
        background-color 200ms ease,
        backdrop-filter 200ms ease,
        box-shadow 200ms ease,
        width 200ms ease;
    min-width: 0;
}

/* NavTree as a standalone vertical-nav context — bootstraps the same
   form-language tokens the Nav container provides, so a NavTree rendered
   outside a <Nav> still gets Dropdown-style item surfaces (hover/active),
   the right separator colour, and consistent label typography. When the
   tree IS nested in a Nav, these declarations are no-ops (same values).
   The fallbacks default to the page palette so a tree on a custom canvas
   still has a sane border colour for its inline separators. */
.cbui-nav-tree {
    --cbui-nav-border: var(--color-border);
    --cbui-nav-text: var(--color-text);
    --cbui-nav-text-secondary: var(--color-text-secondary);
    --cbui-nav-px: var(--nav-padding-x, 0.5rem);
    --cbui-nav-hf-bg: var(--color-bg-subtle);
    --cbui-nav-hf-border: var(--cbui-nav-border);
    --cbui-nav-label-fs: var(--font-size-md, 1rem);
    --cbui-nav-item-surface-hover: var(--color-bg-subtle);
    --cbui-nav-item-surface-active: var(--color-bg-muted);
    --cbui-nav-sep-inset: 0.625rem;
}

/* Dark-mode: header/footer band lifts off the panel slightly, the separator
   line darkens below it (groove, not ridge), and the item hover surface
   shifts to bg-muted so hover registers on the receded panel — direct mirror
   of the Dropdown rule in dropdown.css. The token override is shared
   between Nav and standalone NavTree so a tree dropped on any panel reads
   the same in dark mode. */
.dark :where(.cbui-nav, .cbui-nav-tree),
[data-scheme="dark"] :where(.cbui-nav, .cbui-nav-tree) {
    --cbui-nav-hf-bg: color-mix(in srgb, var(--color-surface) 50%, var(--color-bg-muted));
    --cbui-nav-hf-border: color-mix(in srgb, var(--cbui-nav-bg, var(--color-bg-elevated)) 60%, black);
    --cbui-nav-item-surface-hover: var(--color-bg-muted);
    --cbui-nav-item-surface-active: var(--color-bg-strong, rgb(255 255 255 / 0.14));
}

/* Items inside ANY vertical-nav context (a <Nav> wrapper OR a standalone
   <NavTree>) get the Dropdown form-language: fixed neutral hover/active
   surface (instead of the Navbar's currentColor mix), Dropdown-inset item
   radius, full-width stretch. NavTree without an outer Nav still picks up
   the form because both selectors are listed — single source of truth. */
:is(.cbui-nav, .cbui-nav-tree) .cbui-nav-item-base,
:is(.cbui-nav, .cbui-nav-tree) .cbui-nav-item {
    --cbui-nav-item-text: var(--cbui-nav-text);
    --cbui-nav-item-text-muted: var(--cbui-nav-text-secondary);
    --cbui-nav-item-hover-bg: var(--cbui-nav-item-surface-hover);
    --cbui-nav-item-active-bg: var(--cbui-nav-item-surface-active);
    --cbui-nav-item-radius: var(--radius-sm, 0.375rem);
    width: 100%;
    /* Items stretch to the panel edge — tighter alignment for label /
       description / end-slots and sane click targets. */
    display: flex;
}

/* Tone-coloured vertical Navs (rare — primary/danger/etc): items revert to
   the Navbar currentColor-mix recipe so hover stays readable on the tinted
   bg. The Dropdown-style fixed neutral surface would visually disconnect
   from the tone-coloured panel. NavTrees inside a tone-coloured Nav inherit
   this via the descendant selector. */
.cbui-nav:not(.cbui-nav-tone-neutral) .cbui-nav-item-base,
.cbui-nav:not(.cbui-nav-tone-neutral) .cbui-nav-item {
    --cbui-nav-item-hover-bg: color-mix(in srgb, currentColor 10%, transparent);
    --cbui-nav-item-active-bg: color-mix(in srgb, currentColor 14%, transparent);
}

/* Icon-spacer: hidden in horizontal Navbar (icons inline, no alignment cost),
   but inside a vertical context it must reserve the icon slot so labels of
   icon-less items align with iconed siblings — same pattern as DropdownItem. */
:is(.cbui-nav, .cbui-nav-tree) .cbui-nav-item-icon-spacer {
    display: inline-flex;
}

/* ── Header / Body / Footer ──────────────────────────────────────────────────
 * Header and footer mirror DropdownHeader/Footer: subtle "card-band" surface,
 * min-height tall enough to seat a sm Button, dark-mode groove separator,
 * matched-to-the-box edge corners on floating Navs, and a 0.25rem breathing
 * gap between the band and the first/last body item. The padding-x lines up
 * with item-px so a brand row inside the header sits flush with the items
 * underneath.
 */
.cbui-nav-header,
.cbui-nav-footer {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    /* Match Dropdown header padding rhythm: align with item content
       (item-px + nav-px) on the inline axis; vertical padding mirrors
       --cbui-nav-py so the band sits in the same rhythm as the items. */
    padding-inline: calc(var(--cbui-nav-item-px, 0.75rem) + var(--cbui-nav-px));
    padding-block: var(--cbui-nav-py);
    min-height: 2.5rem;
    flex-shrink: 0;
    background-color: var(--cbui-nav-hf-bg);
}
.cbui-nav-header {
    border-bottom: 1px solid var(--cbui-nav-hf-border);
}
.cbui-nav-footer {
    border-top: 1px solid var(--cbui-nav-hf-border);
    margin-block-start: auto;
}

/* Edge-corner radius matching for floating Navs — header/footer corners hug
   the panel's outer curve via the same formula DropdownHeader/Footer use.
   Non-floating Navs sit edge-to-edge inside their wrapper, so this is a no-op. */
.cbui-nav-floating > .cbui-nav-header:first-child {
    border-start-start-radius: calc(var(--cbui-nav-radius) - 1px);
    border-start-end-radius:   calc(var(--cbui-nav-radius) - 1px);
}
.cbui-nav-floating > .cbui-nav-footer:last-child {
    border-end-start-radius: calc(var(--cbui-nav-radius) - 1px);
    border-end-end-radius:   calc(var(--cbui-nav-radius) - 1px);
}

/* Breathing room between the band and the body — matches the Dropdown
   `+ first item gets margin-top: 0.25rem` rule so the band reads as a
   distinct section instead of crowding the first item. */
.cbui-nav-header + .cbui-nav-body {
    padding-block-start: calc(var(--cbui-nav-py) + 0.25rem);
}
.cbui-nav-body:has(+ .cbui-nav-footer) {
    padding-block-end: calc(var(--cbui-nav-py) + 0.25rem);
}

.cbui-nav-body {
    display: flex;
    flex-direction: column;
    gap: var(--cbui-nav-gap);
    padding: var(--cbui-nav-py) var(--cbui-nav-px);
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
}

/* Inline separators inside any vertical-nav context (Nav OR standalone NavTree)
   — Dropdown rhythm: 1px line, fixed border colour (not currentColor-mix),
   inset by --cbui-nav-sep-inset minus whatever inline padding the body already
   provides. Gives a uniform visual gap to the panel border regardless of body
   padding. Overrides the `.cbui-nav-separator-horizontal` defaults from
   navbar.css (which use currentColor-mix) so separators in a vertical context
   match DropdownSeparator. */
:is(.cbui-nav, .cbui-nav-tree) .cbui-nav-separator-horizontal {
    height: 1px;
    width: auto;
    background-color: var(--cbui-nav-border);
    margin-block: 0.25rem;
    margin-inline: max(0px, calc(var(--cbui-nav-sep-inset) - var(--cbui-nav-px, 0px)));
}

/* ── Variants ────────────────────────────────────────────────────────────── */
.cbui-nav-default {
    background-color: var(--cbui-nav-bg);
}

/* Rail — icon-only narrow sidebar. Items collapse to icon-only by hiding the
   text body, badge, shortcut, and end icon. The caret stays visible because a
   rail with collapsible groups still wants to show the toggle (toggling
   reveals a popout — use Tooltip / Dropdown to surface labels on hover). */
.cbui-nav-rail {
    width: var(--cbui-nav-rail-width);
    flex-shrink: 0;
    align-items: center;
}
.cbui-nav-rail .cbui-nav-body {
    align-items: center;
    padding-inline: 0;
}
.cbui-nav-rail .cbui-nav-header,
.cbui-nav-rail .cbui-nav-footer {
    justify-content: center;
    padding-inline: 0;
}
.cbui-nav-rail .cbui-nav-item {
    --cbui-nav-item-px: 0;
    --cbui-nav-item-py: 0.625rem;
    width: calc(var(--cbui-nav-rail-width) - var(--cbui-nav-px) * 2);
    justify-content: center;
}
.cbui-nav-rail .cbui-nav-item-body,
.cbui-nav-rail .cbui-nav-item-badge,
.cbui-nav-rail .cbui-nav-item-shortcut,
.cbui-nav-rail .cbui-nav-item-icon-end,
.cbui-nav-rail .cbui-nav-item-icon-spacer,
.cbui-nav-rail .cbui-nav-item-indent {
    display: none;
}
/* In rail mode, the badge can still convey "unread" via a dot at the icon
   corner. Authors who pass `badge="•"` get the smallest possible signal. */
.cbui-nav-rail .cbui-nav-item-icon {
    position: relative;
}
.cbui-nav-rail .cbui-nav-group-label,
.cbui-nav-rail .cbui-nav-group-content {
    display: none;
}

/* ── Modifiers ───────────────────────────────────────────────────────────── */
.cbui-nav-mod-bg-blur {
    backdrop-filter: blur(var(--cbui-nav-blur)) saturate(var(--cbui-nav-saturation));
    -webkit-backdrop-filter: blur(var(--cbui-nav-blur)) saturate(var(--cbui-nav-saturation));
    background-color: color-mix(in srgb, var(--cbui-nav-bg) calc(var(--cbui-nav-bg-alpha) * 100%), transparent);
}

.cbui-nav-mod-bg-fill.cbui-nav-default {
    background-color: color-mix(in srgb, var(--cbui-nav-bg) 92%, transparent);
}

.cbui-nav-mod-shadow-lift {
    box-shadow: var(--cbui-nav-shadow-lift);
}

/* ── Tones — like Navbar, only the bg + on-color flip; items inherit. ────── */
.cbui-nav-tone-neutral { /* default */ }
.cbui-nav-tone-primary { --cbui-nav-bg: var(--color-primary); --cbui-nav-text: var(--color-text-on-primary); --cbui-nav-text-secondary: color-mix(in srgb, var(--color-text-on-primary) 75%, transparent); --cbui-nav-border: color-mix(in srgb, var(--color-text-on-primary) 18%, transparent); }
.cbui-nav-tone-info    { --cbui-nav-bg: var(--color-info);    --cbui-nav-text: var(--color-text-on-info);    --cbui-nav-text-secondary: color-mix(in srgb, var(--color-text-on-info) 75%, transparent);    --cbui-nav-border: color-mix(in srgb, var(--color-text-on-info) 18%, transparent); }
.cbui-nav-tone-success { --cbui-nav-bg: var(--color-success); --cbui-nav-text: var(--color-text-on-success); --cbui-nav-text-secondary: color-mix(in srgb, var(--color-text-on-success) 75%, transparent); --cbui-nav-border: color-mix(in srgb, var(--color-text-on-success) 18%, transparent); }
.cbui-nav-tone-warning { --cbui-nav-bg: var(--color-warning); --cbui-nav-text: var(--color-text-on-warning); --cbui-nav-text-secondary: color-mix(in srgb, var(--color-text-on-warning) 75%, transparent); --cbui-nav-border: color-mix(in srgb, var(--color-text-on-warning) 18%, transparent); }
.cbui-nav-tone-danger  { --cbui-nav-bg: var(--color-danger);  --cbui-nav-text: var(--color-text-on-danger);  --cbui-nav-text-secondary: color-mix(in srgb, var(--color-text-on-danger) 75%, transparent);  --cbui-nav-border: color-mix(in srgb, var(--color-text-on-danger) 18%, transparent); }
.cbui-nav-tone-accent  { --cbui-nav-bg: var(--color-text);    --cbui-nav-text: var(--color-bg-base);          --cbui-nav-text-secondary: color-mix(in srgb, var(--color-bg-base) 70%, transparent);          --cbui-nav-border: color-mix(in srgb, var(--color-bg-base) 18%, transparent); }
.cbui-nav-tone-muted   { --cbui-nav-bg: var(--color-bg-subtle, var(--color-bg-elevated)); }
/* plain (CB-1154, SectionNav's Stripe-/GitHub-settings look): no panel
 * fill, no border — the active-item treatment (accent bar instead of a
 * tinted pill) lives in navbar.css next to the pill/underline/plain
 * NavItem variants. */
.cbui-nav-tone-plain   { --cbui-nav-bg: transparent; --cbui-nav-border: transparent; }

/* ── Floating layout ─────────────────────────────────────────────────────── */
.cbui-nav-floating {
    margin: var(--cbui-nav-floating-margin);
    border-radius: var(--cbui-nav-radius);
    border: 1px solid var(--cbui-nav-border);
    box-shadow: var(--cbui-nav-shadow-floating);
    overflow: hidden;
}

/* ── Sizes (cascade to NavItem) ──────────────────────────────────────────── */
.cbui-nav-size-sm .cbui-nav-item { --cbui-nav-item-font-size: var(--font-size-md, 1rem); --cbui-nav-item-py: calc(var(--nav-item-padding-y, 0.5rem) * 0.7); }
.cbui-nav-size-lg .cbui-nav-item { --cbui-nav-item-font-size: var(--font-size-md, 1rem); --cbui-nav-item-py: calc(var(--nav-item-padding-y, 0.5rem) * 1.2); }

/* ── Sticky ──────────────────────────────────────────────────────────────── */
.cbui-nav-sticky {
    position: sticky;
    top: 0;
    max-height: 100vh;
    z-index: 20;
}
.cbui-nav-sticky.cbui-nav-floating {
    top: var(--cbui-nav-floating-margin);
    max-height: calc(100vh - 2 * var(--cbui-nav-floating-margin));
}

/* ── A11y ────────────────────────────────────────────────────────────────── */
.a11y-reduced-motion .cbui-nav,
.a11y-reduced-motion .cbui-nav-item-caret {
    transition: none;
}
.a11y-reduced-transparency .cbui-nav-mod-bg-blur {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--cbui-nav-bg);
}
.a11y-high-contrast .cbui-nav-floating,
.a11y-high-contrast .cbui-nav-header,
.a11y-high-contrast .cbui-nav-footer {
    border-color: var(--color-border-strong);
    border-width: 2px;
}

/* ──────────────────────────────────────────────────────────────────────────
 * NavGroup — collapsible section with a header + content slot.
 * The header is a regular <NavItem expandable> in vertical mode (caret on
 * the start, label, optional icon/badge); the content is a flex-column of
 * items that hides via `hidden` when the user collapses the group.
 * ────────────────────────────────────────────────────────────────────────── */
.cbui-nav-group {
    display: flex;
    flex-direction: column;
    gap: var(--cbui-nav-gap);
}
.cbui-nav-group + .cbui-nav-group {
    margin-block-start: var(--cbui-nav-section-gap);
}
.cbui-nav-group-label {
    /* A non-collapsible group renders just a label as a small uppercase title
       above its items (Slack/Discord-style category headers). Typography +
       padding rhythm verbatim from DropdownLabel so a Nav with grouped
       sections reads identically to a Dropdown with section labels. */
    font-size: var(--cbui-nav-label-fs, var(--font-size-md, 1rem));
    font-weight: var(--font-weight-semibold, 600);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    line-height: 1.4;
    color: var(--color-text-muted);
    padding-inline: var(--cbui-nav-item-px, var(--nav-group-label-padding-x, 0.75rem));
    /* Asymmetric block padding mirrors DropdownLabel — more headroom above,
       almost flush below so the items sit close enough to read as members. */
    padding-block: 0.375rem 0.125rem;
    margin: 0;
    user-select: none;
}
.cbui-nav-group-content {
    display: flex;
    flex-direction: column;
    gap: var(--cbui-nav-gap);
}
.cbui-nav-group-content[hidden] {
    display: none;
}

/* ──────────────────────────────────────────────────────────────────────────
 * NavTree — recursive tree.
 * Tree nodes are NavItem instances; a node with children gets `expandable`
 * (the caret), and the children list (`<ul role="group">`) is hidden when
 * `aria-expanded="false"`. Lazy-load support: the children list can be a
 * placeholder marked with `data-cbui-nav-tree-load-url` — the Stimulus
 * controller fetches the URL on first expand and replaces the placeholder
 * with the response HTML.
 *
 * Indent math (read this if you ever want to change --cbui-nav-tree-indent):
 *
 *   Items have padding-inline of `--cbui-nav-item-px` (0.75rem at md),
 *   then [caret/spacer · gap · icon · gap · label] in a flex row with
 *   `--cbui-nav-item-gap` (0.5rem) between slots, both caret and icon at
 *   `--cbui-nav-item-icon-size` (1rem).
 *
 *   Default tree-children indent = `caret-size + gap` = 1rem + 0.5rem = 1.5rem.
 *   That puts a child's caret/spacer column directly under its parent's
 *   icon column — the natural "guide line" each level adds — and keeps a
 *   child's icon column aligned with where its parent's label starts.
 *   Picking 1.25rem (the previous value) breaks both alignments by 4px.
 * ────────────────────────────────────────────────────────────────────────── */
.cbui-nav-tree {
    /* Per-level indent presets — picked by the `indent` prop on <NavTree>.
     *
     *   sm = caret width only            → 1rem  (16px)
     *         compact; child caret column sits in the gap between parent
     *         caret and parent icon. Loses column alignment but saves room
     *         for very deep file trees / dense docs nav.
     *
     *   md = caret + gap                 → 1.5rem (24px)
     *         default. Child caret column sits exactly under parent icon
     *         column, so child icons line up with parent labels — the
     *         clean staircase rhythm.
     *
     *   lg = 2 × caret                   → 2rem  (32px)
     *         generous. Child caret column sits past parent icon column,
     *         giving each nesting level extra breathing room for marketing
     *         / settings nav where rows are otherwise sparse.
     *
     * The active value lives in --cbui-nav-tree-indent, used by every
     * nested <ul.cbui-nav-tree-children> for its padding-inline-start.
     * Authors can also override it inline (style="--cbui-nav-tree-indent: …").
     */
    --cbui-nav-tree-indent-sm: var(--cbui-nav-item-icon-size, 1rem);
    --cbui-nav-tree-indent-md: calc(
        var(--cbui-nav-item-icon-size, 1rem) + var(--cbui-nav-item-gap, 0.5rem)
    );
    --cbui-nav-tree-indent-lg: calc(var(--cbui-nav-item-icon-size, 1rem) * 2);
    --cbui-nav-tree-indent: var(--cbui-nav-tree-indent-md);

    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--cbui-nav-gap, 0.125rem);
}

/* Indent-prop modifier classes — selected one-of-three in NavTree.html.twig.
   The default class `-md` is emitted symmetrically (no special-casing in
   Twig), even though md matches the base value above. */
.cbui-nav-tree-indent-sm { --cbui-nav-tree-indent: var(--cbui-nav-tree-indent-sm); }
.cbui-nav-tree-indent-md { --cbui-nav-tree-indent: var(--cbui-nav-tree-indent-md); }
.cbui-nav-tree-indent-lg { --cbui-nav-tree-indent: var(--cbui-nav-tree-indent-lg); }
.cbui-nav-tree-node {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}
.cbui-nav-tree-children {
    list-style: none;
    margin: 0;
    padding: 0;
    /* Each level indents its children visually — the structural depth cue.
       Composable trees benefit automatically (each level wraps in a fresh
       <ul.cbui-nav-tree-children>); data-driven trees inherit the same. */
    padding-inline-start: var(--cbui-nav-tree-indent);
    display: flex;
    flex-direction: column;
    gap: var(--cbui-nav-gap, 0.125rem);
}
/* Hide the collapsed children — driven by `aria-expanded` on the trigger
   NavItem (single source of truth, no extra class). */
.cbui-nav-tree-node > .cbui-nav-item[aria-expanded="false"] + .cbui-nav-tree-children {
    display: none;
}
/* Leaf nodes (no caret) get a caret-sized pseudo-spacer so their icon column
   sits exactly under the icon column of expandable siblings at the same
   depth. Width === --cbui-nav-item-icon-size matches the caret slot byte for
   byte; the flex `gap` separates it from the icon just like a real caret. */
.cbui-nav-tree-node > .cbui-nav-item:not(.cbui-nav-item-expandable)::before {
    content: "";
    display: inline-block;
    width: var(--cbui-nav-item-icon-size, 1rem);
    flex-shrink: 0;
}
/* Lazy placeholder while children are loading — picks up the same muted
   secondary-text colour DropdownDescription uses, with item-rhythm padding so
   it sits like a regular item in the tree. */
.cbui-nav-tree-loading {
    padding-inline: var(--cbui-nav-item-px, 0.75rem);
    padding-block: var(--cbui-nav-item-py, 0.5rem);
    font-size: var(--cbui-nav-item-font-size, var(--font-size-md, 1rem));
    font-style: italic;
    color: var(--color-text-muted);
    opacity: 0.85;
    user-select: none;
}

/* ── NavTree · drag & drop (reorderable) ─────────────────────────────────────
 * Same visual grammar as UiData:Tree — an insertion LINE for before / after,
 * a wash + inset ring for an "inside" (re-parent) drop. Only the ::after
 * pseudo is used for the line: `::before` on a NavItem is already taken by the
 * leaf caret-spacer rule above, and a second ::before would silently replace
 * that spacer, shifting every leaf label out of its icon column.
 *
 * An INVALID target paints nothing at all: the controller never sets
 * data-cbui-nav-tree-drop on it and sets dropEffect="none", so "no cue" IS the
 * rejection. Nothing to style for that case — a red cue would read as an
 * error the user caused.
 * ────────────────────────────────────────────────────────────────────────── */
.cbui-nav-tree-reorderable {
    --cbui-nav-tree-drop-line: var(--color-primary);
    --cbui-nav-tree-drop-inside-bg: color-mix(in srgb, var(--color-primary) 12%, transparent);
    --cbui-nav-tree-drop-inside-ring: var(--color-primary);
}
.cbui-nav-tree-reorderable .cbui-nav-item[draggable="true"] {
    cursor: grab;
}
.cbui-nav-tree-reorderable[data-cbui-nav-tree-dnd-active] {
    user-select: none;
}
.cbui-nav-tree-node[data-cbui-nav-tree-dragging] > .cbui-nav-item {
    opacity: 0.45;
    cursor: grabbing;
}
/* The row is the positioning context for the insertion line. */
.cbui-nav-tree-reorderable .cbui-nav-tree-node > .cbui-nav-item {
    position: relative;
}
.cbui-nav-tree-node[data-cbui-nav-tree-drop="before"] > .cbui-nav-item::after,
.cbui-nav-tree-node[data-cbui-nav-tree-drop="after"] > .cbui-nav-item::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    height: 2px;
    border-radius: 1px;
    background: var(--cbui-nav-tree-drop-line);
    pointer-events: none;
}
.cbui-nav-tree-node[data-cbui-nav-tree-drop="before"] > .cbui-nav-item::after {
    inset-block-start: -1px;
}
.cbui-nav-tree-node[data-cbui-nav-tree-drop="after"] > .cbui-nav-item::after {
    inset-block-end: -1px;
}
/* Re-parent INSIDE the target — wash + inset ring on the would-be parent. */
.cbui-nav-tree-node[data-cbui-nav-tree-drop="inside"] > .cbui-nav-item {
    background: var(--cbui-nav-tree-drop-inside-bg);
    box-shadow: inset 0 0 0 1px var(--cbui-nav-tree-drop-inside-ring);
}
/* Visually-hidden live-region (the controller inserts it as the tree's next
   sibling). Self-contained sr-only so announcements work outside Tailwind. */
.cbui-nav-tree-live {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
.a11y-high-contrast .cbui-nav-tree-node[data-cbui-nav-tree-drop="before"] > .cbui-nav-item::after,
.a11y-high-contrast .cbui-nav-tree-node[data-cbui-nav-tree-drop="after"] > .cbui-nav-item::after {
    height: 3px;
}
.a11y-high-contrast .cbui-nav-tree-node[data-cbui-nav-tree-drop="inside"] > .cbui-nav-item {
    box-shadow: inset 0 0 0 2px var(--cbui-nav-tree-drop-inside-ring);
}

/* cbui/components/modal */
/* cbui-modal — overlay dialog with Card-inspired anatomy.
 *
 * Built on the native <dialog> element. Browser provides:
 *   - focus-trap (inert outside content while .open)
 *   - ESC to close (cancel event)
 *   - ::backdrop pseudo-element for the dim layer
 *
 * Anatomy + slot bands mirror Card. Same dividers + flush props.
 *
 * Variants:
 *   outlined  — neutral surface, 1px border, soft shadow (default)
 *   elevated  — same surface, heavier lift (Aero plastik exclusive)
 *
 * Modifiers (composable via + in the variant string):
 *   bg-fill, header-fill, footer-fill,
 *   border-accent-{start|end|top|bottom}{|-sharp}
 *
 * Sizes: sm | md | lg | xl | fullscreen
 *
 * Backdrop is ALWAYS dark (rgb(0 0 0 / 0.5)) regardless of scheme — a light
 * backdrop in dark mode reads as broken. The previous color-mix(--color-text)
 * recipe inverted in dark mode.
 */

@layer components {

  /* ─────────────────────────────────────────────────────────────────── */
  /*  BACKDROP — always dark, light blur for "out of focus" effect       */
  /* ─────────────────────────────────────────────────────────────────── */

  /* Backdrop blur is a constant 1px by default — subtle enough not to
     impair readability, strong enough to read as "page is out of focus".
     Flavors can override via --overlay-backdrop-blur if they want a more
     pronounced effect; mode (light/dark) does NOT change the blur. */
  .cbui-modal::backdrop {
    background-color: rgb(0 0 0 / 0.40);
    backdrop-filter: blur(var(--overlay-backdrop-blur, 1px));
    -webkit-backdrop-filter: blur(var(--overlay-backdrop-blur, 1px));
  }
  /* Dark mode reads cleaner with a slightly heavier dim — the page
     surface is already dark, so the backdrop must be darker to create
     the "stage light on the dialog" effect. */
  .dark .cbui-modal::backdrop,
  [data-scheme="dark"] .cbui-modal::backdrop {
    background-color: rgb(0 0 0 / 0.60);
  }
  .a11y-reduced-transparency .cbui-modal::backdrop,
  .a11y-high-contrast        .cbui-modal::backdrop {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: rgb(0 0 0 / 0.75);
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  DIALOG — frame                                                       */
  /* ─────────────────────────────────────────────────────────────────── */

  :where(.cbui-modal) {
    --cbui-shade-soft: rgb(0 0 0 / 0.04);
    --cbui-shade-strong: rgb(0 0 0 / 0.08);
    --cbui-modal-edge: transparent;
  }
  .dark :where(.cbui-modal),
  [data-scheme="dark"] :where(.cbui-modal) {
    --cbui-shade-soft: rgb(255 255 255 / 0.05);
    --cbui-shade-strong: rgb(255 255 255 / 0.10);
    --cbui-modal-edge: rgb(0 0 0 / 0.40);
  }

  .cbui-modal {
    /* Reset the native <dialog>'s margin/padding/border. */
    margin: auto;
    padding: 0;
    border: 1px solid transparent;
    color: inherit;
    /* Flex column so slot bands stack and the body can flex-grow. */
    display: flex;
    flex-direction: column;
    overflow: hidden;
    width: 100%;
    background-color: var(--color-bg-elevated, var(--color-surface));
    border-radius: var(--modal-radius);
    box-shadow: var(--shadow-xl);
    max-height: calc(100dvh - 2rem);
    /* Compact-by-default padding scale. Authors can override via the
       modal-padding token or per-instance. The close-X sits flush in the
       header inline padding (no extra right offset needed). */
    --cbui-modal-pad-x: var(--modal-padding-x, 1.25rem);
    --cbui-modal-pad-y: var(--modal-padding-y, 1.25rem);
    --cbui-modal-header-pad-y: var(--modal-header-padding-y, 0.875rem);
  }
  /* Hidden by default — native <dialog> takes care of display:none when not
     open. The browser sets display:block on .showModal(); flex column wins
     because of specificity. */
  .cbui-modal:not([open]) {
    display: none;
  }

  /* Size scale — width caps. fullscreen escapes the radius+margin. */
  .cbui-modal-size-sm        { max-width: 24rem; }
  .cbui-modal-size-md        { max-width: 32rem; }
  .cbui-modal-size-lg        { max-width: 48rem; }
  .cbui-modal-size-xl        { max-width: 64rem; }
  .cbui-modal-size-fullscreen {
    max-width: none;
    width: 100vw;
    height: 100dvh;
    max-height: none;
    border-radius: 0;
    margin: 0;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Slot bands — header/footer use the tighter header-pad-y so they     */
  /*  read as bands rather than full bodies (mirrors Card).               */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-modal-header {
    padding-inline-start: var(--cbui-modal-pad-x);
    /* Right padding is half the close-X "block" so the × ends up at the
       same distance from top + right edges. With header-pad-y = 0.875rem
       and a 2rem-tall icon-sm button, the visual centre is well-aligned. */
    padding-inline-end: var(--cbui-modal-header-pad-y);
    padding-block: var(--cbui-modal-header-pad-y);
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-shrink: 0;
    justify-content: space-between;
    min-height: calc(2rem + var(--cbui-modal-header-pad-y) * 2);
  }

  /* ── Header height scale ─────────────────────────────────────────────
   * Token-aligned min-height tier (shared --cbui-bar-h-* scale). Padding
   * stays on so the inner content keeps its breathing room. */
  .cbui-modal-header-h-xs { min-height: var(--cbui-bar-h-xs); }
  .cbui-modal-header-h-sm { min-height: var(--cbui-bar-h-sm); }
  .cbui-modal-header-h-md { min-height: var(--cbui-bar-h-md); }
  .cbui-modal-header-h-lg { min-height: var(--cbui-bar-h-lg); }
  .cbui-modal-header-h-xl { min-height: var(--cbui-bar-h-xl); }
  /* Footer buttons are left-aligned, primary action first (G-OCA-22,
     Matthias 2026-09-28: "Ich finde es in Modals links besser" — reference
     is the BlockEditor "Mitglied hinzufuegen" modal). */
  .cbui-modal-footer {
    padding-inline: var(--cbui-modal-pad-x);
    padding-block: var(--cbui-modal-header-pad-y);
    display: flex;
    align-items: center;
    gap: var(--cbui-action-gap, 0.625rem);
    flex-shrink: 0;
    /* CB-2004: actions start at the left edge, the main action first (Matthias: Modal-Knöpfe links). */
    justify-content: flex-start;
  }
  .cbui-modal-title {
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--color-text);
    margin: 0;
    flex: 1 1 auto;
    min-width: 0;
  }
  .cbui-modal-close {
    flex-shrink: 0;
  }
  .cbui-modal-body {
    position: relative;
    padding-inline: var(--cbui-modal-pad-x);
    padding-block: var(--cbui-modal-pad-y);
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
  }

  .cbui-modal-header-flush,
  .cbui-modal-body-flush,
  .cbui-modal-footer-flush {
    padding: 0;
  }

  /* Confirm dialog (ui-component--confirm) — the body message paragraph. */
  .cbui-confirm-message {
    margin: 0;
    color: var(--color-text);
    line-height: 1.5;
  }

  /* Confirm dialog typed confirmation — the label above the input that names what must be typed. */
  .cbui-confirm-typed-prompt {
    display: block;
    margin-block: var(--spacing-md) var(--spacing-xs);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-medium);
    line-height: 1.5;
  }

  .cbui-modal-divider-header .cbui-modal-header {
    border-block-end: 1px solid var(--color-border);
  }
  .cbui-modal-divider-footer .cbui-modal-footer {
    border-block-start: 1px solid var(--color-border);
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Loading overlay — for Turbo-Frame lazy-loads or explicit loading.   */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-modal-loading {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: color-mix(in srgb, var(--color-bg-elevated, var(--color-surface)) 75%, transparent);
    z-index: 1;
  }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Variants                                                             */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-modal-outlined {
    border-color: var(--color-border);
  }
  .cbui-modal-elevated {
    border-color: var(--cbui-modal-edge);
    box-shadow: var(--shadow-2xl, var(--shadow-xl));
  }
  .dark .cbui-modal-elevated,
  [data-scheme="dark"] .cbui-modal-elevated {
    box-shadow:
      inset 0 1px 0 rgb(255 255 255 / 0.06),
      inset 1px 0 0 rgb(255 255 255 / 0.04),
      0 12px 32px rgb(0 0 0 / 0.60);
  }

  /* Tone tinting — borders use a stronger mix (60%) so a tone-coloured
     modal is recognisable at a glance. The earlier 35% was too quiet for
     status-level dialogs ("Are you sure you want to DELETE?"). */
  .cbui-modal-outlined.cbui-modal-tone-primary { border-color: color-mix(in srgb, var(--color-primary) 60%, var(--color-border)); --cbui-modal-accent: var(--color-primary); }
  .cbui-modal-outlined.cbui-modal-tone-accent  { border-color: color-mix(in srgb, var(--color-accent)  60%, var(--color-border)); --cbui-modal-accent: var(--color-accent); }
  .cbui-modal-outlined.cbui-modal-tone-success { border-color: color-mix(in srgb, var(--color-success) 60%, var(--color-border)); --cbui-modal-accent: var(--color-success); }
  .cbui-modal-outlined.cbui-modal-tone-danger  { border-color: color-mix(in srgb, var(--color-danger)  60%, var(--color-border)); --cbui-modal-accent: var(--color-danger); }
  .cbui-modal-outlined.cbui-modal-tone-warning { border-color: color-mix(in srgb, var(--color-warning) 60%, var(--color-border)); --cbui-modal-accent: var(--color-warning); }
  .cbui-modal-outlined.cbui-modal-tone-info    { border-color: color-mix(in srgb, var(--color-info)    60%, var(--color-border)); --cbui-modal-accent: var(--color-info); }
  .cbui-modal-tone-neutral { --cbui-modal-accent: var(--color-border-strong); }
  .cbui-modal-tone-primary { --cbui-modal-accent: var(--color-primary); }
  .cbui-modal-tone-accent  { --cbui-modal-accent: var(--color-accent); }
  .cbui-modal-tone-success { --cbui-modal-accent: var(--color-success); }
  .cbui-modal-tone-danger  { --cbui-modal-accent: var(--color-danger); }
  .cbui-modal-tone-warning { --cbui-modal-accent: var(--color-warning); }
  .cbui-modal-tone-info    { --cbui-modal-accent: var(--color-info); }

  /* When a non-neutral tone is set without an explicit modifier, the
     title picks up the tone colour so the modal is recognisable as
     "this dialog carries meaning". Body text stays neutral for
     readability. */
  .cbui-modal-tone-primary .cbui-modal-title { color: var(--color-primary); }
  .cbui-modal-tone-accent  .cbui-modal-title { color: var(--color-accent); }
  .cbui-modal-tone-success .cbui-modal-title { color: var(--color-success); }
  .cbui-modal-tone-danger  .cbui-modal-title { color: var(--color-danger); }
  .cbui-modal-tone-warning .cbui-modal-title { color: var(--color-warning); }
  .cbui-modal-tone-info    .cbui-modal-title { color: var(--color-info); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Modifiers — same vocabulary as Card                                 */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-modal.cbui-modal-mod-bg-fill.cbui-modal-tone-neutral  { background-image: linear-gradient(var(--color-bg-subtle), var(--color-bg-subtle)); }
  .cbui-modal.cbui-modal-mod-bg-fill.cbui-modal-tone-primary  { background-image: linear-gradient(color-mix(in srgb, var(--color-primary) 8%, transparent), color-mix(in srgb, var(--color-primary) 8%, transparent)); }
  .cbui-modal.cbui-modal-mod-bg-fill.cbui-modal-tone-success  { background-image: linear-gradient(color-mix(in srgb, var(--color-success) 8%, transparent), color-mix(in srgb, var(--color-success) 8%, transparent)); }
  .cbui-modal.cbui-modal-mod-bg-fill.cbui-modal-tone-danger   { background-image: linear-gradient(color-mix(in srgb, var(--color-danger)  8%, transparent), color-mix(in srgb, var(--color-danger)  8%, transparent)); }
  .cbui-modal.cbui-modal-mod-bg-fill.cbui-modal-tone-warning  { background-image: linear-gradient(color-mix(in srgb, var(--color-warning) 8%, transparent), color-mix(in srgb, var(--color-warning) 8%, transparent)); }
  .cbui-modal.cbui-modal-mod-bg-fill.cbui-modal-tone-info     { background-image: linear-gradient(color-mix(in srgb, var(--color-info)    8%, transparent), color-mix(in srgb, var(--color-info)    8%, transparent)); }

  .cbui-modal.cbui-modal-mod-header-fill.cbui-modal-tone-neutral > .cbui-modal-header,
  .cbui-modal.cbui-modal-mod-footer-fill.cbui-modal-tone-neutral > .cbui-modal-footer { background-color: var(--color-bg-muted); }
  .cbui-modal.cbui-modal-mod-header-fill.cbui-modal-tone-primary > .cbui-modal-header,
  .cbui-modal.cbui-modal-mod-footer-fill.cbui-modal-tone-primary > .cbui-modal-footer { background-color: color-mix(in srgb, var(--color-primary) 14%, transparent); }
  .cbui-modal.cbui-modal-mod-header-fill.cbui-modal-tone-success > .cbui-modal-header,
  .cbui-modal.cbui-modal-mod-footer-fill.cbui-modal-tone-success > .cbui-modal-footer { background-color: color-mix(in srgb, var(--color-success) 14%, transparent); }
  .cbui-modal.cbui-modal-mod-header-fill.cbui-modal-tone-warning > .cbui-modal-header,
  .cbui-modal.cbui-modal-mod-footer-fill.cbui-modal-tone-warning > .cbui-modal-footer { background-color: color-mix(in srgb, var(--color-warning) 14%, transparent); }
  .cbui-modal.cbui-modal-mod-header-fill.cbui-modal-tone-danger  > .cbui-modal-header,
  .cbui-modal.cbui-modal-mod-footer-fill.cbui-modal-tone-danger  > .cbui-modal-footer { background-color: color-mix(in srgb, var(--color-danger)  14%, transparent); }
  .cbui-modal.cbui-modal-mod-header-fill.cbui-modal-tone-info    > .cbui-modal-header,
  .cbui-modal.cbui-modal-mod-footer-fill.cbui-modal-tone-info    > .cbui-modal-footer { background-color: color-mix(in srgb, var(--color-info)    14%, transparent); }

  /* Accent-bar modifiers — bumped to 4px width + full-tone colour so the
     bar is recognisable at a glance. The earlier 3px on top of the 1px
     border was too subtle and visually merged with the box border. */
  .cbui-modal.cbui-modal-mod-border-accent-start         { border-inline-start: 4px solid var(--cbui-modal-accent, currentColor); }
  .cbui-modal.cbui-modal-mod-border-accent-end           { border-inline-end:   4px solid var(--cbui-modal-accent, currentColor); }
  .cbui-modal.cbui-modal-mod-border-accent-top           { border-block-start:  4px solid var(--cbui-modal-accent, currentColor); }
  .cbui-modal.cbui-modal-mod-border-accent-bottom        { border-block-end:    4px solid var(--cbui-modal-accent, currentColor); }
  .cbui-modal.cbui-modal-mod-border-accent-start-sharp   { border-inline-start: 4px solid var(--cbui-modal-accent, currentColor); border-start-start-radius: 0; border-end-start-radius: 0; }
  .cbui-modal.cbui-modal-mod-border-accent-end-sharp     { border-inline-end:   4px solid var(--cbui-modal-accent, currentColor); border-start-end-radius: 0;   border-end-end-radius: 0; }
  .cbui-modal.cbui-modal-mod-border-accent-top-sharp     { border-block-start:  4px solid var(--cbui-modal-accent, currentColor); border-start-start-radius: 0; border-start-end-radius: 0; }
  .cbui-modal.cbui-modal-mod-border-accent-bottom-sharp  { border-block-end:    4px solid var(--cbui-modal-accent, currentColor); border-end-start-radius: 0;   border-end-end-radius: 0; }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Reduced motion                                                       */
  /* ─────────────────────────────────────────────────────────────────── */

  .a11y-reduced-motion .cbui-modal,
  .a11y-reduced-motion .cbui-modal::backdrop {
    transition: none !important;
    animation: none !important;
  }
}

/* cbui/components/spinner */
/* cbui-spinner — pure-CSS loading indicator.
 *
 * Inline form: a sized circle with a rotating arc.
 * Overlay form: absolute-inset container that dims its parent and centres
 * the spinner. Inherits the parent's border-radius so it doesn't paint a
 * sharp rectangle inside a rounded card / modal body.
 *
 * Reduced-motion: spin pauses, aria-live announcement remains.
 */

@layer components {

  :where(.cbui-spinner) {
    --cbui-spinner-size: 1.25rem;
    --cbui-spinner-stroke: 2px;
    --cbui-spinner-color: currentColor;
    --cbui-spinner-track: color-mix(in srgb, currentColor 20%, transparent);
  }

  .cbui-spinner {
    display: inline-block;
    width: var(--cbui-spinner-size);
    height: var(--cbui-spinner-size);
    border-radius: 9999px;
    border: var(--cbui-spinner-stroke) solid var(--cbui-spinner-track);
    border-top-color: var(--cbui-spinner-color);
    animation: cbui-spinner-rotate 700ms linear infinite;
    flex-shrink: 0;
    vertical-align: -0.15em;
  }
  .cbui-spinner-label {
    /* Visually hidden but screen-reader accessible. */
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  .cbui-spinner-xs { --cbui-spinner-size: 0.875rem;  --cbui-spinner-stroke: 1.5px; }
  .cbui-spinner-sm { --cbui-spinner-size: 1rem;      --cbui-spinner-stroke: 2px;   }
  .cbui-spinner-md { --cbui-spinner-size: 1.25rem;   --cbui-spinner-stroke: 2px;   }
  .cbui-spinner-lg { --cbui-spinner-size: 2rem;      --cbui-spinner-stroke: 3px;   }

  .cbui-spinner-tone-primary { --cbui-spinner-color: var(--color-primary); --cbui-spinner-track: color-mix(in srgb, var(--color-primary) 20%, transparent); }
  .cbui-spinner-tone-accent  { --cbui-spinner-color: var(--color-accent);  --cbui-spinner-track: color-mix(in srgb, var(--color-accent)  20%, transparent); }
  .cbui-spinner-tone-success { --cbui-spinner-color: var(--color-success); --cbui-spinner-track: color-mix(in srgb, var(--color-success) 20%, transparent); }
  .cbui-spinner-tone-danger  { --cbui-spinner-color: var(--color-danger);  --cbui-spinner-track: color-mix(in srgb, var(--color-danger)  20%, transparent); }
  .cbui-spinner-tone-warning { --cbui-spinner-color: var(--color-warning); --cbui-spinner-track: color-mix(in srgb, var(--color-warning) 20%, transparent); }
  .cbui-spinner-tone-info    { --cbui-spinner-color: var(--color-info);    --cbui-spinner-track: color-mix(in srgb, var(--color-info)    20%, transparent); }
  .cbui-spinner-tone-neutral { --cbui-spinner-color: var(--color-text);    --cbui-spinner-track: var(--color-border-strong); }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Overlay — covers the nearest positioned ancestor                   */
  /*                                                                     */
  /*  Inherit the parent's border-radius so the dim layer hugs the same   */
  /*  shape as the surface it covers. Stronger dim (translucent surface)  */
  /*  so the spinner stands out against the underlying content. The       */
  /*  spinner inside gets a soft glow that switches to a soft drop-shadow */
  /*  in dark mode so it reads on both light and dark surfaces.           */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-spinner-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: color-mix(in srgb, var(--color-bg-base) 80%, transparent);
    /* Hug the parent's curve — falls back to 0 when the parent is square. */
    border-radius: inherit;
    z-index: 1;
  }
  .cbui-spinner-overlay > .cbui-spinner {
    /* Light mode: white halo + subtle dark drop so the spinner pops. */
    filter:
      drop-shadow(0 0 6px rgb(255 255 255 / 0.85))
      drop-shadow(0 1px 2px rgb(0 0 0 / 0.15));
  }
  .dark .cbui-spinner-overlay > .cbui-spinner,
  [data-scheme="dark"] .cbui-spinner-overlay > .cbui-spinner {
    /* Dark mode: subtle dark glow + tighter highlight ring. */
    filter:
      drop-shadow(0 0 6px rgb(0 0 0 / 0.6))
      drop-shadow(0 0 1px rgb(255 255 255 / 0.4));
  }

  @keyframes cbui-spinner-rotate {
    to { transform: rotate(360deg); }
  }

  .a11y-reduced-motion .cbui-spinner { animation: none; opacity: 0.7; }
}

/* cbui/components/choice-group */
/* cbui-choice-group — container for Radio/Checkbox sets and the Rich
 * variants. Carries layout (vertical / horizontal / grid) and tone-cascade
 * tokens so individual items inside the group can read them.
 *
 * Classes:
 *   - cbui-choice-group           base
 *   - cbui-choice-group-radio     plain RadioGroup
 *   - cbui-choice-group-checkbox  plain CheckboxGroup
 *   - cbui-choice-group-rich      Rich variant (both radio + checkbox)
 *   - cbui-choice-group-vertical / -horizontal / -grid
 *   - cbui-choice-group-tone-{primary|success|warning|danger|info}
 *   - cbui-choice-group-size-{sm|md|lg}
 *   - cbui-choice-group-disabled
 *
 * Plus the Rich card item:
 *   - cbui-choice-card            base option-card
 *   - cbui-choice-card-checked
 *   - cbui-choice-card-disabled
 *   - cbui-choice-card-indicator  (radio dot OR checkbox glyph)
 *   - cbui-choice-card-body       (icon + text)
 */

@layer components {
  /* ──────────────────────────────────────────────────────────────────────
   * Group container
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-choice-group {
    --cbui-choice-group-gap: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: var(--cbui-choice-group-gap);
    min-width: 0;
  }

  /* One row rhythm for every vertical radio list (settings drill-ins, forms, pick rows with a trailing icon button):
     a row is never shorter than --cbui-choice-row-min, so a plain radio and a radio with a 40px icon button at its end
     sit on the same pitch (row + gap). The rhythm lives here only; no fragment sets its own row spacing. */
  .cbui-choice-group-radio:not(.cbui-choice-group-horizontal):not(.cbui-choice-group-grid) {
    --cbui-choice-row-min: 2.5rem;
    --cbui-choice-group-gap: 0.25rem;
  }
  .cbui-choice-group-radio:not(.cbui-choice-group-horizontal):not(.cbui-choice-group-grid) > * {
    min-height: var(--cbui-choice-row-min);
    align-items: center;
  }
  /* CB-4111 - an option with a description is two lines: the radio sits on the label line, not between the lines. */
  .cbui-choice-group-radio:not(.cbui-choice-group-horizontal):not(.cbui-choice-group-grid) > .cbui-radio:has(.cbui-radio-description) {
    align-items: flex-start;
  }

  .cbui-choice-group-horizontal {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1.25rem;
  }

  .cbui-choice-group-grid {
    --cbui-choice-group-cols: 2;
    display: grid;
    grid-template-columns: repeat(var(--cbui-choice-group-cols), minmax(0, 1fr));
    gap: 0.75rem;
  }

  /* Disabled cascade — the wrapper's flag dims the whole set; individual
     items can still be enabled if needed by passing disabled={false}. */
  .cbui-choice-group-disabled {
    pointer-events: none;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Rich choice card
   *
   * A label that wraps a (visually-hidden) native input + a visible
   * indicator (radio dot or check glyph) + a body (optional icon + label
   * + description). Whole card is clickable; checked state lifts the
   * surface and tints the border.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-choice-card {
    /* Choice cards always use the same radius regardless of flavor —
       Aero's larger radii (radius-lg = 1.25rem) make these settings
       cards look like floating panels rather than form-control surfaces.
       Hardcoded to Clean's radius-lg (0.75rem) so the visual stays
       calm across every flavor. Same rationale as section-chrome
       (Tabnav, Input, Textarea) in the radius-discipline rule:
       section-level controls stay calm, only freestanding objects
       (Buttons, floating Navbars) inherit the flavor's loud radii. */
    --cbui-choice-card-radius:        0.75rem;
    --cbui-choice-card-pad-x:         0.875rem;
    --cbui-choice-card-pad-y:         0.875rem;
    --cbui-choice-card-tone:          var(--color-primary);
    --cbui-choice-card-tone-ring:     var(--color-focus-ring, var(--color-primary));
    --cbui-choice-card-tone-on:       var(--color-text-on-primary, white);
    --cbui-choice-card-surface:       var(--color-bg-base);
    --cbui-choice-card-surface-hover: var(--color-bg-subtle);
    --cbui-choice-card-surface-checked: color-mix(in srgb, var(--cbui-choice-card-tone) 8%, var(--color-bg-base));

    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: var(--cbui-choice-card-pad-y) var(--cbui-choice-card-pad-x);
    background-color: var(--cbui-choice-card-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--cbui-choice-card-radius);
    cursor: pointer;
    transition:
      background-color 150ms ease,
      border-color 150ms ease,
      box-shadow 150ms ease;
  }

  /* Input overlay (invisible, click-through). See checkbox.css for the
     rationale — sr-only with negative margins caused page-scroll jumps
     on focus. */
  .cbui-choice-card-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    opacity: 0;
    cursor: inherit;
    appearance: none;
    -webkit-appearance: none;
  }

  .cbui-choice-card-indicator {
    flex: 0 0 auto;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.125rem;
    height: 1.125rem;
    background-color: var(--color-bg-base);
    border: 1.5px solid var(--color-border-strong);
    /* Vertical alignment with the first text line (matches checkbox/radio
       box-vs-label baseline). */
    margin-top: 0.125rem;
    transition: background-color 120ms ease, border-color 120ms ease;
  }
  .cbui-choice-card-indicator-radio { border-radius: 50%; }
  .cbui-choice-card-indicator-checkbox { border-radius: 0.25rem; }

  .cbui-choice-card-indicator-dot {
    width: 50%;
    height: 50%;
    border-radius: 50%;
    background-color: var(--cbui-choice-card-tone-on);
    opacity: 0;
    transform: scale(0);
    transition: opacity 80ms ease, transform 80ms ease;
  }
  .cbui-choice-card-indicator-glyph {
    width: 80%;
    height: 80%;
    opacity: 0;
    transform: scale(0.6);
    color: var(--cbui-choice-card-tone-on);
    transition: opacity 80ms ease, transform 80ms ease;
  }

  .cbui-choice-card-body {
    display: flex;
    align-items: flex-start;
    gap: 0.625rem;
    min-width: 0;
    flex: 1 1 auto;
  }
  .cbui-choice-card-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-secondary);
    font-size: 1.25rem;
    /* Match indicator's top alignment. */
    margin-top: 0.0625rem;
  }
  .cbui-choice-card-text {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
    /* Hyphenate at syllables (the page language drives the patterns); a hard break only as the net for a word
       that has no hyphenation point. Not "anywhere": that cuts mid-word. */
    hyphens: auto;
    overflow-wrap: break-word;
  }
  .cbui-choice-card-label {
    color: var(--color-text);
    font-weight: var(--font-weight-medium, 500);
    font-size: var(--font-size-md, 1rem);
    line-height: 1.4;
  }
  .cbui-choice-card-description {
    color: var(--color-text-secondary);
    font-size: var(--font-size-md, 1rem);
    line-height: 1.45;
  }

  /* Hover (only when neither checked nor disabled — defined via :has()
     to mirror the rest of the dynamic state logic). */
  .cbui-choice-card:hover:not(:has(.cbui-choice-card-input:checked)):not(.cbui-choice-card-disabled):not(:has(.cbui-choice-card-input:disabled)) {
    background-color: var(--cbui-choice-card-surface-hover);
    border-color: var(--color-border-strong);
  }

  /* Focus indicator on the card itself when the inner native input has
     focus-visible — same gapped two-layer ring as Input. */
  .cbui-choice-card:has(.cbui-choice-card-input:focus-visible) {
    outline: none;
    box-shadow:
      0 0 0 2px var(--color-bg-base),
      0 0 0 4px var(--cbui-choice-card-tone-ring);
  }

  /* Checked surface + border + indicator paint-in.
   *
   * Driven purely by `:has(:checked)` — the inner native input carries
   * the canonical state (set initially by Twig via the `checked`
   * attribute, then by the browser as the user clicks). No
   * server-rendered helper class, so radio group switches always reflect
   * the actual input state.
   */
  .cbui-choice-card:has(.cbui-choice-card-input:checked) {
    background-color: var(--cbui-choice-card-surface-checked);
    border-color: var(--cbui-choice-card-tone);
  }
  .cbui-choice-card:has(.cbui-choice-card-input:checked) .cbui-choice-card-indicator {
    background-color: var(--cbui-choice-card-tone);
    border-color: var(--cbui-choice-card-tone);
  }
  .cbui-choice-card:has(.cbui-choice-card-input:checked) .cbui-choice-card-indicator-dot,
  .cbui-choice-card:has(.cbui-choice-card-input:checked) .cbui-choice-card-indicator-glyph {
    opacity: 1;
    transform: scale(1);
  }
  .cbui-choice-card:has(.cbui-choice-card-input:checked) .cbui-choice-card-icon {
    color: var(--cbui-choice-card-tone);
  }

  /* Disabled. */
  .cbui-choice-card-disabled,
  .cbui-choice-card:has(.cbui-choice-card-input:disabled) {
    opacity: 0.55;
    cursor: not-allowed;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Button alignment — by default the indicator (radio dot / check glyph)
   * sits at the START of the card; `-button-right` flips it to the END.
   * Useful for settings-row layouts where the control belongs at the
   * row's trailing edge.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-choice-card-button-right {
    flex-direction: row-reverse;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Tones — the group's tone-class sets the card token.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-choice-group-tone-primary .cbui-choice-card { --cbui-choice-card-tone: var(--color-primary); --cbui-choice-card-tone-ring: var(--color-primary); --cbui-choice-card-tone-on: var(--color-text-on-primary, white); }
  .cbui-choice-group-tone-success .cbui-choice-card { --cbui-choice-card-tone: var(--color-success); --cbui-choice-card-tone-ring: var(--color-success); --cbui-choice-card-tone-on: var(--color-text-on-success, white); }
  .cbui-choice-group-tone-warning .cbui-choice-card { --cbui-choice-card-tone: var(--color-warning); --cbui-choice-card-tone-ring: var(--color-warning); --cbui-choice-card-tone-on: var(--color-text-on-warning, white); }
  .cbui-choice-group-tone-danger  .cbui-choice-card { --cbui-choice-card-tone: var(--color-danger);  --cbui-choice-card-tone-ring: var(--color-danger);  --cbui-choice-card-tone-on: var(--color-text-on-danger,  white); }
  .cbui-choice-group-tone-info    .cbui-choice-card { --cbui-choice-card-tone: var(--color-info);    --cbui-choice-card-tone-ring: var(--color-info);    --cbui-choice-card-tone-on: var(--color-text-on-info,    white); }

  /* ──────────────────────────────────────────────────────────────────────
   * Sizes — drives the rich-card padding density.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-choice-group-size-sm .cbui-choice-card {
    --cbui-choice-card-pad-x: 0.625rem;
    --cbui-choice-card-pad-y: 0.625rem;
    gap: 0.5rem;
  }

  .cbui-choice-group-size-lg .cbui-choice-card {
    --cbui-choice-card-pad-x: 1.125rem;
    --cbui-choice-card-pad-y: 1.125rem;
    gap: 1rem;
  }
  .cbui-choice-group-size-lg .cbui-choice-card-label { font-size: var(--font-size-md); }

  /* ──────────────────────────────────────────────────────────────────────
   * Dark mode — lift the unchecked-card surface to match the rest of the
   * elevated-card recipe; the checked surface-mix recipe stays the same
   * but lands brighter against the dark surface (color-mix handles it).
   * ────────────────────────────────────────────────────────────────────── */
  .dark .cbui-choice-card,
  [data-scheme="dark"] .cbui-choice-card {
    --cbui-choice-card-surface:       var(--color-bg-elevated, var(--color-bg-base));
    --cbui-choice-card-surface-hover: var(--color-bg-subtle);
    --cbui-choice-card-surface-checked: color-mix(in srgb, var(--cbui-choice-card-tone) 14%, var(--color-bg-base));
  }

  /* ──────────────────────────────────────────────────────────────────────
   * High contrast (a11y)
   * ────────────────────────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-choice-card {
    border-width: 2px;
    border-color: var(--color-text);
  }
  .a11y-high-contrast .cbui-choice-card:has(.cbui-choice-card-input:checked) {
    border-color: var(--cbui-choice-card-tone);
  }
  .a11y-high-contrast .cbui-choice-card-indicator {
    border-width: 2px;
  }
  .a11y-high-contrast .cbui-choice-card:has(.cbui-choice-card-input:focus-visible) {
    box-shadow:
      0 0 0 2px var(--color-bg-base),
      0 0 0 5px var(--cbui-choice-card-tone-ring);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * CHIPS variant — pill-row radio/checkbox group.
   *
   * Each item renders as a chip. The native input still exists (for form
   * submission, focus and a11y) but is visually hidden; the <label>
   * itself becomes the chip. Tone-aware pressed state — per-item `tone`
   * via items[].tone wins over group tone.
   *
   * Canonical for: filter editor surfaces (pick enum value(s)), inline
   * status pickers, anywhere a chip-row reads better than a dot/check.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-choice-group-chips {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
  }

  /* The label-wrapping element of Radio/Checkbox becomes the chip. */
  .cbui-choice-group-chips .cbui-radio,
  .cbui-choice-group-chips .cbui-checkbox {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.3125rem 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 9999px;
    background-color: transparent;
    color: var(--color-text-secondary);
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    line-height: 1.2;
    cursor: pointer;
    transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
    white-space: nowrap;
  }

  /* Hide the visible custom indicator. */
  .cbui-choice-group-chips .cbui-radio-box,
  .cbui-choice-group-chips .cbui-checkbox-box {
    display: none;
  }

  /* Visually-hide the native <input> but keep it accessible. */
  .cbui-choice-group-chips .cbui-radio-input,
  .cbui-choice-group-chips .cbui-checkbox-input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
    white-space: nowrap;
  }

  /* Match the chip's text colour on the leading icon (carried by items[].icon
     via the label block in RadioGroup/CheckboxGroup). */
  .cbui-choice-group-chips .cbui-choice-group-item-icon {
    display: inline-flex;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
  }

  /* The label/text inside the chip. */
  .cbui-choice-group-chips .cbui-radio-label,
  .cbui-choice-group-chips .cbui-checkbox-label {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-weight: inherit;
    color: inherit;
  }

  /* Optional count after the label (items[].count): muted, tabular, follows the chip colour. */
  .cbui-choice-count {
    font-size: var(--font-size-md, 1rem);
    font-variant-numeric: tabular-nums;
    font-weight: var(--font-weight-medium, 500);
    color: inherit;
    opacity: 0.65;
  }

  /* Hover (only when not checked / disabled). */
  .cbui-choice-group-chips .cbui-radio:hover:not(:has(.cbui-radio-input:checked)):not(:has(.cbui-radio-input:disabled)),
  .cbui-choice-group-chips .cbui-checkbox:hover:not(:has(.cbui-checkbox-input:checked)):not(:has(.cbui-checkbox-input:disabled)) {
    background-color: color-mix(in srgb, currentColor 8%, transparent);
    color: var(--color-text);
  }

  /* Focus ring (when native input has :focus-visible). */
  .cbui-choice-group-chips .cbui-radio:has(.cbui-radio-input:focus-visible),
  .cbui-choice-group-chips .cbui-checkbox:has(.cbui-checkbox-input:focus-visible) {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
  }

  /* Disabled. */
  .cbui-choice-group-chips .cbui-radio:has(.cbui-radio-input:disabled),
  .cbui-choice-group-chips .cbui-checkbox:has(.cbui-checkbox-input:disabled) {
    opacity: 0.5;
    pointer-events: none;
  }

  /* ── Checked — neutral fallback (when no tone class is on item or group). ─ */
  .cbui-choice-group-chips .cbui-radio:has(.cbui-radio-input:checked),
  .cbui-choice-group-chips .cbui-checkbox:has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-text) 12%, transparent);
    border-color: color-mix(in srgb, var(--color-text) 30%, transparent);
    color: var(--color-text);
  }

  /* ── Checked — tone variants. Per-item tone (cbui-radio-tone-X /
        cbui-checkbox-tone-X on the chip itself) wins; group tone is a
        fallback for items without their own tone class. ────────────────── */
  .cbui-choice-group-chips .cbui-radio-tone-primary:has(.cbui-radio-input:checked),
  .cbui-choice-group-chips .cbui-checkbox-tone-primary:has(.cbui-checkbox-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-primary .cbui-radio:not([class*="cbui-radio-tone-"]):has(.cbui-radio-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-primary .cbui-checkbox:not([class*="cbui-checkbox-tone-"]):has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-primary) 16%, transparent);
    border-color: color-mix(in srgb, var(--color-primary) 50%, transparent);
    color: var(--color-primary);
  }
  .cbui-choice-group-chips .cbui-radio-tone-success:has(.cbui-radio-input:checked),
  .cbui-choice-group-chips .cbui-checkbox-tone-success:has(.cbui-checkbox-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-success .cbui-radio:not([class*="cbui-radio-tone-"]):has(.cbui-radio-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-success .cbui-checkbox:not([class*="cbui-checkbox-tone-"]):has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-success) 16%, transparent);
    border-color: color-mix(in srgb, var(--color-success) 50%, transparent);
    color: var(--color-success);
  }
  .cbui-choice-group-chips .cbui-radio-tone-warning:has(.cbui-radio-input:checked),
  .cbui-choice-group-chips .cbui-checkbox-tone-warning:has(.cbui-checkbox-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-warning .cbui-radio:not([class*="cbui-radio-tone-"]):has(.cbui-radio-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-warning .cbui-checkbox:not([class*="cbui-checkbox-tone-"]):has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-warning) 16%, transparent);
    border-color: color-mix(in srgb, var(--color-warning) 50%, transparent);
    color: var(--color-warning);
  }
  .cbui-choice-group-chips .cbui-radio-tone-danger:has(.cbui-radio-input:checked),
  .cbui-choice-group-chips .cbui-checkbox-tone-danger:has(.cbui-checkbox-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-danger .cbui-radio:not([class*="cbui-radio-tone-"]):has(.cbui-radio-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-danger .cbui-checkbox:not([class*="cbui-checkbox-tone-"]):has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-danger) 16%, transparent);
    border-color: color-mix(in srgb, var(--color-danger) 50%, transparent);
    color: var(--color-danger);
  }
  .cbui-choice-group-chips .cbui-radio-tone-info:has(.cbui-radio-input:checked),
  .cbui-choice-group-chips .cbui-checkbox-tone-info:has(.cbui-checkbox-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-info .cbui-radio:not([class*="cbui-radio-tone-"]):has(.cbui-radio-input:checked),
  .cbui-choice-group-chips.cbui-choice-group-tone-info .cbui-checkbox:not([class*="cbui-checkbox-tone-"]):has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-info) 16%, transparent);
    border-color: color-mix(in srgb, var(--color-info) 50%, transparent);
    color: var(--color-info);
  }

  /* Size variants on chips. */
  .cbui-choice-group-chips.cbui-choice-group-size-sm .cbui-radio,
  .cbui-choice-group-chips.cbui-choice-group-size-sm .cbui-checkbox {
    padding: 0.0625rem 0.5625rem;
    font-size: var(--font-size-md, 1rem);
  }
  .cbui-choice-group-chips.cbui-choice-group-size-lg .cbui-radio,
  .cbui-choice-group-chips.cbui-choice-group-size-lg .cbui-checkbox {
    padding: 0.4375rem 1rem;
    font-size: var(--font-size-md, 1rem);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * SEGMENTED variant — connected segmented control.
   *
   * One outer border wraps all segments; each segment is divided by a
   * shared 1px line. Hide the native indicator. The checked segment gets
   * a tinted background. Tone-aware via group tone (per-item tone is
   * rarely meaningful for a segmented control — they describe ONE choice
   * across N segments).
   *
   * Canonical for: UND / ODER toggles, Quick / Erweitert, sort direction,
   * view-toggle, mode switches.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-choice-group-segmented {
    display: inline-flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 0;
    /* Control-height box so a segmented control lines up with inputs, buttons
       and outline switches in a filter/toolbar row (shared --input-height-*
       token, per size). Segments stretch to fill via align-items: stretch. */
    box-sizing: border-box;
    min-height: var(--input-height-md);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 0.5rem);
    overflow: hidden;
    background-color: var(--color-surface, var(--color-bg-elevated));
  }
  .cbui-choice-group-segmented.cbui-choice-group-size-sm { min-height: var(--input-height-sm); }
  .cbui-choice-group-segmented.cbui-choice-group-size-lg { min-height: var(--input-height-lg); }
  .cbui-choice-group-segmented-width-full {
    display: flex;
    width: 100%;
  }
  .cbui-choice-group-segmented-width-full .cbui-radio,
  .cbui-choice-group-segmented-width-full .cbui-checkbox {
    flex: 1 1 0;
    justify-content: center;
  }

  .cbui-choice-group-segmented .cbui-radio,
  .cbui-choice-group-segmented .cbui-checkbox {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    border: 0;
    border-radius: 0;
    background-color: transparent;
    color: var(--color-text-secondary);
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    line-height: 1.2;
    cursor: pointer;
    transition: background-color 150ms ease, color 150ms ease;
    white-space: nowrap;
  }

  /* Shared divider between adjacent segments. Implemented via the trailing
     segment's inline-start border so adjacency selectors stay simple. The
     percentages are deliberately subtle: visible enough to read as a
     divider, not loud enough to compete with the segment labels. */
  .cbui-choice-group-segmented .cbui-radio + .cbui-radio,
  .cbui-choice-group-segmented .cbui-checkbox + .cbui-checkbox {
    border-inline-start: 1px solid color-mix(in srgb, var(--color-text) 12%, transparent);
  }
  /* Active segment paints its inline-start divider slightly stronger so
     the line stays readable when a neighbour carries a tinted bg. */
  .cbui-choice-group-segmented .cbui-radio:has(.cbui-radio-input:checked) + .cbui-radio,
  .cbui-choice-group-segmented .cbui-radio + .cbui-radio:has(.cbui-radio-input:checked),
  .cbui-choice-group-segmented .cbui-checkbox:has(.cbui-checkbox-input:checked) + .cbui-checkbox,
  .cbui-choice-group-segmented .cbui-checkbox + .cbui-checkbox:has(.cbui-checkbox-input:checked) {
    border-inline-start-color: color-mix(in srgb, var(--color-text) 20%, transparent);
  }

  .cbui-choice-group-segmented .cbui-radio-box,
  .cbui-choice-group-segmented .cbui-checkbox-box {
    display: none;
  }
  .cbui-choice-group-segmented .cbui-radio-input,
  .cbui-choice-group-segmented .cbui-checkbox-input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
    white-space: nowrap;
  }
  .cbui-choice-group-segmented .cbui-radio-label,
  .cbui-choice-group-segmented .cbui-checkbox-label {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-weight: inherit;
    color: inherit;
  }
  .cbui-choice-group-segmented .cbui-choice-group-item-icon {
    display: inline-flex;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
  }

  .cbui-choice-group-segmented .cbui-radio:hover:not(:has(.cbui-radio-input:checked)):not(:has(.cbui-radio-input:disabled)),
  .cbui-choice-group-segmented .cbui-checkbox:hover:not(:has(.cbui-checkbox-input:checked)):not(:has(.cbui-checkbox-input:disabled)) {
    background-color: color-mix(in srgb, currentColor 6%, transparent);
    color: var(--color-text);
  }

  .cbui-choice-group-segmented .cbui-radio:has(.cbui-radio-input:focus-visible),
  .cbui-choice-group-segmented .cbui-checkbox:has(.cbui-checkbox-input:focus-visible) {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -2px;
    z-index: 1;
  }

  .cbui-choice-group-segmented .cbui-radio:has(.cbui-radio-input:disabled),
  .cbui-choice-group-segmented .cbui-checkbox:has(.cbui-checkbox-input:disabled) {
    opacity: 0.5;
    pointer-events: none;
  }

  /* Checked — neutral fallback. */
  .cbui-choice-group-segmented .cbui-radio:has(.cbui-radio-input:checked),
  .cbui-choice-group-segmented .cbui-checkbox:has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-text) 12%, transparent);
    color: var(--color-text);
    font-weight: var(--font-weight-semibold, 600);
  }

  /* Tone-aware checked. */
  .cbui-choice-group-segmented.cbui-choice-group-tone-primary .cbui-radio:has(.cbui-radio-input:checked),
  .cbui-choice-group-segmented.cbui-choice-group-tone-primary .cbui-checkbox:has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-primary) 14%, transparent);
    color: var(--color-primary);
  }
  .cbui-choice-group-segmented.cbui-choice-group-tone-success .cbui-radio:has(.cbui-radio-input:checked),
  .cbui-choice-group-segmented.cbui-choice-group-tone-success .cbui-checkbox:has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-success) 14%, transparent);
    color: var(--color-success);
  }
  .cbui-choice-group-segmented.cbui-choice-group-tone-warning .cbui-radio:has(.cbui-radio-input:checked),
  .cbui-choice-group-segmented.cbui-choice-group-tone-warning .cbui-checkbox:has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-warning) 14%, transparent);
    color: var(--color-warning);
  }
  .cbui-choice-group-segmented.cbui-choice-group-tone-danger .cbui-radio:has(.cbui-radio-input:checked),
  .cbui-choice-group-segmented.cbui-choice-group-tone-danger .cbui-checkbox:has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-danger) 14%, transparent);
    color: var(--color-danger);
  }
  .cbui-choice-group-segmented.cbui-choice-group-tone-info .cbui-radio:has(.cbui-radio-input:checked),
  .cbui-choice-group-segmented.cbui-choice-group-tone-info .cbui-checkbox:has(.cbui-checkbox-input:checked) {
    background-color: color-mix(in srgb, var(--color-info) 14%, transparent);
    color: var(--color-info);
  }

  /* Size variants on segmented. */
  .cbui-choice-group-segmented.cbui-choice-group-size-sm .cbui-radio,
  .cbui-choice-group-segmented.cbui-choice-group-size-sm .cbui-checkbox {
    padding: 0.125rem 0.5rem;
    font-size: var(--font-size-md, 1rem);
  }
  .cbui-choice-group-segmented.cbui-choice-group-size-lg .cbui-radio,
  .cbui-choice-group-segmented.cbui-choice-group-size-lg .cbui-checkbox {
    padding: 0.5rem 1rem;
    font-size: var(--font-size-md, 1rem);
  }

  /* A11y — reduced motion. */
  .a11y-reduced-motion .cbui-choice-group-chips .cbui-radio,
  .a11y-reduced-motion .cbui-choice-group-chips .cbui-checkbox,
  .a11y-reduced-motion .cbui-choice-group-segmented .cbui-radio,
  .a11y-reduced-motion .cbui-choice-group-segmented .cbui-checkbox {
    transition: none;
  }

  /* High-contrast (a11y). */
  .a11y-high-contrast .cbui-choice-group-chips .cbui-radio,
  .a11y-high-contrast .cbui-choice-group-chips .cbui-checkbox {
    border-width: 2px;
  }
  .a11y-high-contrast .cbui-choice-group-segmented {
    border-width: 2px;
  }
  .a11y-high-contrast .cbui-choice-group-segmented .cbui-radio + .cbui-radio,
  .a11y-high-contrast .cbui-choice-group-segmented .cbui-checkbox + .cbui-checkbox {
    border-inline-start-width: 2px;
  }
}

/* cbui/components/radio */
/* cbui-radio — native radio with CBUI chrome.
 *
 * Sibling of cbui-checkbox: same wrapper/sr-only/custom-box pattern,
 * same Variant×Tone+Modifier grammar, parallel state vocabulary.
 * Geometry differs in two ways only:
 *   1. The box is a perfect circle (radius = 50%).
 *   2. The checked glyph is a centered dot, not a checkmark SVG.
 *
 * Classes:
 *   - cbui-radio             base reset
 *   - cbui-radio-{variant}   solid | outline
 *   - cbui-radio-size-{sm,md,lg}
 *   - cbui-radio-tone-{primary|success|warning|danger|info}
 *   - cbui-radio-mod-block   label fills the row width
 */

@layer components {
  /* ──────────────────────────────────────────────────────────────────────
   * Wrapper
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio {
    --cbui-radio-box: 1.125rem;
    --cbui-radio-fs:  var(--font-size-md, 1rem);

    --cbui-radio-tone:        var(--color-primary);
    --cbui-radio-tone-strong: var(--color-primary);
    --cbui-radio-tone-focus:  var(--color-primary);
    --cbui-radio-tone-ring:   var(--color-focus-ring, var(--color-primary));
    --cbui-radio-tone-on:     var(--color-text-on-primary, white);

    --cbui-radio-ring-gap:        2px;
    --cbui-radio-ring-width:      2px;
    --cbui-radio-ring-gap-color:  var(--color-bg-base);

    /* See checkbox.css for the rationale behind position:relative +
       absolute-inset input overlay — fixes browser scroll-into-view
       jumping to the off-screen sr-only input. */
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-family: inherit;
    font-size: var(--cbui-radio-fs);
    line-height: 1.4;
    color: var(--color-text);
    cursor: pointer;
  }

  .cbui-radio-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    opacity: 0;
    cursor: inherit;
    appearance: none;
    -webkit-appearance: none;
  }

  .cbui-radio-box {
    flex: 0 0 auto;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--cbui-radio-box);
    height: var(--cbui-radio-box);
    border-radius: 50%;
    background-color: var(--color-bg-base);
    border: 1.5px solid var(--color-border-strong);
    transition:
      background-color 120ms ease,
      border-color 120ms ease,
      box-shadow 120ms ease;
  }

  /* Inner dot — visible when checked. */
  .cbui-radio-dot {
    width: 50%;
    height: 50%;
    border-radius: 50%;
    background-color: var(--cbui-radio-tone-on);
    transform: scale(0);
    opacity: 0;
    transition: transform 80ms ease, opacity 80ms ease, background-color 120ms ease;
  }

  .cbui-radio-label {
    flex: 1 1 auto;
    color: var(--color-text);
  }

  /* CB-4111 - an optional description under the label, shaped like the Switch's: a column, secondary colour, never below
     the normal size. The box sits on the label line, not centred between the two lines. */
  .cbui-radio-text {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
  }
  .cbui-radio-description {
    color: var(--color-text-secondary);
    font-size: var(--font-size-md, 1rem);
  }
  .cbui-radio:has(.cbui-radio-description) {
    align-items: flex-start;
  }
  .cbui-radio:has(.cbui-radio-description) .cbui-radio-box {
    margin-top: calc((var(--cbui-radio-fs) * 1.4 - var(--cbui-radio-box)) / 2);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Sizes
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio-size-sm { --cbui-radio-box: 0.9375rem; --cbui-radio-fs: var(--font-size-md, 1rem); }
  .cbui-radio-size-md { --cbui-radio-box: 1.125rem;  --cbui-radio-fs: var(--font-size-md, 1rem); }
  .cbui-radio-size-lg { --cbui-radio-box: 1.375rem;  --cbui-radio-fs: var(--font-size-md, 1rem); }

  /* ──────────────────────────────────────────────────────────────────────
   * Hover
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio:hover .cbui-radio-input:not(:disabled) ~ .cbui-radio-box {
    border-color: var(--cbui-radio-tone);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Focus (gapped two-layer ring)
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio-input:focus-visible ~ .cbui-radio-box,
  .cbui-radio[data-cbui-demo-state="focused"] .cbui-radio-box {
    outline: none;
    box-shadow:
      0 0 0 var(--cbui-radio-ring-gap) var(--cbui-radio-ring-gap-color),
      0 0 0 calc(var(--cbui-radio-ring-gap) + var(--cbui-radio-ring-width)) var(--cbui-radio-tone-ring);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: solid — checked: filled with tone, dot in contrast-text.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio-solid .cbui-radio-input:checked ~ .cbui-radio-box,
  .cbui-radio-solid[data-cbui-demo-state="checked"] .cbui-radio-box {
    background-color: var(--cbui-radio-tone);
    border-color: var(--cbui-radio-tone);
  }
  .cbui-radio-solid .cbui-radio-input:checked ~ .cbui-radio-box .cbui-radio-dot,
  .cbui-radio-solid[data-cbui-demo-state="checked"] .cbui-radio-box .cbui-radio-dot {
    background-color: var(--cbui-radio-tone-on);
    transform: scale(1);
    opacity: 1;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: outline — checked: tone border + tone dot, no fill.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio-outline .cbui-radio-input:checked ~ .cbui-radio-box,
  .cbui-radio-outline[data-cbui-demo-state="checked"] .cbui-radio-box {
    background-color: transparent;
    border-color: var(--cbui-radio-tone);
  }
  .cbui-radio-outline .cbui-radio-input:checked ~ .cbui-radio-box .cbui-radio-dot,
  .cbui-radio-outline[data-cbui-demo-state="checked"] .cbui-radio-box .cbui-radio-dot {
    background-color: var(--cbui-radio-tone);
    transform: scale(1);
    opacity: 1;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Disabled
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio-input:disabled ~ .cbui-radio-box,
  .cbui-radio-disabled .cbui-radio-box,
  .cbui-radio-input:disabled ~ .cbui-radio-label,
  .cbui-radio-input:disabled ~ .cbui-radio-text,
  .cbui-radio-disabled .cbui-radio-label,
  .cbui-radio-disabled .cbui-radio-text {
    opacity: 0.55;
  }
  .cbui-radio:has(.cbui-radio-input:disabled),
  .cbui-radio-disabled { cursor: not-allowed; }

  /* ──────────────────────────────────────────────────────────────────────
   * Modifier: block
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio-mod-block { display: flex; width: 100%; }
  .cbui-radio-mod-block .cbui-radio-label { flex: 1 1 auto; }

  /* ──────────────────────────────────────────────────────────────────────
   * Tones
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-radio-tone-primary { --cbui-radio-tone: var(--color-primary); --cbui-radio-tone-strong: var(--color-primary); --cbui-radio-tone-focus: var(--color-primary); --cbui-radio-tone-ring: var(--color-primary); --cbui-radio-tone-on: var(--color-text-on-primary, white); }
  .cbui-radio-tone-success { --cbui-radio-tone: var(--color-success); --cbui-radio-tone-strong: var(--color-success); --cbui-radio-tone-focus: var(--color-success); --cbui-radio-tone-ring: var(--color-success); --cbui-radio-tone-on: var(--color-text-on-success, white); }
  .cbui-radio-tone-warning { --cbui-radio-tone: var(--color-warning); --cbui-radio-tone-strong: var(--color-warning); --cbui-radio-tone-focus: var(--color-warning); --cbui-radio-tone-ring: var(--color-warning); --cbui-radio-tone-on: var(--color-text-on-warning, white); }
  .cbui-radio-tone-danger  { --cbui-radio-tone: var(--color-danger);  --cbui-radio-tone-strong: var(--color-danger);  --cbui-radio-tone-focus: var(--color-danger);  --cbui-radio-tone-ring: var(--color-danger);  --cbui-radio-tone-on: var(--color-text-on-danger,  white); }
  .cbui-radio-tone-info    { --cbui-radio-tone: var(--color-info);    --cbui-radio-tone-strong: var(--color-info);    --cbui-radio-tone-focus: var(--color-info);    --cbui-radio-tone-ring: var(--color-info);    --cbui-radio-tone-on: var(--color-text-on-info,    white); }

  /* ──────────────────────────────────────────────────────────────────────
   * High contrast (a11y)
   * ────────────────────────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-radio .cbui-radio-box {
    border-width: 2px;
    border-color: var(--color-text);
    --cbui-radio-ring-width: 3px;
  }
}

/* cbui/components/select */
/* cbui-select — native <select> with CBUI chrome.
 *
 * The native control supplies semantics (keyboard, accessibility, mobile
 * pickers) — we only paint the surrounding chrome. Borrows the Input
 * token cascade (input-padding-*, input-height-*, input-font-size-*,
 * input-radius) so a Select sits flush next to an Input at the same size.
 *
 *   - cbui-select             base reset (sits on the native <select>)
 *   - cbui-select-wrap        outer chrome carrier (custom caret lives here)
 *   - cbui-select-{variant}   default | filled | flush
 *   - cbui-select-size-{sm,md,lg}
 *   - cbui-select-tone-{primary|success|warning|danger|info}
 *
 * Chevron geometry (symmetric):
 *   left-edge --[pad-x]-- text --[pad-x]-- chevron --[pad-x]-- right-edge
 * The chevron lives in an absolutely-positioned ::after on the wrap; the
 * inner <select> reserves padding-inline-end = 2*pad-x + chevron-icon so
 * the value text never overlaps the chevron.
 */

@layer components {
  /* ──────────────────────────────────────────────────────────────────────
   * Wrap — chrome carrier
   *
   * Carries the size-aware pad-x token so both the wrapper's ::after
   * (chevron position) and the inner <select> (text padding) read from a
   * single source. With width: auto the wrap shrinks to the inner
   * select's intrinsic size, including the reserved chevron padding, so
   * the chevron always sits inside the visible select rectangle.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select-wrap {
    --cbui-select-caret-icon-w: 0.875rem;
    --cbui-select-pad-x:        var(--input-padding-x-md);
    --cbui-select-caret-color:  var(--color-text-secondary);

    position: relative;
    display: inline-flex;
    width: 100%;
    min-width: 0;
  }

  /* Custom caret — painted via `mask-image`, so the colour comes from
     `background-color` and follows the theme automatically. SVG
     `currentColor` is not honoured when the SVG loads as a CSS
     background-image; mask-image works in every modern engine. */
  .cbui-select-wrap::after {
    content: '';
    position: absolute;
    pointer-events: none;
    inset-block: 0;
    /* Chevron sits pad-x from the right edge — same as text from left. */
    inset-inline-end: var(--cbui-select-pad-x);
    width: var(--cbui-select-caret-icon-w);
    background-color: var(--cbui-select-caret-color);
    -webkit-mask: no-repeat center / 0.875rem 0.875rem url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><polyline points='4 6 8 10 12 6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    mask: no-repeat center / 0.875rem 0.875rem url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><polyline points='4 6 8 10 12 6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  }

  /* Multiple-mode renders as a list — no caret. */
  .cbui-select-wrap:has(.cbui-select-multiple)::after { display: none; }

  /* ──────────────────────────────────────────────────────────────────────
   * Base select reset + chrome carrier
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select {
    --cbui-select-pad-x:    var(--input-padding-x-md);
    --cbui-select-pad-y:    var(--input-padding-y-md);
    --cbui-select-height:   var(--input-height-md);
    --cbui-select-fs:       var(--input-font-size-md);

    --cbui-select-tone:        var(--color-border);
    --cbui-select-tone-strong: var(--color-border-strong);
    --cbui-select-tone-focus:  var(--color-primary);
    --cbui-select-tone-ring:   var(--color-focus-ring, var(--color-primary));
    --cbui-select-glow-alpha:  35%;

    --cbui-select-ring-gap:        2px;
    --cbui-select-ring-width:      2px;
    --cbui-select-ring-gap-color:  var(--color-bg-base);

    --cbui-select-flush-bg:        var(--color-bg-subtle);

    display: block;
    width: 100%;
    min-width: 0;
    height: var(--cbui-select-height);
    padding-inline-start: var(--cbui-select-pad-x);
    /* Right side reserves chevron-icon + 2× pad-x so the gap from text
       to chevron AND from chevron to right-edge both equal pad-x —
       symmetric with the left side. */
    padding-inline-end: calc(var(--cbui-select-caret-icon-w, 0.875rem) + var(--cbui-select-pad-x) * 2);
    padding-block: var(--cbui-select-pad-y);
    font-family: inherit;
    font-size: var(--cbui-select-fs);
    line-height: 1.5;
    color: var(--color-text);
    background-color: transparent;
    border: 1px solid transparent;
    border-radius: var(--input-radius);
    appearance: none;
    -webkit-appearance: none;
    background-image: none;
    box-shadow: none;
    /* Truncate the selected option's label with an ellipsis when the
       container is narrower than the value. */
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
    transition:
      border-color 150ms ease,
      background-color 150ms ease,
      box-shadow 150ms ease;
  }

  .cbui-select:disabled,
  .cbui-select-disabled {
    opacity: 0.55;
    cursor: not-allowed;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Multiple mode
   *
   * Native <select multiple> renders as a vertical list. Browsers vary
   * widely in option styling (Firefox most permissive, Chromium honours
   * padding + checked bg but ignores margin in some cases). The styling
   * mirrors the Dropdown menu's "inset items" treatment so a Select-
   * multiple reads as a sibling of a Dropdown's menu list:
   *
   *   - Container carries a small inset (0.25rem) on every side so the
   *     items don't kiss the select's border.
   *   - Each option's padding-inline is calc(pad-x − 0.25rem) so the
   *     visual distance from the select edge to the text matches pad-x
   *     (same as in a single-select).
   *   - Items use the flavor's radius-sm (same token Dropdown items use
   *     in inset mode), so a Sharp flavor gets squared item corners and
   *     a Soft flavor gets generously rounded ones.
   *   - 1px gap between consecutive items so a run of selected options
   *     reads as discrete rows rather than one tall stripe.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select-multiple {
    --cbui-select-multiple-inset: 0.25rem;
    --cbui-select-multiple-item-radius: var(--radius-sm, 0.375rem);

    height: auto;
    min-height: var(--cbui-select-height);
    padding: var(--cbui-select-multiple-inset);
    overflow: auto;
    text-overflow: clip;
    white-space: normal;
  }
  .cbui-select-multiple option {
    padding-inline: calc(var(--cbui-select-pad-x) - var(--cbui-select-multiple-inset));
    padding-block: 0.375rem;
    margin: 0 0 1px 0;
    border-radius: var(--cbui-select-multiple-item-radius);
  }
  .cbui-select-multiple option:last-child {
    margin-bottom: 0;
  }
  .cbui-select-multiple option:checked {
    background-color: color-mix(in srgb, var(--color-primary) 14%, var(--color-bg-base));
    color: var(--color-text);
  }
  .cbui-select-multiple option:hover {
    background-color: var(--color-bg-subtle);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Width control
   *
   * Default: `cbui-select-wrap-w-full` → fills the container (100%).
   * Opt-out: `cbui-select-wrap-w-auto` → intrinsic to content.
   * Custom: callers can pass `style="width: 14rem"` directly.
   *
   * With width: auto the wrap is set to display: inline-block so the
   * intrinsic width includes the inner <select>'s padding (which holds
   * the chevron-reserve). Without that, inline-flex's auto sizing would
   * not include the absolute-positioned chevron's footprint and the
   * chevron could land outside the visible select rectangle.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select-wrap-w-full { width: 100%; }
  /* `max-content` is critical: a plain `display: inline-block` would be
     blockified to `display: block` when the wrap sits inside a Grid (most
     form-row layouts), which then fills the grid cell — the chevron's
     `inset-inline-end: pad-x` then lands inside the empty cell area
     rather than at the visible select's edge. `max-content` keeps the
     wrap sized to its intrinsic content (the inner select) regardless
     of layout context. */
  .cbui-select-wrap-w-auto {
    width: max-content;
    max-width: 100%;
  }
  .cbui-select-wrap-w-auto .cbui-select { width: auto; }

  .dark .cbui-select,
  [data-scheme="dark"] .cbui-select {
    --cbui-select-glow-alpha: 50%;
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Sizes — drive pad-x on BOTH wrap and select so the chevron position
   * (on wrap) and the text padding (on select) stay in sync.
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select-size-sm,
  .cbui-select-wrap-size-sm {
    --cbui-select-pad-x:  var(--input-padding-x-sm);
  }
  .cbui-select-size-sm {
    --cbui-select-pad-y:  var(--input-padding-y-sm);
    --cbui-select-height: var(--input-height-sm);
    --cbui-select-fs:     var(--input-font-size-sm);
  }
  .cbui-select-size-md,
  .cbui-select-wrap-size-md {
    --cbui-select-pad-x:  var(--input-padding-x-md);
  }
  .cbui-select-size-md {
    --cbui-select-pad-y:  var(--input-padding-y-md);
    --cbui-select-height: var(--input-height-md);
    --cbui-select-fs:     var(--input-font-size-md);
  }
  .cbui-select-size-lg,
  .cbui-select-wrap-size-lg {
    --cbui-select-pad-x:  var(--input-padding-x-lg);
  }
  .cbui-select-size-lg {
    --cbui-select-pad-y:  var(--input-padding-y-lg);
    --cbui-select-height: var(--input-height-lg);
    --cbui-select-fs:     var(--input-font-size-lg);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: default — surface + border (matches Input default).
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select-default {
    background-color: var(--color-bg-base);
    border-color: var(--cbui-select-tone);
  }
  .cbui-select-default:hover:not(:disabled):not(:focus) {
    border-color: var(--cbui-select-tone-strong);
  }
  .cbui-select-default:focus,
  .cbui-select-default:focus-visible,
  .cbui-select-default[data-cbui-demo-state="focused"] {
    outline: none;
    border-color: var(--cbui-select-tone-focus);
    box-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--cbui-select-tone-ring) var(--cbui-select-glow-alpha), transparent);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: filled
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select-filled {
    background-color: var(--color-bg-subtle);
    border-color: transparent;
    border-bottom: 2px solid transparent;
    padding-bottom: calc(var(--cbui-select-pad-y) - 1px);
  }
  .cbui-select-filled:hover:not(:disabled):not(:focus) {
    background-color: var(--color-bg-muted);
  }
  .cbui-select-filled:focus,
  .cbui-select-filled:focus-visible,
  .cbui-select-filled[data-cbui-demo-state="focused"] {
    outline: none;
    background-color: var(--color-bg-subtle);
    border-bottom-color: var(--cbui-select-tone-focus);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Variant: flush — themed surface + bottom rule. Unlike Input flush,
   * Select keeps padding-inline-start = pad-x: the chevron must sit on
   * the trailing edge with its own pad-x breathing room, and the value
   * text should align on the left edge with the same pad-x so the
   * leading + trailing rhythm matches the other variants. The "flush"
   * here refers to the chrome (no border, only a bottom rule).
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select-flush {
    background-color: var(--cbui-select-flush-bg);
    border-color: transparent;
    border-bottom: 1px solid var(--cbui-select-tone);
    border-radius: 0;
  }
  .cbui-select-flush:hover:not(:disabled):not(:focus) {
    border-bottom-color: var(--cbui-select-tone-strong);
  }
  .cbui-select-flush:focus,
  .cbui-select-flush:focus-visible,
  .cbui-select-flush[data-cbui-demo-state="focused"] {
    outline: none;
    border-bottom-width: 2px;
    border-bottom-color: var(--cbui-select-tone-focus);
    padding-bottom: calc(var(--cbui-select-pad-y) - 1px);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * Tones
   * ────────────────────────────────────────────────────────────────────── */
  .cbui-select-tone-primary {
    --cbui-select-tone:        color-mix(in srgb, var(--color-primary) 55%, var(--color-border));
    --cbui-select-tone-strong: var(--color-primary);
    --cbui-select-tone-focus:  var(--color-primary);
    --cbui-select-tone-ring:   var(--color-primary);
  }
  .cbui-select-tone-success {
    --cbui-select-tone:        color-mix(in srgb, var(--color-success) 55%, var(--color-border));
    --cbui-select-tone-strong: var(--color-success);
    --cbui-select-tone-focus:  var(--color-success);
    --cbui-select-tone-ring:   var(--color-success);
  }
  .cbui-select-tone-warning {
    --cbui-select-tone:        color-mix(in srgb, var(--color-warning) 60%, var(--color-border));
    --cbui-select-tone-strong: var(--color-warning);
    --cbui-select-tone-focus:  var(--color-warning);
    --cbui-select-tone-ring:   var(--color-warning);
  }
  .cbui-select-tone-danger {
    --cbui-select-tone:        var(--color-danger);
    --cbui-select-tone-strong: var(--color-danger);
    --cbui-select-tone-focus:  var(--color-danger);
    --cbui-select-tone-ring:   var(--color-danger);
  }
  .cbui-select-tone-info {
    --cbui-select-tone:        color-mix(in srgb, var(--color-info) 55%, var(--color-border));
    --cbui-select-tone-strong: var(--color-info);
    --cbui-select-tone-focus:  var(--color-info);
    --cbui-select-tone-ring:   var(--color-info);
  }

  /* ──────────────────────────────────────────────────────────────────────
   * High contrast (a11y)
   * ────────────────────────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-select {
    border-width: 2px;
    --cbui-select-tone:        var(--color-text);
    --cbui-select-tone-strong: var(--color-text);
    --cbui-select-tone-ring:   var(--color-text);
    --cbui-select-ring-width:  3px;
  }
  .a11y-high-contrast .cbui-select:focus,
  .a11y-high-contrast .cbui-select:focus-visible,
  .a11y-high-contrast .cbui-select[data-cbui-demo-state="focused"] {
    outline: none;
    box-shadow:
      0 0 0 var(--cbui-select-ring-gap) var(--cbui-select-ring-gap-color),
      0 0 0 calc(var(--cbui-select-ring-gap) + var(--cbui-select-ring-width)) var(--cbui-select-tone-ring);
  }
}

/* cbui/components/textarea */
/* cbui-textarea — multi-line text input.
 *
 * Mirrors `cbui-input` exactly in vocabulary, grammar and tokens. Per-
 * variant focus by default; the `+focus-ring` modifier and HC mode
 * upgrade to the gapped ring.
 */

@layer components {
  .cbui-textarea {
    --cbui-input-pad-x:    var(--input-padding-x-md);
    --cbui-input-pad-y:    var(--input-padding-y-md);
    --cbui-input-fs:       var(--input-font-size-md);

    --cbui-input-tone:        var(--color-border);
    --cbui-input-tone-strong: var(--color-border-strong);
    --cbui-input-tone-focus:  var(--color-primary);
    --cbui-input-tone-ring:   var(--color-focus-ring, var(--color-primary));

    --cbui-input-glow-alpha: 35%;

    --cbui-input-ring-gap:        2px;
    --cbui-input-ring-width:      2px;
    --cbui-input-ring-gap-color:  var(--color-bg-base);

    --cbui-input-flush-bg:    var(--color-bg-subtle);

    display: block;
    width: 100%;
    min-width: 0;
    padding-inline: var(--cbui-input-pad-x);
    padding-block: var(--cbui-input-pad-y);
    font-family: inherit;
    font-size: var(--cbui-input-fs);
    line-height: 1.5;
    color: var(--color-text);
    background-color: transparent;
    border: 1px solid transparent;
    border-radius: var(--input-radius);
    appearance: none;
    -webkit-appearance: none;
    box-shadow: none;
    transition:
      border-color 150ms ease,
      background-color 150ms ease,
      box-shadow 150ms ease;
  }

  .cbui-textarea::placeholder {
    color: var(--color-text-tertiary);
    opacity: 1;
  }

  .cbui-textarea:disabled,
  .cbui-textarea.cbui-textarea-disabled {
    opacity: 0.55;
    cursor: not-allowed;
  }

  .cbui-textarea:read-only,
  .cbui-textarea.cbui-textarea-readonly {
    cursor: default;
  }

  .dark .cbui-textarea,
  [data-scheme="dark"] .cbui-textarea {
    --cbui-input-glow-alpha: 50%;
  }

  /* Resize axis */
  .cbui-textarea-resize-none       { resize: none; }
  .cbui-textarea-resize-vertical   { resize: vertical; }
  .cbui-textarea-resize-horizontal { resize: horizontal; }
  .cbui-textarea-resize-both       { resize: both; }

  .cbui-textarea-autoresize {
    resize: none;
    overflow: hidden;
  }

  /* Sizes */
  .cbui-textarea-size-sm {
    --cbui-input-pad-x: var(--input-padding-x-sm);
    --cbui-input-pad-y: var(--input-padding-y-sm);
    --cbui-input-fs:    var(--input-font-size-sm);
  }
  .cbui-textarea-size-md {
    --cbui-input-pad-x: var(--input-padding-x-md);
    --cbui-input-pad-y: var(--input-padding-y-md);
    --cbui-input-fs:    var(--input-font-size-md);
  }
  .cbui-textarea-size-lg {
    --cbui-input-pad-x: var(--input-padding-x-lg);
    --cbui-input-pad-y: var(--input-padding-y-lg);
    --cbui-input-fs:    var(--input-font-size-lg);
  }

  /* Variant: default — border + soft glow on focus */
  .cbui-textarea-default {
    background-color: var(--color-bg-base);
    border-color: var(--cbui-input-tone);
  }
  .cbui-textarea-default:hover:not(:disabled):not(:focus) {
    border-color: var(--cbui-input-tone-strong);
  }
  .cbui-textarea-default:focus,
  .cbui-textarea-default:focus-visible,
  .cbui-textarea-default[data-cbui-demo-state="focused"] {
    outline: none;
    border-color: var(--cbui-input-tone-focus);
    box-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--cbui-input-tone-ring) var(--cbui-input-glow-alpha), transparent);
  }
  .cbui-textarea-default:disabled,
  .cbui-textarea-default.cbui-textarea-disabled {
    background-color: var(--color-bg-subtle);
  }

  /* Variant: filled */
  .cbui-textarea-filled {
    background-color: var(--color-bg-subtle);
    border-color: transparent;
    border-bottom: 2px solid transparent;
    padding-bottom: calc(var(--cbui-input-pad-y) - 1px);
  }
  .cbui-textarea-filled:hover:not(:disabled):not(:focus) {
    background-color: var(--color-bg-muted);
  }
  .cbui-textarea-filled:focus,
  .cbui-textarea-filled:focus-visible,
  .cbui-textarea-filled[data-cbui-demo-state="focused"] {
    outline: none;
    background-color: var(--color-bg-subtle);
    border-bottom-color: var(--cbui-input-tone-focus);
    box-shadow: none;
  }

  /* Variant: flush */
  .cbui-textarea-flush {
    background-color: var(--cbui-input-flush-bg);
    border-color: transparent;
    border-bottom: 1px solid var(--cbui-input-tone);
    border-radius: 0;
    padding-inline: 0;
    padding-bottom: calc(var(--cbui-input-pad-y) - 1px);
  }
  .cbui-textarea-flush:hover:not(:disabled):not(:focus) {
    border-bottom-color: var(--cbui-input-tone-strong);
  }
  .cbui-textarea-flush:focus,
  .cbui-textarea-flush:focus-visible,
  .cbui-textarea-flush[data-cbui-demo-state="focused"] {
    outline: none;
    border-bottom-width: 2px;
    border-bottom-color: var(--cbui-input-tone-focus);
    padding-bottom: calc(var(--cbui-input-pad-y) - 2px);
  }

  /* Modifier: +focus-ring (opt-in) */
  .cbui-textarea.cbui-textarea-mod-focus-ring:focus,
  .cbui-textarea.cbui-textarea-mod-focus-ring:focus-visible,
  .cbui-textarea.cbui-textarea-mod-focus-ring[data-cbui-demo-state="focused"] {
    outline: none;
    box-shadow:
      0 0 0 var(--cbui-input-ring-gap) var(--cbui-input-ring-gap-color),
      0 0 0 calc(var(--cbui-input-ring-gap) + var(--cbui-input-ring-width)) var(--cbui-input-tone-ring);
  }
  .cbui-textarea-filled.cbui-textarea-mod-focus-ring:focus {
    border-bottom-color: var(--cbui-input-tone-focus);
  }

  /* Modifier: +focus-underline */
  .cbui-textarea.cbui-textarea-mod-focus-underline {
    border-bottom: 2px solid transparent;
    padding-bottom: calc(var(--cbui-input-pad-y) - 1px);
  }
  .cbui-textarea.cbui-textarea-mod-focus-underline:focus,
  .cbui-textarea.cbui-textarea-mod-focus-underline:focus-visible,
  .cbui-textarea.cbui-textarea-mod-focus-underline[data-cbui-demo-state="focused"] {
    border-bottom-color: var(--cbui-input-tone-focus);
  }

  /* Tones */
  .cbui-textarea-tone-primary {
    --cbui-input-tone:        color-mix(in srgb, var(--color-primary) 55%, var(--color-border));
    --cbui-input-tone-strong: var(--color-primary);
    --cbui-input-tone-focus:  var(--color-primary);
    --cbui-input-tone-ring:   var(--color-primary);
  }
  .cbui-textarea-tone-success {
    --cbui-input-tone:        color-mix(in srgb, var(--color-success) 55%, var(--color-border));
    --cbui-input-tone-strong: var(--color-success);
    --cbui-input-tone-focus:  var(--color-success);
    --cbui-input-tone-ring:   var(--color-success);
  }
  .cbui-textarea-tone-warning {
    --cbui-input-tone:        color-mix(in srgb, var(--color-warning) 60%, var(--color-border));
    --cbui-input-tone-strong: var(--color-warning);
    --cbui-input-tone-focus:  var(--color-warning);
    --cbui-input-tone-ring:   var(--color-warning);
  }
  .cbui-textarea-tone-danger {
    --cbui-input-tone:        var(--color-danger);
    --cbui-input-tone-strong: var(--color-danger);
    --cbui-input-tone-focus:  var(--color-danger);
    --cbui-input-tone-ring:   var(--color-danger);
  }
  .cbui-textarea-tone-info {
    --cbui-input-tone:        color-mix(in srgb, var(--color-info) 55%, var(--color-border));
    --cbui-input-tone-strong: var(--color-info);
    --cbui-input-tone-focus:  var(--color-info);
    --cbui-input-tone-ring:   var(--color-info);
  }

  /* Character counter */
  .cbui-textarea-group {
    position: relative;
    display: block;
  }
  .cbui-textarea-counter {
    position: absolute;
    right: 0.625rem;
    bottom: 0.375rem;
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-tertiary);
    pointer-events: none;
    font-variant-numeric: tabular-nums;
  }
  .cbui-textarea-counter-sep { opacity: 0.6; }

  /* Dictation row (CB-3183): the control sits right-aligned under the field. */
  .cbui-textarea-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.375rem;
  }
  /* With both a counter and the dictation row, the counter sits above the row, not on it. */
  .cbui-textarea-group:has(.cbui-textarea-actions) .cbui-textarea-counter {
    bottom: 2.75rem;
  }
  .cbui-textarea-counter.is-near-limit { color: var(--color-text-warning); }
  .cbui-textarea-counter.is-over-limit { color: var(--color-text-danger); }

  /* High contrast — force the gapped ring */
  .a11y-high-contrast .cbui-textarea {
    border-width: 2px;
    --cbui-input-tone:        var(--color-text);
    --cbui-input-tone-strong: var(--color-text);
    --cbui-input-tone-ring:   var(--color-text);
    --cbui-input-ring-width:  3px;
  }

  .a11y-high-contrast .cbui-textarea-tone-primary { --cbui-input-tone: var(--color-primary); --cbui-input-tone-strong: var(--color-primary); --cbui-input-tone-ring: var(--color-primary); --cbui-input-tone-focus: var(--color-primary); }
  .a11y-high-contrast .cbui-textarea-tone-success { --cbui-input-tone: var(--color-success); --cbui-input-tone-strong: var(--color-success); --cbui-input-tone-ring: var(--color-success); --cbui-input-tone-focus: var(--color-success); }
  .a11y-high-contrast .cbui-textarea-tone-warning { --cbui-input-tone: var(--color-warning); --cbui-input-tone-strong: var(--color-warning); --cbui-input-tone-ring: var(--color-warning); --cbui-input-tone-focus: var(--color-warning); }
  .a11y-high-contrast .cbui-textarea-tone-danger  { --cbui-input-tone: var(--color-danger);  --cbui-input-tone-strong: var(--color-danger);  --cbui-input-tone-ring: var(--color-danger);  --cbui-input-tone-focus: var(--color-danger); }
  .a11y-high-contrast .cbui-textarea-tone-info    { --cbui-input-tone: var(--color-info);    --cbui-input-tone-strong: var(--color-info);    --cbui-input-tone-ring: var(--color-info);    --cbui-input-tone-focus: var(--color-info); }

  .a11y-high-contrast .cbui-textarea:focus,
  .a11y-high-contrast .cbui-textarea:focus-visible,
  .a11y-high-contrast .cbui-textarea[data-cbui-demo-state="focused"] {
    outline: none;
    box-shadow:
      0 0 0 var(--cbui-input-ring-gap) var(--cbui-input-ring-gap-color),
      0 0 0 calc(var(--cbui-input-ring-gap) + var(--cbui-input-ring-width)) var(--cbui-input-tone-ring);
  }

  .a11y-high-contrast .cbui-textarea-flush {
    background-color: var(--color-bg-subtle);
    border-bottom-width: 2px;
  }
  .a11y-high-contrast .cbui-textarea-flush:focus,
  .a11y-high-contrast .cbui-textarea-flush:focus-visible,
  .a11y-high-contrast .cbui-textarea-flush[data-cbui-demo-state="focused"] {
    border-bottom-width: 3px;
  }

  /* Width control — mirrors Input. */
  .cbui-textarea-w-full { width: 100%; }
  .cbui-textarea-w-auto { width: auto; }

  /* Marks (CB-3166): the highlight layer of the textarea controller. It exists only where that controller is mounted
     (autoresize, showCount or marks); the Composer field always has it. The layer copies the field metrics in JS. */
  .cbui-textarea-marks {
    position: absolute;
    z-index: 1;
    overflow: hidden;
    white-space: pre-wrap;
    overflow-wrap: break-word;
    pointer-events: none;
    color: transparent;
    animation: cbui-textarea-mark-out 300ms ease-in forwards;
    animation-delay: calc(var(--cbui-textarea-mark-ms, 1500ms) - 300ms);
  }

  .cbui-textarea-mark {
    color: transparent;
    background-color: color-mix(in srgb, var(--color-warning) 35%, transparent);
    border-radius: var(--input-radius);
  }

  /* Tokens (CB-3529): the persistent layer of the textarea controller, below the marks (z-index 0). The layer text stays
     transparent, the field's own text shows through; no animation. A mention is a tint in the primary colour, an
     ambiguous name (a spoken name that fits several people) a dotted underline in the warning colour with a light tint. */
  .cbui-textarea-tokens {
    position: absolute;
    z-index: 0;
    overflow: hidden;
    white-space: pre-wrap;
    overflow-wrap: break-word;
    pointer-events: none;
    color: transparent;
  }

  .cbui-textarea-token {
    color: transparent;
    border-radius: var(--input-radius);
  }

  .cbui-textarea-token-mention {
    background-color: color-mix(in srgb, var(--color-primary) 22%, transparent);
  }

  .cbui-textarea-token-ambiguous {
    background-color: color-mix(in srgb, var(--color-warning) 14%, transparent);
    text-decoration: underline dotted var(--color-warning);
    text-decoration-thickness: 2px;
    text-underline-offset: 0.15em;
  }

  @keyframes cbui-textarea-mark-out {
    to { opacity: 0; }
  }

  @media (prefers-reduced-motion: reduce) {
    .cbui-textarea-marks { animation: none; }
  }

  /* ── @-mention menu (CB-3752) ───────────────────────────────────────────
   * Appended by ui-component--mention to the element it is mounted on (the Textarea group, or a host such as the
   * Composer). Default placement: below the field. Panel chrome as the Dropdown panel. */
  .cbui-mention-menu {
    position: absolute;
    inset-inline: 0;
    /* A very narrow field must not squeeze the label to nothing next to the group badge. */
    min-inline-size: min(14rem, calc(100vw - 2rem));
    top: calc(100% + 0.25rem);
    z-index: var(--z-dropdown, var(--cbui-z-overlay, 1900));
    max-height: 16rem;
    overflow-y: auto;
    padding: 0.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-md, 0.5rem);
    box-shadow: var(--shadow-lg, 0 8px 24px rgb(0 0 0 / 0.16));
  }

  .cbui-mention-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem;
    border-radius: var(--radius-sm, 0.375rem);
    color: var(--color-text-base, var(--color-text));
    cursor: pointer;
    user-select: none;
  }

  .cbui-mention-item-active {
    background-color: var(--color-bg-subtle);
  }

  .cbui-mention-item-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    color: var(--color-text-secondary);
    font-size: 1rem;
  }

  .cbui-mention-item-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
  }

  .cbui-mention-item-label {
    font-size: var(--font-size-md, 1rem);
    line-height: 1.25;
    color: var(--color-text-base, var(--color-text));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cbui-mention-item-desc {
    font-size: var(--font-size-md, 1rem);
    line-height: 1.3;
    color: var(--color-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cbui-mention-item-group {
    flex: none;
    margin-inline-start: auto;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-tertiary, var(--color-text-secondary));
  }

  .a11y-reduced-motion .cbui-mention-menu {
    scroll-behavior: auto;
  }

  .a11y-high-contrast .cbui-mention-menu {
    border-width: 2px;
    border-color: var(--color-text);
  }
  .a11y-high-contrast .cbui-mention-item-active {
    outline: 2px solid var(--color-primary);
    outline-offset: -2px;
  }
}

/* cbui/components/bar */
/* cbui-bar — shared 3-zone slot recipe used by Navbar, Header and Toolbar.
 *
 * Why a shared base?
 *   Navbar already had a 3-zone (start / content / end) slot recipe with
 *   Variant×Tone+Modifier styling, padding/gap tokens and an opt-out divider.
 *   Header and Toolbar need exactly that same 3-zone visual language — but with
 *   different APIs (semantic title/breadcrumb/actions slots for Header; position
 *   for Toolbar) and without Navbar's scroll-driven behaviours. So we extract
 *   the recipe into .cbui-bar and have all three layer their own per-component
 *   adjustments on top:
 *
 *     .cbui-bar-navbar   opens placement, floating, sticky and scroll-* hooks
 *     .cbui-bar-header   pins height (page / section sizes)
 *     .cbui-bar-toolbar  opens position-bottom, density and inline mode
 *
 *   Aero/Glass plastic treatments target .cbui-bar.* directly, so any future
 *   bar-shaped wrapper picks them up automatically.
 *
 * Anatomy:
 *   .cbui-bar              outer wrapper (host element — usually <header>, <nav>, <div>)
 *   .cbui-bar-inner        flex row, optional max-width container
 *   .cbui-bar-start        leading slot
 *   .cbui-bar-content      primary slot (grows)
 *   .cbui-bar-end          trailing slot
 *
 * Variants:    solid · outline · ghost
 * Modifiers:   bg-blur · bg-fill · shadow-lift
 * Tones:       neutral (default) + primary · accent · info · success · danger · warning · muted
 * Inner cap:   .cbui-bar-inner-{sm|md|lg|xl|2xl|full}
 *
 * Tokens (overridable per host component via --bar-* or --cbui-bar-*):
 *   --cbui-bar-bg, --cbui-bar-text, --cbui-bar-text-secondary, --cbui-bar-border
 *   --cbui-bar-radius, --cbui-bar-px, --cbui-bar-py, --cbui-bar-gap
 *   --cbui-bar-shadow-lift, --cbui-bar-blur, --cbui-bar-saturation, --cbui-bar-bg-alpha
 */

.cbui-bar {
    --cbui-bar-bg: var(--bar-bg, var(--color-bg-elevated));
    --cbui-bar-text: var(--bar-text, var(--color-text));
    --cbui-bar-text-secondary: var(--bar-text-secondary, var(--color-text-secondary));
    --cbui-bar-border: var(--bar-border, var(--color-border));
    --cbui-bar-radius: var(--bar-radius, var(--navbar-radius, 0.75rem));
    --cbui-bar-px: var(--bar-padding-x, var(--navbar-padding-x, 1rem));
    --cbui-bar-py: var(--bar-padding-y, var(--navbar-padding-y, 0.75rem));
    --cbui-bar-gap: var(--bar-gap, 1rem);
    --cbui-bar-shadow-lift: 0 4px 12px -2px rgb(0 0 0 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.04);
    --cbui-bar-blur: 12px;
    --cbui-bar-saturation: 160%;
    --cbui-bar-bg-alpha: 0.78;

    position: relative;
    color: var(--cbui-bar-text);
    background-color: var(--cbui-bar-bg);
    transition:
        background-color 200ms ease,
        backdrop-filter 200ms ease,
        box-shadow 200ms ease,
        border-color 200ms ease;
}

/* ── Inner row (3-zone flexbox) ──────────────────────────────────────────── */
.cbui-bar-inner {
    display: flex;
    align-items: center;
    gap: var(--cbui-bar-gap);
    padding-inline: var(--cbui-bar-px);
    padding-block: var(--cbui-bar-py);
    width: 100%;
    margin-inline: auto;
    transition: padding 200ms ease, max-width 200ms ease;
    min-width: 0;
}

/* ── Height scale ────────────────────────────────────────────────────────
 * Token-aligned min-height tier. Padding stays on so the inner content
 * keeps its breathing room — the token is a FLOOR, not a rigid pin.
 * Sibling components (Drawer header, Card header, Modal header,
 * WindowFrame header) consume the same tokens so a surface pair can
 * match by picking the same height tier.
 */
.cbui-bar-h-xs { min-height: var(--cbui-bar-h-xs); }
.cbui-bar-h-sm { min-height: var(--cbui-bar-h-sm); }
.cbui-bar-h-md { min-height: var(--cbui-bar-h-md); }
.cbui-bar-h-lg { min-height: var(--cbui-bar-h-lg); }
.cbui-bar-h-xl { min-height: var(--cbui-bar-h-xl); }

.cbui-bar-start {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-shrink: 0;
    min-width: 0;
}

/* ── Fluid start ─────────────────────────────────────────────────────────
 * The start slot defaults to a fixed, never-shrinking width — right for a
 * logo/title. Some bars pack CONTENT that must give way on a narrow window
 * instead (a segmented Tabnav sharing the slot with the search field), so
 * `start-fluid` lets the start zone shrink like the content zone does. Opt
 * in per bar (CB-1917) — the default stays untouched for every other host. */
.cbui-bar-start-fluid > .cbui-bar-inner > .cbui-bar-start {
    flex-shrink: 1;
    min-width: 0;
}

.cbui-bar-content {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: 1 1 auto;
    min-width: 0;
}

.cbui-bar-end {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-shrink: 0;
    margin-inline-start: auto;
}

/* When `content` slot is omitted, push `end` to the trailing edge so layouts
   with only start+end keep the canonical "logo left, actions right" feel. */
.cbui-bar-inner > .cbui-bar-end:nth-child(2) { margin-inline-start: auto; }

/* ── Inner-width caps ────────────────────────────────────────────────────── */
.cbui-bar-inner-sm   { max-width: 640px; }
.cbui-bar-inner-md   { max-width: 768px; }
.cbui-bar-inner-lg   { max-width: 1024px; }
.cbui-bar-inner-xl   { max-width: 1280px; }
.cbui-bar-inner-2xl  { max-width: 1536px; }
.cbui-bar-inner-full { max-width: none; }

/* ── Variants ────────────────────────────────────────────────────────────── */
.cbui-bar-solid   { background-color: var(--cbui-bar-bg); }
.cbui-bar-outline { background-color: transparent; }
.cbui-bar-ghost   { background-color: transparent; }

/* ── Divider ─────────────────────────────────────────────────────────────── */
/* The `divider` knob is direction-agnostic. Host components decide which side
   gets the line. Default helpers below cover the common cases (bottom for
   top-anchored bars, top for bottom-anchored bars). */
.cbui-bar-with-divider-bottom { border-bottom: 1px solid var(--cbui-bar-border); }
.cbui-bar-with-divider-top    { border-top:    1px solid var(--cbui-bar-border); }

/* Outline variant always carries a divider on the corresponding side, even
   when the `divider` knob is off — the line IS the variant. Hosts apply the
   helper class above to choose the side. */
.cbui-bar-outline.cbui-bar-with-divider-bottom { border-bottom-color: var(--cbui-bar-border); }
.cbui-bar-outline.cbui-bar-with-divider-top    { border-top-color:    var(--cbui-bar-border); }

/* ── Modifiers ───────────────────────────────────────────────────────────── */
.cbui-bar-mod-bg-blur {
    backdrop-filter: blur(var(--cbui-bar-blur)) saturate(var(--cbui-bar-saturation));
    -webkit-backdrop-filter: blur(var(--cbui-bar-blur)) saturate(var(--cbui-bar-saturation));
}
.cbui-bar-solid.cbui-bar-mod-bg-blur {
    background-color: color-mix(in srgb, var(--cbui-bar-bg) calc(var(--cbui-bar-bg-alpha) * 100%), transparent);
}

.cbui-bar-mod-bg-fill.cbui-bar-ghost,
.cbui-bar-mod-bg-fill.cbui-bar-outline {
    background-color: color-mix(in srgb, var(--cbui-bar-bg) 85%, transparent);
}

.cbui-bar-mod-shadow-lift { box-shadow: var(--cbui-bar-shadow-lift); }

/* ── Tones ───────────────────────────────────────────────────────────────── */
/* Tone classes only redefine the colour tokens — they don't add background
   rules of their own, so the variant rules above keep working. */
.cbui-bar-tone-neutral { /* defaults already set on .cbui-bar */ }

.cbui-bar-tone-primary.cbui-bar-solid { --cbui-bar-bg: var(--color-primary); --cbui-bar-text: var(--color-text-on-primary); --cbui-bar-text-secondary: color-mix(in srgb, var(--color-text-on-primary) 75%, transparent); --cbui-bar-border: color-mix(in srgb, var(--color-text-on-primary) 18%, transparent); }
.cbui-bar-tone-info.cbui-bar-solid    { --cbui-bar-bg: var(--color-info);    --cbui-bar-text: var(--color-text-on-info);    --cbui-bar-text-secondary: color-mix(in srgb, var(--color-text-on-info) 75%, transparent);    --cbui-bar-border: color-mix(in srgb, var(--color-text-on-info) 18%, transparent); }
.cbui-bar-tone-success.cbui-bar-solid { --cbui-bar-bg: var(--color-success); --cbui-bar-text: var(--color-text-on-success); --cbui-bar-text-secondary: color-mix(in srgb, var(--color-text-on-success) 75%, transparent); --cbui-bar-border: color-mix(in srgb, var(--color-text-on-success) 18%, transparent); }
.cbui-bar-tone-warning.cbui-bar-solid { --cbui-bar-bg: var(--color-warning); --cbui-bar-text: var(--color-text-on-warning); --cbui-bar-text-secondary: color-mix(in srgb, var(--color-text-on-warning) 75%, transparent); --cbui-bar-border: color-mix(in srgb, var(--color-text-on-warning) 18%, transparent); }
.cbui-bar-tone-danger.cbui-bar-solid  { --cbui-bar-bg: var(--color-danger);  --cbui-bar-text: var(--color-text-on-danger);  --cbui-bar-text-secondary: color-mix(in srgb, var(--color-text-on-danger) 75%, transparent);  --cbui-bar-border: color-mix(in srgb, var(--color-text-on-danger) 18%, transparent); }
.cbui-bar-tone-accent.cbui-bar-solid  { --cbui-bar-bg: var(--color-text);    --cbui-bar-text: var(--color-bg-base);          --cbui-bar-text-secondary: color-mix(in srgb, var(--color-bg-base) 70%, transparent);          --cbui-bar-border: color-mix(in srgb, var(--color-bg-base) 18%, transparent); }
.cbui-bar-tone-muted.cbui-bar-solid   { --cbui-bar-bg: var(--color-bg-subtle); --cbui-bar-text: var(--color-text-secondary); --cbui-bar-text-secondary: var(--color-text-secondary); --cbui-bar-border: var(--color-border); }

/* Outline / ghost tones tint the text + border. */
.cbui-bar-tone-primary:where(.cbui-bar-outline, .cbui-bar-ghost) { --cbui-bar-text: var(--color-primary); --cbui-bar-border: var(--color-primary); }
.cbui-bar-tone-info:where(.cbui-bar-outline, .cbui-bar-ghost)    { --cbui-bar-text: var(--color-info);    --cbui-bar-border: var(--color-info);    }
.cbui-bar-tone-success:where(.cbui-bar-outline, .cbui-bar-ghost) { --cbui-bar-text: var(--color-success); --cbui-bar-border: var(--color-success); }
.cbui-bar-tone-warning:where(.cbui-bar-outline, .cbui-bar-ghost) { --cbui-bar-text: var(--color-warning); --cbui-bar-border: var(--color-warning); }
.cbui-bar-tone-danger:where(.cbui-bar-outline, .cbui-bar-ghost)  { --cbui-bar-text: var(--color-danger);  --cbui-bar-border: var(--color-danger);  }

/* ── Reduced motion / transparency / high-contrast ───────────────────────── */
.a11y-reduced-motion .cbui-bar,
.a11y-reduced-motion .cbui-bar-inner {
    transition: none;
}

.a11y-reduced-transparency .cbui-bar-mod-bg-blur {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--cbui-bar-bg);
}

.a11y-high-contrast .cbui-bar-with-divider-bottom,
.a11y-high-contrast .cbui-bar-with-divider-top,
.a11y-high-contrast .cbui-bar-outline {
    border-color: var(--color-border-strong);
    border-width: 2px;
}

/* cbui/components/navbar */
/* cbui-navbar — horizontal navigation bar, dockable to top or bottom.
 *
 * Anatomy:
 *   .cbui-navbar              outer <nav> (positioning, container query host)
 *   .cbui-navbar-inner        flex row, optional max-width container
 *   .cbui-navbar-start        leading slot (logo / brand)
 *   .cbui-navbar-content      primary nav slot (collapses below md)
 *   .cbui-navbar-end          trailing slot (CTA / avatar / hamburger)
 *
 * Variants (visual treatment):  solid · outline · ghost
 * Modifiers (compose with +):   bg-blur · bg-fill · shadow-lift
 * Tones (semantic colour):      neutral (default) + primary/accent/info/success/danger/warning/muted
 *
 * Layout:
 *   placement: 'top' (default) | 'bottom' — which edge the bar docks to.
 *              The divider side, sticky offset, scroll-hide direction, and
 *              scroll-reveal slide direction all flip based on this. Shadows
 *              always cast downward (light-from-above) — visible on top bars
 *              and on floating-bottom bars (shadow falls into the gap), but
 *              clipped on full-bottom bars (the divider does the separating).
 *   width:    'full' (default) — inner content edge-to-edge of the bar
 *             'contained'      — inner content capped to container_width, centered
 *   floating: bool             — bar detached from edges, picks up navbar-radius
 *   divider:  bool             — show / hide content-facing 1px line (default true)
 *
 * Behaviors:
 *   sticky                    — position: sticky pinned to placement edge
 *   scroll_elevate            — shadow appears once page is scrolled (controller toggles)
 *   scroll_shrink             — vertical padding shrinks once scrolled (now ~50% reduction)
 *   scroll_hide               — hides on scroll-down past threshold, shows on direction change
 *   scroll_reveal             — velocity-tied: bar slides off the placement edge at scroll
 *                                rate on scroll-down, snaps back on scroll-up. Uses sticky.
 *   transparent_until_scroll  — starts transparent, becomes solid+blur after scroll
 *
 * Responsive:
 *   The bar uses `container-type: inline-size` so authors can drop it into any
 *   container width. Below ~640px the .cbui-navbar-content slot hides; the
 *   author places the hamburger trigger using the visibility utility classes.
 *
 * Visibility utilities (work via @container queries, not @media — so they
 * track the navbar's own width, not the viewport):
 *   .cbui-navbar-show-from-md    visible when container >= 640px
 *   .cbui-navbar-show-below-md   visible when container <  640px
 */

/* ── Default tokens (overridable by flavor) ───────────────────────────────── */
/* The 3-zone slot recipe + variants/tones/modifiers live in bar.css under
   .cbui-bar / .cbui-bar-* — this file owns only navbar-specific layout
   (placement, floating, sticky, scroll behaviours, container-queries
   responsive collapse, brand) and tokens that drive them. The Navbar template
   emits both class systems on every element (cbui-bar-* and cbui-navbar-*),
   so the legacy aero/glass/demo selectors keep working. */
.cbui-navbar {
    /* Bridge: feed the bar tokens with our navbar-specific defaults so the
       inherited .cbui-bar rules pick them up automatically. */
    --bar-bg: var(--color-bg-elevated);
    --bar-text: var(--color-text);
    --bar-text-secondary: var(--color-text-secondary);
    --bar-border: var(--color-border);
    --bar-radius: var(--navbar-radius, 0.75rem);
    --bar-padding-x: var(--navbar-padding-x, 1rem);
    /* Default vertical padding is generous so the shrink delta is visible
       without making the bar feel cramped at rest. Flavors can still override. */
    --bar-padding-y: var(--navbar-padding-y, 0.875rem);

    /* Aliases retained for the rules below + any author CSS still using them. */
    --cbui-navbar-bg: var(--bar-bg);
    --cbui-navbar-text: var(--bar-text);
    --cbui-navbar-text-secondary: var(--bar-text-secondary);
    --cbui-navbar-border: var(--bar-border);
    --cbui-navbar-radius: var(--bar-radius);
    --cbui-navbar-px: var(--bar-padding-x);
    --cbui-navbar-py: var(--bar-padding-y);
    /* Shrunk-state padding — half of base is the canonical "compact" target. */
    --cbui-navbar-py-shrunk: calc(var(--cbui-navbar-py) * 0.45);
    --cbui-navbar-gap: 1rem;
    --cbui-navbar-floating-margin: 1rem;
    --cbui-navbar-blur: 12px;
    --cbui-navbar-saturation: 160%;
    --cbui-navbar-bg-alpha: 0.78;
    /* Shadows always cast downward (light-from-above convention). For top
       placement the shadow lands on content (visible). For floating-bottom
       it falls into the gap (visible). For full-bottom non-floating the
       shadow is clipped by the viewport — by design: divider takes over. */
    --cbui-navbar-shadow-floating: 0 8px 24px -6px rgb(0 0 0 / 0.12), 0 2px 6px -2px rgb(0 0 0 / 0.06);

    /* Container queries: track our own width, not the viewport. */
    container-type: inline-size;
    container-name: cbui-navbar;

    /* NOTE: NO `isolation: isolate` here. A Navbar typically hosts Dropdowns
       (user menu, switchers) that MUST escape the navbar's stacking context
       to land above a focused / docked window (HANDOVER-SHELL §6). The
       framework-wide z-index bands (UiKernel base.css) coordinate stacking;
       the navbar's loud-vs-quiet visual stays intact without isolation
       because its own backdrop-filter / shadow rules are scoped to .cbui-bar
       rules that don't depend on isolation. */

    transition:
        background-color 200ms ease,
        backdrop-filter 200ms ease,
        box-shadow 200ms ease,
        border-bottom-color 200ms ease,
        transform 240ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* width=full overrides any inner-* cap to keep content edge-to-edge */
.cbui-navbar-width-full > .cbui-navbar-inner { max-width: none; }

/* Tighter content gap inside the navbar (NavItems prefer 0.25rem). The shared
   .cbui-bar-content default is 0.5rem; we override here. */
.cbui-navbar-content { gap: 0.25rem; }

/* ── Floating layout ─────────────────────────────────────────────────────── */
.cbui-navbar-floating {
    margin: var(--cbui-navbar-floating-margin);
    border-radius: var(--cbui-navbar-radius);
    border: 1px solid var(--cbui-navbar-border);
    box-shadow: var(--cbui-navbar-shadow-floating);
    overflow: hidden;
}
/* Floating already has a full border — the divider switch is a no-op visually
   (the same border is already there on every side). Kept for completeness; the
   intent stays the same as for non-floating: side flips with placement. */
.cbui-navbar-placement-top.cbui-navbar-floating.cbui-navbar-with-divider {
    border-bottom: 1px solid var(--cbui-navbar-border);
}
.cbui-navbar-placement-bottom.cbui-navbar-floating.cbui-navbar-with-divider {
    border-top: 1px solid var(--cbui-navbar-border);
}

/* ── Sticky behavior (pure CSS) ──────────────────────────────────────────── */
.cbui-navbar-sticky {
    position: sticky;
    z-index: 30;
}
.cbui-navbar-placement-top.cbui-navbar-sticky    { top: 0; }
.cbui-navbar-placement-bottom.cbui-navbar-sticky { bottom: 0; }
.cbui-navbar-placement-top.cbui-navbar-sticky.cbui-navbar-floating {
    top: var(--cbui-navbar-floating-margin);
}
.cbui-navbar-placement-bottom.cbui-navbar-sticky.cbui-navbar-floating {
    bottom: var(--cbui-navbar-floating-margin);
}

/* ── scroll_reveal — sticky bar driven by --cbui-navbar-reveal-offset ─────
 * Two animation modes share one offset variable but apply it differently:
 *
 *   Non-floating (default): TRANSFORM-based. The bar translateYs off-screen
 *     by `offset` pixels — sign depends on placement (up for top, down for
 *     bottom). Layout (the bar's flow position) is unaffected.
 *
 *   Floating: TOP / BOTTOM-based. The sticky placement-edge value animates
 *     from `floating-margin` toward a negative offset, which shifts the bar's
 *     entire margin-box. The floating margin (visual breathing-room on the
 *     placement side) slides out *together* with the bar — instead of
 *     remaining as a static canvas-coloured strip while the bar disappears
 *     underneath it. Animating top/bottom doesn't reflow other content
 *     because sticky positioning is fully visual.
 *
 * Both modes share `reveal_speed` (fast=instant snap; medium=450ms ease-out;
 * slow=850ms ease-out) for slide-IN. Slide-OUT always tracks scroll velocity
 * (~80ms linear) for both modes.
 */
.cbui-navbar-scroll-reveal {
    position: sticky;
    z-index: 30;
    --cbui-navbar-reveal-offset: 0px;
    --cbui-navbar-reveal-out-duration: 80ms;
    --cbui-navbar-reveal-out-easing: linear;
    --cbui-navbar-reveal-in-duration: 0ms;
    --cbui-navbar-reveal-in-easing: linear;
    transition:
        transform var(--cbui-navbar-reveal-out-duration) var(--cbui-navbar-reveal-out-easing),
        top    var(--cbui-navbar-reveal-out-duration) var(--cbui-navbar-reveal-out-easing),
        bottom var(--cbui-navbar-reveal-out-duration) var(--cbui-navbar-reveal-out-easing),
        background-color 200ms ease,
        box-shadow 200ms ease;
}
/* Non-floating reveal: transform direction flips with placement. */
.cbui-navbar-placement-top.cbui-navbar-scroll-reveal {
    top: 0;
    transform: translateY(calc(var(--cbui-navbar-reveal-offset) * -1));
}
.cbui-navbar-placement-bottom.cbui-navbar-scroll-reveal {
    bottom: 0;
    transform: translateY(var(--cbui-navbar-reveal-offset));
}
/* Floating reveal: sticky edge animates with the offset.
 *
 *   Sticky `top`/`bottom` clamps the BORDER box (not the margin box) — so to
 *   actually show the floating margin around the bar at rest, the placement
 *   side must equal `floating-margin` (not 0). Slide-out goes from
 *   floating-margin toward a negative value; slide-in returns to
 *   floating-margin so the breathing-room is back exactly where it was.
 *
 *   The non-placement margin (margin-bottom for top placement, margin-top
 *   for bottom placement) is dropped: the bar's content-facing edge meets
 *   page content with no extra gap so floating-reveal feels visually
 *   consistent with the non-floating reveal demo.
 */
.cbui-navbar-floating.cbui-navbar-scroll-reveal {
    transform: none;
}
.cbui-navbar-placement-top.cbui-navbar-floating.cbui-navbar-scroll-reveal {
    margin-bottom: 0;
    top: calc(var(--cbui-navbar-floating-margin) - var(--cbui-navbar-reveal-offset, 0px));
}
.cbui-navbar-placement-bottom.cbui-navbar-floating.cbui-navbar-scroll-reveal {
    margin-top: 0;
    bottom: calc(var(--cbui-navbar-floating-margin) - var(--cbui-navbar-reveal-offset, 0px));
}
/* When the controller has just snapped offset back to 0 (scroll-up), the bar
   uses the slide-IN duration/easing instead of the slide-OUT one. The class
   stays on until the next scroll-down so the slide-in animation can play to
   completion without being interrupted by the slide-out's transition. */
.cbui-navbar-scroll-reveal.cbui-navbar-reveal-snap {
    transition:
        transform var(--cbui-navbar-reveal-in-duration) var(--cbui-navbar-reveal-in-easing),
        top var(--cbui-navbar-reveal-in-duration) var(--cbui-navbar-reveal-in-easing),
        background-color 200ms ease,
        box-shadow 200ms ease;
}
/* Speed modifiers — apply to the bar to set the slide-in feel.
   `fast` (default) is the original snap behaviour and needs no class.
   `medium` and `slow` use a soft expo-style ease-out so the bar settles
   smoothly rather than springing. */
.cbui-navbar-reveal-speed-medium {
    --cbui-navbar-reveal-in-duration: 450ms;
    --cbui-navbar-reveal-in-easing: cubic-bezier(0.16, 1, 0.3, 1);
}
.cbui-navbar-reveal-speed-slow {
    --cbui-navbar-reveal-in-duration: 850ms;
    --cbui-navbar-reveal-in-easing: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ── Scroll-elevate — shadow appears past the threshold ──────────────────── */
.cbui-navbar-scroll-elevate.is-scrolled {
    box-shadow: var(--cbui-navbar-shadow-lift);
}
.cbui-navbar-scroll-elevate.cbui-navbar-floating.is-scrolled {
    box-shadow: var(--cbui-navbar-shadow-floating), 0 0 0 1px var(--cbui-navbar-border);
}

/* ── Scroll-shrink — half-padding once scrolled ──────────────────────────── */
.cbui-navbar-scroll-shrink.is-scrolled .cbui-navbar-inner {
    padding-block: var(--cbui-navbar-py-shrunk);
}

/* ── Scroll-hide — slide off the placement edge (snap, on direction change) ─ */
.cbui-navbar-placement-top.cbui-navbar-scroll-hide.is-hidden {
    transform: translateY(-100%);
}
.cbui-navbar-placement-bottom.cbui-navbar-scroll-hide.is-hidden {
    transform: translateY(100%);
}
.cbui-navbar-placement-top.cbui-navbar-scroll-hide.cbui-navbar-floating.is-hidden {
    transform: translateY(calc(-100% - var(--cbui-navbar-floating-margin)));
}
.cbui-navbar-placement-bottom.cbui-navbar-scroll-hide.cbui-navbar-floating.is-hidden {
    transform: translateY(calc(100% + var(--cbui-navbar-floating-margin)));
}

/* ── transparent_until_scroll — initial transparent, snap to bg on scroll ──
   Strip the divider while transparent so the bar reads as truly invisible.
   The divider side flips with placement; we use the `border-color` shorthand
   so the rule stays placement-neutral (only the side with non-zero width
   actually paints). */
.cbui-navbar-transparent-until-scroll {
    background-color: transparent;
    border-color: transparent;
}
.cbui-navbar-transparent-until-scroll.is-scrolled {
    background-color: color-mix(in srgb, var(--color-bg-elevated) calc(var(--cbui-navbar-bg-alpha) * 100%), transparent);
    border-color: var(--cbui-navbar-border);
    backdrop-filter: blur(var(--cbui-navbar-blur)) saturate(var(--cbui-navbar-saturation));
    -webkit-backdrop-filter: blur(var(--cbui-navbar-blur)) saturate(var(--cbui-navbar-saturation));
}

/* ── Container-query-driven responsive collapse ──────────────────────────── */
@container cbui-navbar (max-width: 640px) {
    .cbui-navbar-content { display: none; }
    .cbui-navbar-show-from-md { display: none !important; }
}
@container cbui-navbar (min-width: 640px) {
    .cbui-navbar-show-below-md { display: none !important; }
}

@supports not (container-type: inline-size) {
    @media (max-width: 640px) {
        .cbui-navbar-content { display: none; }
        .cbui-navbar-show-from-md { display: none !important; }
    }
    @media (min-width: 641px) {
        .cbui-navbar-show-below-md { display: none !important; }
    }
}

/* ── Reduced motion / transparency / high-contrast ───────────────────────── */
.a11y-reduced-motion .cbui-navbar,
.a11y-reduced-motion .cbui-navbar-inner {
    transition: none;
}
.a11y-reduced-motion .cbui-navbar-scroll-hide.is-hidden {
    transform: none;
    display: none;
}
.a11y-reduced-motion .cbui-navbar-scroll-reveal {
    transform: none !important;
}

.a11y-reduced-transparency .cbui-navbar-mod-bg-blur,
.a11y-reduced-transparency .cbui-navbar-transparent-until-scroll.is-scrolled {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--cbui-navbar-bg);
}
.a11y-reduced-transparency .cbui-navbar-transparent-until-scroll {
    background-color: var(--cbui-navbar-bg);
    border-bottom-color: var(--cbui-navbar-border);
}

.a11y-high-contrast .cbui-navbar-with-divider,
.a11y-high-contrast .cbui-navbar-outline {
    border-bottom-width: 2px;
    border-bottom-color: var(--color-border-strong);
}
.a11y-high-contrast .cbui-navbar-floating {
    border-width: 2px;
    border-color: var(--color-border-strong);
}

/* ──────────────────────────────────────────────────────────────────────────
 * NavItem — the single, unified pill / underline / plain interactive
 * inside Navbar (and later Nav). Brand uses the same hover/focus recipe
 * via .cbui-nav-item-base — there is one styling, applied two places.
 * ────────────────────────────────────────────────────────────────────────── */

.cbui-nav-item-base,
.cbui-nav-item {
    --cbui-nav-item-radius: var(--nav-item-radius, var(--navbar-link-radius, 0.5rem));
    --cbui-nav-item-px: var(--nav-item-padding-x, var(--navbar-link-padding-x, 0.75rem));
    /* md item: 1 rem type grew the line by 2.5 px, so the vertical padding gives back 1 px per side (CB-2918) */
    --cbui-nav-item-py: calc(var(--nav-item-padding-y, var(--navbar-link-padding-y, 0.5rem)) - 0.0625rem);
    --cbui-nav-item-font-size: var(--font-size-md, 1rem);
    --cbui-nav-item-icon-size: 1rem;
    --cbui-nav-item-gap: 0.5rem;
    --cbui-nav-item-indent: 0;
    --cbui-nav-item-indent-step: 1rem;
    --cbui-nav-item-text: var(--cbui-navbar-text, var(--color-text));
    --cbui-nav-item-text-muted: var(--cbui-navbar-text-secondary, var(--color-text-secondary));
    --cbui-nav-item-hover-bg: color-mix(in srgb, currentColor 10%, transparent);
    --cbui-nav-item-active-bg: color-mix(in srgb, currentColor 14%, transparent);

    display: inline-flex;
    align-items: center;
    gap: var(--cbui-nav-item-gap);
    padding-inline: var(--cbui-nav-item-px);
    padding-block: var(--cbui-nav-item-py);
    border-radius: var(--cbui-nav-item-radius);
    font-size: var(--cbui-nav-item-font-size);
    font-weight: var(--font-weight-medium, 500);
    line-height: 1.25;
    color: var(--cbui-nav-item-text-muted);
    background-color: transparent;
    border: 0;
    cursor: pointer;
    text-decoration: none;
    transition: background-color 150ms, color 150ms, box-shadow 150ms;
    position: relative;
    white-space: nowrap;
    text-align: start;
    font-family: inherit;
}

.cbui-nav-item-base:hover,
.cbui-nav-item:hover {
    color: var(--cbui-nav-item-text);
    background-color: var(--cbui-nav-item-hover-bg);
}
.cbui-nav-item-base:focus-visible,
.cbui-nav-item:focus-visible {
    outline: none;
    box-shadow:
        0 0 0 var(--focus-ring-offset, 2px) var(--cbui-navbar-bg),
        0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring);
}

.cbui-nav-item-disabled,
.cbui-nav-item[aria-disabled="true"],
.cbui-nav-item:disabled {
    opacity: 0.5;
    pointer-events: none;
    cursor: not-allowed;
}

/* Icon slots — wrapped in spans so the same selectors work whether the icon
   exists or not, and align with DropdownItem's anatomy. */
.cbui-nav-item-icon,
.cbui-nav-item-icon-end,
.cbui-nav-item-caret {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--cbui-nav-item-icon-size);
    height: var(--cbui-nav-item-icon-size);
    flex-shrink: 0;
}
.cbui-nav-item-icon-spacer {
    display: none; /* horizontal navbar — icons are inline; spacer is for vertical Nav */
    width: var(--cbui-nav-item-icon-size);
    flex-shrink: 0;
}

/* Body wraps label + (optional) description so that sibling slots (badge,
   shortcut, end-icon) sit at the trailing edge regardless of description. */
.cbui-nav-item-body {
    display: inline-flex;
    flex-direction: column;
    gap: 0.125rem;
    flex: 1 1 auto;
    min-width: 0;
}
.cbui-nav-item-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: inherit;
}
.cbui-nav-item-description {
    font-size: var(--font-size-md, 1rem);
    line-height: 1.3;
    color: var(--cbui-nav-item-text-muted);
    opacity: 0.85;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.cbui-nav-item-with-description {
    align-items: flex-start;
    padding-block: calc(var(--cbui-nav-item-py) + 0.0625rem);
}
.cbui-nav-item-with-description .cbui-nav-item-icon,
.cbui-nav-item-with-description .cbui-nav-item-icon-end,
.cbui-nav-item-with-description .cbui-nav-item-caret {
    margin-block-start: 0.0625rem;
}

/* Trailing-edge slots: badge + shortcut. They push to the right of the body
   via margin-inline-start: auto on the first one in the row. */
.cbui-nav-item-badge {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    height: 1.25rem;
    padding-inline: 0.375rem;
    border-radius: 999px;
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-semibold, 600);
    line-height: 1;
    background-color: color-mix(in srgb, currentColor 18%, transparent);
    color: var(--cbui-nav-item-text);
    margin-inline-start: auto;
}
/* When the active state is on a tinted bg (pill variant), the badge needs to
   stand off it slightly more — bump alpha so it doesn't disappear into the
   pill background. */
.cbui-nav-item-pill[aria-current="page"] .cbui-nav-item-badge,
.cbui-nav-item-pill.cbui-nav-item-active .cbui-nav-item-badge {
    background-color: color-mix(in srgb, currentColor 28%, transparent);
}

/* The chip paint (mono font, fill, border, radius) now lives in the shared
   Kbd primitive (CB-50) — NavItem composes <twig:UiComponent:Kbd> for the
   shortcut hint. Only the layout role stays here. */
.cbui-nav-item-shortcut {
    flex-shrink: 0;
    color: var(--cbui-nav-item-text-muted);
}
.cbui-nav-item-badge + .cbui-nav-item-shortcut {
    margin-inline-start: 0;
}
/* If a shortcut exists but no badge, push shortcut to the right edge. */
.cbui-nav-item:not(:has(.cbui-nav-item-badge)) .cbui-nav-item-shortcut {
    margin-inline-start: auto;
}

/* Caret rotation — driven by aria-expanded on the same element. */
.cbui-nav-item-caret {
    transition: transform 150ms ease;
}
.cbui-nav-item[aria-expanded="true"] .cbui-nav-item-caret {
    transform: rotate(90deg);
}

/* Indent — used by NavTreeNode to communicate depth. Maps the integer var
   to a calc'd start padding. */
.cbui-nav-item {
    padding-inline-start: calc(var(--cbui-nav-item-px) + var(--cbui-nav-item-indent) * var(--cbui-nav-item-indent-step));
}

/* Sizes — text + padding + icon size. md is the default and reuses tokens
   already set above; sm and lg override. */
.cbui-nav-item-size-sm {
    --cbui-nav-item-font-size: var(--font-size-md, 1rem);
    --cbui-nav-item-px: calc(var(--nav-item-padding-x, var(--navbar-link-padding-x, 0.75rem)) * 0.85);
    --cbui-nav-item-py: calc(var(--nav-item-padding-y, var(--navbar-link-padding-y, 0.5rem)) * 0.7);
    --cbui-nav-item-icon-size: 0.875rem;
    --cbui-nav-item-gap: 0.4375rem;
}
.cbui-nav-item-size-lg {
    --cbui-nav-item-font-size: var(--font-size-md, 1rem);
    --cbui-nav-item-px: calc(var(--nav-item-padding-x, var(--navbar-link-padding-x, 0.75rem)) * 1.15);
    --cbui-nav-item-py: calc(var(--nav-item-padding-y, var(--navbar-link-padding-y, 0.5rem)) * 1.2);
    --cbui-nav-item-icon-size: 1.125rem;
    --cbui-nav-item-gap: 0.625rem;
}

/* Variant: pill — active state is a tinted pill */
.cbui-nav-item-pill[aria-current="page"],
.cbui-nav-item-pill.cbui-nav-item-active {
    color: var(--cbui-nav-item-text);
    background-color: var(--cbui-nav-item-active-bg);
}

/* Variant: underline — active state is a 2px bottom indicator */
.cbui-nav-item-underline {
    border-radius: 0;
    padding-block: calc(var(--cbui-nav-item-py) + 2px) var(--cbui-nav-item-py);
}
.cbui-nav-item-underline::after {
    content: "";
    position: absolute;
    inset-inline: var(--cbui-nav-item-px);
    bottom: 0;
    height: 2px;
    background-color: currentColor;
    border-radius: 1px 1px 0 0;
    opacity: 0;
    transition: opacity 150ms;
}
.cbui-nav-item-underline:hover { background-color: transparent; }
.cbui-nav-item-underline:hover::after { opacity: 0.4; }
.cbui-nav-item-underline[aria-current="page"],
.cbui-nav-item-underline.cbui-nav-item-active {
    color: var(--cbui-nav-item-text);
    background-color: transparent;
}
.cbui-nav-item-underline[aria-current="page"]::after,
.cbui-nav-item-underline.cbui-nav-item-active::after {
    opacity: 1;
}

/* Variant: plain — no chrome, just colour change */
.cbui-nav-item-plain {
    padding-inline: 0.25rem;
    border-radius: 0;
}
.cbui-nav-item-plain:hover { background-color: transparent; color: var(--cbui-nav-item-text); }
.cbui-nav-item-plain[aria-current="page"],
.cbui-nav-item-plain.cbui-nav-item-active { color: var(--cbui-nav-item-text); }

/* Tone: plain (CB-1154) — a Stripe-/GitHub-settings-style quiet Nav. Active
 * state is a 2px leading accent bar in --color-primary plus primary-coloured
 * text. Scoped to the pill NavItem variant (SectionNav's `side` items use
 * variant="pill") inside a tone="plain" Nav container.
 *
 * ⭐ Matthias 2026-09-06: the active item carries a PERMANENT surface, not
 * only one on hover. Before this it was `background-color: transparent`, so
 * the only permanent marker was the 2px bar — and hovering any OTHER item
 * gave that item a stronger fill than the active one, which read as if the
 * hovered row were the current one. The fill is a primary tint (not
 * --color-bg-subtle, which hover uses) so active and hover stay
 * distinguishable, and it mixes with `transparent` so it works over whatever
 * surface the host panel has, light and dark. */
.cbui-nav-tone-plain .cbui-nav-item-pill[aria-current="page"],
.cbui-nav-tone-plain .cbui-nav-item-pill.cbui-nav-item-active {
    background-color: color-mix(in srgb, var(--color-primary) 10%, transparent);
    color: var(--color-primary);
    border-inline-start: 2px solid var(--color-primary);
    padding-inline-start: calc(var(--cbui-nav-item-px) - 2px);
}
.cbui-nav-tone-plain .cbui-nav-item-pill:hover {
    background-color: var(--color-bg-subtle);
}
/* Hovering the ACTIVE item must not weaken it — the rule above would
   otherwise win by source order and swap the primary tint for the neutral
   hover surface. */
.cbui-nav-tone-plain .cbui-nav-item-pill[aria-current="page"]:hover,
.cbui-nav-tone-plain .cbui-nav-item-pill.cbui-nav-item-active:hover {
    background-color: color-mix(in srgb, var(--color-primary) 16%, transparent);
}

/* Tones */
.cbui-nav-item-tone-danger  { --cbui-nav-item-text: var(--color-danger);  --cbui-nav-item-text-muted: var(--color-danger);  }
.cbui-nav-item-tone-warning { --cbui-nav-item-text: var(--color-warning); --cbui-nav-item-text-muted: var(--color-warning); }
.cbui-nav-item-tone-success { --cbui-nav-item-text: var(--color-success); --cbui-nav-item-text-muted: var(--color-success); }
.cbui-nav-item-tone-info    { --cbui-nav-item-text: var(--color-info);    --cbui-nav-item-text-muted: var(--color-info);    }
.cbui-nav-item-tone-primary { --cbui-nav-item-text: var(--color-primary); --cbui-nav-item-text-muted: var(--color-primary); }
.cbui-nav-item-tone-accent  { --cbui-nav-item-text: var(--color-text);    --cbui-nav-item-text-muted: var(--color-text);    }
.cbui-nav-item-tone-neutral { --cbui-nav-item-text: var(--color-text);    --cbui-nav-item-text-muted: var(--color-text-secondary); }

.a11y-reduced-motion .cbui-nav-item,
.a11y-reduced-motion .cbui-nav-item-base,
.a11y-reduced-motion .cbui-nav-item-underline::after { transition: none; }

/* ──────────────────────────────────────────────────────────────────────────
 * NavSeparator
 * ────────────────────────────────────────────────────────────────────────── */
.cbui-nav-separator {
    display: inline-block;
    background-color: color-mix(in srgb, currentColor 15%, transparent);
    flex-shrink: 0;
    align-self: center;
}
.cbui-nav-separator-vertical {
    width: 1px;
    height: 1.25rem;
    margin-inline: 0.5rem;
}
.cbui-nav-separator-horizontal {
    height: 1px;
    width: 100%;
    margin-block: 0.5rem;
}
.a11y-high-contrast .cbui-nav-separator-vertical { width: 2px; }
.a11y-high-contrast .cbui-nav-separator-horizontal { height: 2px; }

/* ──────────────────────────────────────────────────────────────────────────
 * Brand helper — visually a NavItem-like control (logo + name).
 * Inherits the unified .cbui-nav-item-base styling so brand and nav-items
 * share one hover/focus recipe — no second styling system to maintain.
 * Adds only what's brand-specific: heavier font weight, slightly larger icon.
 * ────────────────────────────────────────────────────────────────────────── */
.cbui-navbar-brand {
    /* class composes with cbui-nav-item-base in the template */
    font-weight: var(--font-weight-semibold, 600);
    font-size: var(--font-size-base, 1rem);
}
.cbui-navbar-brand-icon {
    width: 1.5rem;
    height: 1.5rem;
    flex-shrink: 0;
}
.cbui-navbar-brand-name { white-space: nowrap; }

/* ──────────────────────────────────────────────────────────────────────────
 * Storybook demo helpers
 * Authoring-only utility class to give the `outline` variant a tinted
 * backdrop in light mode so its transparency reads against the page (without
 * it, the white outline bar disappears against a white surface). Dark mode
 * already shows the contrast naturally.
 * ────────────────────────────────────────────────────────────────────────── */
.cbui-navbar-demo-tinted {
    background-color: rgb(254 252 232); /* tailwind yellow-50 */
    border-radius: 0.5rem;
}
[data-scheme="dark"] .cbui-navbar-demo-tinted {
    background-color: transparent;
}

/* cbui/components/header */
/* cbui-bar-header — page / section header (per-component layer on .cbui-bar).
 *
 * Inherits the 3-zone slot recipe + variant/tone/modifier system from .cbui-bar
 * (see bar.css). This file owns only what is header-specific:
 *   - fixed heights (page / section)
 *   - the semantic sub-slots (title-block, breadcrumb, status, description,
 *     extras, actions, back)
 *   - optional sticky positioning
 *
 * Per-component adjustments (§2.1 HANDOVER-WELLE):
 *   .cbui-bar-header           pins height + activates title-block layout
 *   .cbui-bar-header-page      h-15 (60px) — page-level header
 *   .cbui-bar-header-section   h-12 (48px) — inside a card / column
 *   .cbui-bar-header-sticky    position: sticky; top: 0
 */

.cbui-bar-header {
    /* Override the bar's default vertical padding — height is enforced. */
    --bar-padding-y: 0;
}

.cbui-bar-header-page    .cbui-bar-header-inner { min-height: 3.75rem; /* 60px */ }
.cbui-bar-header-section .cbui-bar-header-inner { min-height: 3rem;    /* 48px */ }

/* Sticky inside a scrollable column. */
.cbui-bar-header-sticky {
    position: sticky;
    top: 0;
    z-index: 20;
}

/* ── Start zone ──────────────────────────────────────────────────────────── */
.cbui-bar-header-start {
    /* Title block can grow; back button stays as a fixed lozenge. */
    flex: 1 1 auto;
    min-width: 0;
    gap: 0.75rem;
}

.cbui-bar-header-back {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
}

.cbui-bar-header-title-block {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
}

.cbui-bar-header-breadcrumb {
    font-size: var(--font-size-md, 1rem);
    line-height: 1.25;
    color: var(--cbui-bar-text-secondary);
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cbui-bar-header-title-row {
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
    min-width: 0;
}

.cbui-bar-header-title {
    margin: 0;
    font-weight: var(--font-weight-semibold, 600);
    color: var(--cbui-bar-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    line-height: 1.25;
}

.cbui-bar-header-page    .cbui-bar-header-title { font-size: var(--font-size-lg, 1.125rem); }
.cbui-bar-header-section .cbui-bar-header-title { font-size: var(--font-size-md, 1rem); }

.cbui-bar-header-title-link {
    color: inherit;
    text-decoration: none;
}
.cbui-bar-header-title-link:hover { text-decoration: underline; }
.cbui-bar-header-title-link:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
    border-radius: 2px;
}

.cbui-bar-header-subtitle {
    font-family: var(--font-family-mono, ui-monospace, monospace);
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-medium, 500);
    color: var(--cbui-bar-text-secondary);
    padding: 0.125rem 0.375rem;
    background-color: color-mix(in srgb, currentColor 8%, transparent);
    border-radius: var(--radius-sm, 0.25rem);
    white-space: nowrap;
    flex-shrink: 0;
}

.cbui-bar-header-status {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
}

.cbui-bar-header-description {
    margin: 0;
    font-size: var(--font-size-md, 1rem);
    color: var(--cbui-bar-text-secondary);
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* When breadcrumb + description both render, the description sits on its own
   line below — keep both readable in the constrained header height. */
/* 1 rem type grew the block by 2.8 px, so each side gives back 1 px (CB-2918). */
.cbui-bar-header-page    .cbui-bar-header-title-block { padding-block: 0.3125rem; }
.cbui-bar-header-section .cbui-bar-header-title-block { padding-block: 0.1875rem; }

/* ── End zone ────────────────────────────────────────────────────────────── */
.cbui-bar-header-end {
    gap: 0.5rem;
    flex-shrink: 0;
}

.cbui-bar-header-extras {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: var(--font-size-md, 1rem);
    color: var(--cbui-bar-text-secondary);
    white-space: nowrap;
}

.cbui-bar-header-actions {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

/* ── Responsive: collapse description on narrow containers ───────────────── */
.cbui-bar-header {
    container-type: inline-size;
    container-name: cbui-bar-header;
}
@container cbui-bar-header (max-width: 480px) {
    .cbui-bar-header-description,
    .cbui-bar-header-breadcrumb { display: none; }
    .cbui-bar-header-subtitle   { display: none; }
}

/* ── size="resource" — a resource detail head (CB-1698) ────────────────────
 * Ported from the Agent-Bringer's `.cb-agm-head` (agent-bringer-window.css
 * :289-343) as a reusable CBUI building block. No fixed height pin (unlike
 * page/section): the head grows with its content (name+badges, optional
 * description, meta line).
 * ⚠ `line-height: inherit` on title/description is deliberate — the
 * reference's name/description inherit the surrounding body line-height;
 * Header's own page/section line-heights would make the head 4+ px shorter
 * than the measured Agent-Bringer reference. */
.cbui-bar-header.cbui-bar-header-resource {
    --bar-padding-y: var(--spacing-sm);
    --bar-padding-x: var(--spacing-md);
    --bar-bg: transparent;
}
.cbui-bar-header-resource > .cbui-bar-inner {
    align-items: flex-start;
}
.cbui-bar-header-resource .cbui-bar-header-start {
    gap: var(--spacing-sm);
    align-items: flex-start;
}
/* A back control before the title block is centred like in ResourceHeader
   (CB-2917): the back slot always, the lead slot only when a lone button. */
.cbui-bar-header-resource .cbui-bar-header-back,
.cbui-bar-header-resource .cbui-bar-header-lead:has(> .cbui-button:only-child) {
    align-self: center;
}
.cbui-bar-header-resource .cbui-bar-header-title-block {
    gap: 0.15rem;
}
.cbui-bar-header-resource .cbui-bar-header-title-row {
    gap: var(--spacing-xs);
}
.cbui-bar-header-resource .cbui-bar-header-title {
    font-size: var(--font-size-md, 1rem);
    line-height: inherit;
}
.cbui-bar-header-resource .cbui-bar-header-description {
    line-height: inherit;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* The underline Tabnav right after the head is the resource menubar and docks flush (CB-2913);
   every other variant keeps the gap. */
.cbui-bar-header-resource + .cbui-tabnav:not(.cbui-tabnav-underline) {
    margin-block-start: var(--spacing-sm);
}
.cbui-bar-header-lead {
    flex: none;
    display: inline-flex;
    align-items: center;
}
.cbui-bar-header-lead-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: var(--radius-full, 999px);
    background: var(--color-bg-subtle);
    color: var(--color-text-secondary);
}
.cbui-bar-header-lead-icon svg {
    width: 1.15rem;
    height: 1.15rem;
}
.cbui-bar-header-meta {
    font-size: var(--font-size-md, 1rem);
    color: var(--cbui-bar-text-secondary);
    min-width: 0;
}

/* cbui/components/toolbar */
/* cbui-bar-toolbar — page / section toolbar (per-component layer on .cbui-bar).
 *
 * Inherits the 3-zone slot recipe + variant/tone/modifier system from .cbui-bar
 * (see bar.css). This file owns only toolbar-specific behaviour:
 *   - position: top / bottom / inline
 *   - density: comfortable / compact / spacious
 *   - sticky positioning + safe-area-inset-bottom
 *   - optional title slot styling
 */

/* ── Action gap: ONE distance between neighbouring actions (Matthias 2026-10-29) ──
 * `--cbui-toolbar-action-gap` (10px, 0.625rem; CBUI-wide default `--cbui-action-gap`) is the space between the
 * buttons of one action
 * group in a Toolbar / window menubar, in every slot and every density. Groups that must sit
 * apart (a count chip, a separator, the Overflow button's own group) get the same gap as a
 * flex item, never a hand-set margin. A segmented ButtonGroup / Tabnav stays at 0 between its
 * own segments (it is one control); the token only spaces separate controls. Override per host
 * with `--toolbar-action-gap`, never with margins on the buttons. */
.cbui-bar-toolbar {
    --cbui-toolbar-action-gap: var(--toolbar-action-gap, var(--cbui-action-gap, 0.625rem));
    --actions-bar-gap: var(--cbui-toolbar-action-gap);
}
.cbui-bar-toolbar > .cbui-bar-inner > :is(.cbui-bar-start, .cbui-bar-content, .cbui-bar-end) {
    gap: var(--cbui-toolbar-action-gap);
}

/* ── Density tiers ───────────────────────────────────────────────────────── */
.cbui-bar-toolbar-comfortable { --bar-padding-y: 0.625rem; }
.cbui-bar-toolbar-compact     { --bar-padding-y: 0.375rem; --cbui-bar-gap: 0.5rem; }
.cbui-bar-toolbar-spacious    { --bar-padding-y: 1rem; }

/* ── Height tier: the tier is the height (CB-2911) ───────────────────────
 * A Toolbar with `height` takes its tier as the bar's height, border included
 * (border-box), and centres its content in it. The inner row stretches to fill
 * the tier and carries no vertical padding of its own: the density padding on
 * top of a 40px control (ListControls search field and icon buttons) added up
 * to 40 + 2 x 6 + 1 border = 53px, one pixel over the 52px `md` tier, so a bar
 * with search and sort was taller than one without and everything below it
 * jumped when the tab changed. Content taller than the tier still grows the bar. */
.cbui-bar-toolbar-tiered {
    display: flex;
    flex-direction: column;
}
.cbui-bar-toolbar-tiered > .cbui-bar-inner {
    flex: 1 1 auto;
    padding-block: 0;
}

/* ── Underline Tabnav in a slot is flush (CB-2913) ───────────────────────
 * An underline Tabnav in a Toolbar slot (directly in it, or one wrapping element below)
 * is the bar: no vertical padding on the inner row, the slot and the Tabnav stretch to the
 * full row height, so the hover and active faces run from the top edge to the bottom edge
 * and the active indicator sits ON the bottom line. Only the underline variant is matched
 * (segmented, pill and the rest keep their breathing room). The selectors walk the bar own
 * child chain (depth 1 or 2 below the slot) and never use a descendant combinator, so a
 * Tabnav nested in a panel of another Tabnav cannot match.
 * Exactly one line at the bottom: with a bar divider the bar border is the line and the
 * Tabnav border width goes to 0 (a transparent border would still be 1px tall and leave the
 * indicator 1px above the line); without a divider the Tabnav track is the line. */
.cbui-bar-toolbar:has(> .cbui-bar-inner > * > .cbui-tabnav-underline, > .cbui-bar-inner > * > * > .cbui-tabnav-underline) > .cbui-bar-inner {
    padding-block: 0;
}
.cbui-bar-toolbar > .cbui-bar-inner > :has(> .cbui-tabnav-underline, > * > .cbui-tabnav-underline) {
    align-self: stretch;
}
.cbui-bar-toolbar > .cbui-bar-inner > * > :not([hidden]):has(> .cbui-tabnav-underline) {
    display: flex;
    align-self: stretch;
}
.cbui-bar-toolbar > .cbui-bar-inner > * > .cbui-tabnav-underline,
.cbui-bar-toolbar > .cbui-bar-inner > * > * > .cbui-tabnav-underline {
    display: flex;
    flex-direction: column;
    align-self: stretch;
}
.cbui-bar-toolbar > .cbui-bar-inner > * > .cbui-tabnav-underline > .cbui-tabnav-strip-wrapper,
.cbui-bar-toolbar > .cbui-bar-inner > * > * > .cbui-tabnav-underline > .cbui-tabnav-strip-wrapper {
    flex: 1 1 auto;
}
.cbui-bar-toolbar.cbui-bar-with-divider-bottom > .cbui-bar-inner > * > .cbui-tabnav-underline > .cbui-tabnav-strip-wrapper > .cbui-tabnav-strip,
.cbui-bar-toolbar.cbui-bar-with-divider-bottom > .cbui-bar-inner > * > .cbui-tabnav-underline > .cbui-tabnav-strip-wrapper > .cbui-tabnav-lead,
.cbui-bar-toolbar.cbui-bar-with-divider-bottom > .cbui-bar-inner > * > .cbui-tabnav-underline > .cbui-tabnav-strip-wrapper > .cbui-tabnav-trail,
.cbui-bar-toolbar.cbui-bar-with-divider-bottom > .cbui-bar-inner > * > * > .cbui-tabnav-underline > .cbui-tabnav-strip-wrapper > .cbui-tabnav-strip,
.cbui-bar-toolbar.cbui-bar-with-divider-bottom > .cbui-bar-inner > * > * > .cbui-tabnav-underline > .cbui-tabnav-strip-wrapper > .cbui-tabnav-lead,
.cbui-bar-toolbar.cbui-bar-with-divider-bottom > .cbui-bar-inner > * > * > .cbui-tabnav-underline > .cbui-tabnav-strip-wrapper > .cbui-tabnav-trail {
    border-bottom-width: 0;
}

/* ── No leading inset before an underline Tabnav (CB-2916) ────────────────
 * Matthias 2026-10-17: the space left of the underline tabs in a secondary menubar goes. When the
 * first element of the start slot is an underline Tabnav (directly, or one wrapping element
 * below), the inner row has no inset at the start: the first tab face begins at the bar edge and
 * its text keeps the tab own inner padding. Only the start side: the end slot keeps its inset on
 * the right edge. The extra classes in the selector (start slot, first-child) lift it above a
 * Bringer rule that sets padding-inline on the row, so no important flag is needed. */
.cbui-bar-toolbar > .cbui-bar-inner:has(> .cbui-bar-start > :first-child.cbui-tabnav-underline, > .cbui-bar-start > :first-child > .cbui-tabnav-underline) {
    padding-inline-start: 0;
}

/* ── Sticky positioning ──────────────────────────────────────────────────── */
.cbui-bar-toolbar-top.cbui-bar-toolbar-sticky {
    position: sticky;
    top: 0;
    z-index: 20;
}
.cbui-bar-toolbar-bottom.cbui-bar-toolbar-sticky {
    position: sticky;
    bottom: 0;
    z-index: 20;
    /* iOS safe area: add the bottom inset so the bar doesn't sit under the
       home indicator. The base padding still applies above the inset. */
    padding-bottom: env(safe-area-inset-bottom, 0);
}

/* Bottom-positioned toolbars need a slightly stronger upward separation
   than the simple 1px border-top, because the shadow casts downward and
   leaves the upper edge naked. The shadow on +shadow-lift is upward in
   this case so user can see the lift. */
.cbui-bar-toolbar-bottom.cbui-bar-mod-shadow-lift {
    box-shadow: 0 -4px 12px -2px rgb(0 0 0 / 0.08), 0 -2px 4px -2px rgb(0 0 0 / 0.04);
}

/* Inline toolbars must not introduce sticky behaviour. They live in normal
   flow inside a card / dialog / popover and should not pin. */
.cbui-bar-toolbar-inline {
    position: static;
}

/* ── Slot specifics ──────────────────────────────────────────────────────── */
.cbui-bar-toolbar-inner {
    /* Center always gets centered explicitly — if start/end are missing the
       inner row's auto-margin trick collapses. justify-content: space-between
       on the row plus margin-inline on the center sub-element does the trick. */
}

.cbui-bar-toolbar-center {
    justify-content: center;
    flex: 1 1 auto;
}

/* If only start + end (no center), the bar.css default already handles it.
   If only center, push it to centre by giving it auto-margins on both sides. */
.cbui-bar-toolbar-inner > .cbui-bar-toolbar-center:only-child {
    margin-inline: auto;
}

.cbui-bar-toolbar-title {
    margin: 0;
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--cbui-bar-text);
    white-space: nowrap;
}

/* Compact density changes padding only; the title keeps its 1 rem type (CB-2918). */

/* ── window-menubar — the menubar of a Bringer window (CB-1698, CB-2956) ───
 * `variant="window-menubar"` resolves to the class below via Toolbar's own
 * variant grammar (no template change: any unrecognized `_resolvedVariant`
 * becomes `cbui-bar-<variant>` verbatim, and no `cbui-bar-solid`). Canvas
 * background, full width, square corners — the bar sits flush with the
 * content it sits above, the same surface as the list below it. Values
 * measured against the Agent-Bringer's own menubar
 * (`agent-bringer-window.css:38-42,82,100-103`). Neutral tone only — a
 * window menubar does not carry a state tone.
 * A SECOND window-menubar that follows as a sibling (G-TB-64, CB-2956) gets
 * its own surface: canvas mixed 6 % towards the text colour. Chosen over a
 * tone because it holds in every theme (bg-subtle equals canvas in some
 * themes, bg-muted equals the border colour), cannot be forgotten by a
 * caller, and keeps the rule that a window menubar carries no state tone;
 * `~` instead of `+` so a non-bar node between the rows does not break it.
 * Placed in `window_actions`; the surrounding `.cbui-bringer-window-actions`
 * resets its own padding/border/background below so there is no double
 * inset (`bringer-window.css`). */
.cbui-bar-window-menubar {
    --bar-bg: var(--color-bg-canvas, var(--color-bg-base));
    width: 100%;
    border-radius: 0;
}
/* CBUI rule 15 (2026-10-29) — `plane="canvas"` on any Toolbar: the MAIN menubar of a
 * Bringer is tinted in every view, also where nothing below it is a recessed
 * ListDetail (overview, dashboard, kanban, stream), so switching tabs never flips
 * the tint. Same canvas token as `window-menubar` and as the sibling rule of a
 * recessed ListDetail. Opt-in on purpose: a detail area's own menubar stays white.
 * Specificity 0 (`:where`): a Bringer that sets `--bar-bg` itself still wins. */
:where(.cbui-bar-plane-canvas) {
    --bar-bg: var(--color-bg-canvas, var(--color-bg-base));
}
.cbui-bar-window-menubar ~ .cbui-bar-window-menubar {
    --bar-bg: color-mix(in oklab, var(--color-bg-canvas, var(--color-bg-base)) 94%, var(--color-text) 6%);
}
.cbui-bar-window-menubar > .cbui-bar-inner {
    padding-inline: 0.75rem;
}
/* CB-1724 — a narrow window carries more in `start` (area tabs + ListControls'
 * search/sort/filter) than fits once it has shrunk to its children's own
 * min-widths (the search field already floors at 11rem, list-controls.css).
 * Without containment that overflow renders PAST this flex item's own box —
 * visually on top of the fixed `end` slot's count chip (measured: the filter
 * icon sat on top of "Einträge"). Contain it in its own horizontal scroller
 * instead, same technique the Agent-Bringer Referenz uses for its sort row
 * (`agent-bringer-window.css` "Never clip a count") — the count in `end` is
 * never squeezed or overlapped, and nothing in `start` is ever hard-clipped,
 * only reachable by scroll once truly out of room. */
.cbui-bar-window-menubar > .cbui-bar-inner > .cbui-bar-start {
    flex-shrink: 1;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
}
.cbui-bar-window-menubar > .cbui-bar-inner > .cbui-bar-start::-webkit-scrollbar {
    display: none;
}

/* cbui/components/section */
/* cbui-section — content section with a heading row.
 *
 * Four heading treatments (variant), each visually distinct:
 *   default   — heading + bottom divider
 *   subtle    — quiet muted heading, no chrome
 *   prominent — heading on a tinted band + bottom border
 *   card      — fully bordered: tinted heading band + bordered body
 *
 * Tones work on every variant — they drive --cbui-section-accent, which the
 * variant rules below consume (divider colour / band tint / title colour).
 */

.cbui-section {
    --cbui-section-accent: var(--color-text-secondary);
    --cbui-section-band-bg: var(--color-bg-subtle);
    --cbui-section-band-border: var(--color-border);
    --cbui-section-heading-py: 0.5rem;
    --cbui-section-heading-px: 0;
    --cbui-section-heading-gap: 0.75rem;
    --cbui-section-body-py: 1rem;
    --cbui-section-body-px: 0;
    --cbui-section-radius: var(--radius-md, 0.5rem);
    --cbui-section-icon-size: 1rem;
    --cbui-section-title-fs: var(--font-size-md, 1rem);

    display: flex;
    flex-direction: column;
    color: var(--color-text);
}

/* ── Tones — set the accent colour + band tint. Variants consume them. ───── */
.cbui-section-tone-neutral { --cbui-section-accent: var(--color-text-secondary); }
.cbui-section-tone-primary { --cbui-section-accent: var(--color-primary); --cbui-section-band-bg: color-mix(in srgb, var(--color-primary) 11%, transparent); --cbui-section-band-border: color-mix(in srgb, var(--color-primary) 32%, transparent); }
.cbui-section-tone-success { --cbui-section-accent: var(--color-success); --cbui-section-band-bg: color-mix(in srgb, var(--color-success) 11%, transparent); --cbui-section-band-border: color-mix(in srgb, var(--color-success) 32%, transparent); }
.cbui-section-tone-warning { --cbui-section-accent: var(--color-warning); --cbui-section-band-bg: color-mix(in srgb, var(--color-warning) 11%, transparent); --cbui-section-band-border: color-mix(in srgb, var(--color-warning) 32%, transparent); }
.cbui-section-tone-danger  { --cbui-section-accent: var(--color-danger);  --cbui-section-band-bg: color-mix(in srgb, var(--color-danger)  11%, transparent); --cbui-section-band-border: color-mix(in srgb, var(--color-danger)  32%, transparent); }
.cbui-section-tone-info    { --cbui-section-accent: var(--color-info);    --cbui-section-band-bg: color-mix(in srgb, var(--color-info)    11%, transparent); --cbui-section-band-border: color-mix(in srgb, var(--color-info)    32%, transparent); }
.cbui-section-tone-accent  { --cbui-section-accent: var(--color-text);    --cbui-section-band-bg: var(--color-bg-muted); --cbui-section-band-border: var(--color-border-strong, var(--color-border)); }
.cbui-section-tone-muted   { --cbui-section-accent: var(--color-text-tertiary, var(--color-text-secondary)); }

/* ── Sizes ───────────────────────────────────────────────────────────────── */
.cbui-section-size-sm { --cbui-section-title-fs: var(--font-size-md, 1rem); --cbui-section-icon-size: 0.875rem; }
.cbui-section-size-md { --cbui-section-title-fs: var(--font-size-md, 1rem);     --cbui-section-icon-size: 1rem; }
.cbui-section-size-lg { --cbui-section-title-fs: var(--font-size-lg, 1.125rem); --cbui-section-icon-size: 1.125rem; }

/* ── Heading row ─────────────────────────────────────────────────────────── */
.cbui-section-heading {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-block: var(--cbui-section-heading-py);
    padding-inline: var(--cbui-section-heading-px);
    min-width: 0;
}
.cbui-section-collapsible .cbui-section-heading {
    cursor: pointer;
    user-select: none;
    transition: background-color 120ms ease;
}
.cbui-section-collapsible .cbui-section-heading:hover {
    background-color: color-mix(in srgb, currentColor 5%, transparent);
}
.cbui-section-collapsible .cbui-section-heading:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -2px;
}

.cbui-section-caret {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    color: var(--color-text-secondary);
    transition: transform 150ms ease;
}
.cbui-section.is-collapsed .cbui-section-caret { transform: rotate(-90deg); }

.cbui-section-icon {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--cbui-section-icon-size);
    height: var(--cbui-section-icon-size);
    color: var(--cbui-section-accent);
}

.cbui-section-heading-text {
    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
    min-width: 0;
    flex: 1 1 auto;
}
.cbui-section-title {
    margin: 0;
    font-size: var(--cbui-section-title-fs);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--color-text);
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.cbui-section-title :is(h1, h2, h3, h4, h5, h6) {
    margin: 0; font-size: inherit; font-weight: inherit; color: inherit; line-height: inherit;
}
.cbui-section-description {
    margin: 0;
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
    line-height: 1.4;
    /* CB-3181: the description wraps and shows its whole text. It used to be one line ending in an ellipsis, which cut
       the sentence of every Section; the actions keep their width (flex-shrink 0), so a long text wraps under the title. */
    overflow-wrap: anywhere;
}
.cbui-section-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--cbui-action-gap, 0.625rem);
    margin-inline-start: auto;
    flex-shrink: 0;
}

/* ── Body / Footer ───────────────────────────────────────────────────────── */
.cbui-section-body {
    padding-block: var(--cbui-section-body-py);
    padding-inline: var(--cbui-section-body-px);
}
.cbui-section-footer {
    padding-block: 0.75rem;
    padding-inline: var(--cbui-section-body-px);
    border-block-start: 1px solid var(--color-border);
    color: var(--color-text-secondary);
    font-size: var(--font-size-md, 1rem);
}
.cbui-section-body-flush   .cbui-section-body { padding: 0; }
.cbui-section-body-flush-x .cbui-section-body { padding-inline: 0; }
.cbui-section-body-flush-y .cbui-section-body { padding-block: 0; }

/* Heading → body gap. Owned by a margin on the body (NOT body padding), so it
 * survives bodyFlush — which only zeroes the body's own padding for an
 * edge-to-edge Table / ListGroup. Applied only when a heading is actually
 * rendered (cbui-section-headed); opt out with headingFlush. */
.cbui-section-headed .cbui-section-body {
    padding-block-start: 0;
    margin-block-start: var(--cbui-section-heading-gap);
}
.cbui-section-heading-flush.cbui-section-headed .cbui-section-body {
    margin-block-start: 0;
}
/* A body that starts with a list of rows (List, ListGroup, Nav) needs no heading gap: the list's own inset plus the
 * row padding already put the first row's text 12px under the heading, the same rhythm as the heading padding.
 * With the gap it sat 24px down (Matthias 2026-10-30: "Padding-Top im Body der Box zu viel"). */
.cbui-section-headed .cbui-section-body:has(> :is(.cbui-list, .cbui-list-group, .cbui-nav):first-child:not(.cbui-list-bordered)),
.cbui-section-headed .cbui-section-body:has(> .cbui-stack:first-child > :is(.cbui-list, .cbui-list-group, .cbui-nav):first-child:not(.cbui-list-bordered)) {
    margin-block-start: 0;
}

/* ── Variant: default — heading + bottom divider ─────────────────────────── */
.cbui-section-default .cbui-section-heading {
    border-block-end: 1px solid var(--cbui-section-band-border);
}
.cbui-section-default.cbui-section-tone-neutral .cbui-section-heading {
    border-block-end-color: var(--color-border);
}
.cbui-section-default .cbui-section-title { color: var(--color-text); }

/* ── Variant: subtle — quiet muted heading, no chrome ────────────────────── */
.cbui-section-subtle { --cbui-section-title-fs: var(--font-size-md, 1rem); }
.cbui-section-subtle .cbui-section-heading { --cbui-section-heading-py: 0.375rem; }
.cbui-section-subtle .cbui-section-title {
    font-weight: var(--font-weight-medium, 500);
    color: var(--color-text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: var(--font-size-md, 1rem);
}
.cbui-section-subtle:not(.cbui-section-tone-neutral) .cbui-section-title { color: var(--cbui-section-accent); }
.cbui-section-subtle .cbui-section-body { --cbui-section-body-py: 0.5rem; }
.cbui-section-subtle { --cbui-section-heading-gap: 0.5rem; }

/* ── Variant: prominent — heading band + bottom border ───────────────────── */
.cbui-section-prominent { --cbui-section-title-fs: var(--font-size-lg, 1.125rem); }
.cbui-section-prominent .cbui-section-heading {
    --cbui-section-heading-px: 1rem;
    --cbui-section-heading-py: 0.75rem;
    background-color: var(--cbui-section-band-bg);
    border-block-end: 1px solid var(--cbui-section-band-border);
    border-start-start-radius: var(--cbui-section-radius);
    border-start-end-radius: var(--cbui-section-radius);
}
.cbui-section-prominent:not(.cbui-section-tone-neutral) .cbui-section-title {
    color: var(--cbui-section-accent);
}
.cbui-section-prominent .cbui-section-body { --cbui-section-body-px: 1rem; }

/* ── Variant: card — fully bordered ──────────────────────────────────────── */
.cbui-section-card {
    border: 1px solid var(--cbui-section-band-border);
    border-radius: var(--cbui-section-radius);
    background-color: var(--color-surface, var(--color-bg-elevated));
    /* `clip` not `hidden` (round 5, ui-kanban swim-lane composition,
     * Matthias 2026-09-23): both clip the rounded corners identically, but
     * `hidden` also makes this element a scroll container, which breaks
     * `position: sticky` on any descendant (the heading, when a consumer
     * needs it to stick while the card's OWN content scrolls past — see
     * KanbanSwimlane). `clip` clips the same way without becoming a
     * scrollport. */
    overflow: clip;
}
/* Dashboard rows (Matthias 2026-10-29): in an equal-height grid row every card is as tall as the tallest one; the
 * body takes the free height so the footer of every card sits at the bottom edge, on one line across the row. */
.cbui-section-card {
    display: flex;
    flex-direction: column;
}
.cbui-section-card > .cbui-section-body { flex: 1 1 auto; }
.cbui-section-card > .cbui-section-footer { margin-block-start: auto; }
.cbui-section-card .cbui-section-heading {
    --cbui-section-heading-px: 1rem;
    --cbui-section-heading-py: 0.75rem;
    background-color: var(--cbui-section-band-bg);
    border-block-end: 1px solid var(--cbui-section-band-border);
}
/* A control in a card head (segmented Tabnav, sm buttons, up to ~34px) must not make that head taller than the heads of
 * its neighbours: the actions slot gives back up to 6px above and below (Matthias 2026-10-29, identical dashboard heads). */
.cbui-section-card .cbui-section-heading > .cbui-section-actions { margin-block: -0.375rem; }
/* Collapsed card: the divider between heading and body is meaningless once
 * there is no body showing — drop it (round 5, Matthias: "eingeklappt →
 * Trennlinie weg"). Transparent, not `border: 0`, so the heading's own
 * padding/height never shifts on collapse. */
.cbui-section-card.is-collapsed .cbui-section-heading {
    border-block-end-color: transparent;
}
.cbui-section-card:not(.cbui-section-tone-neutral) .cbui-section-title {
    color: var(--cbui-section-accent);
}
.cbui-section-card .cbui-section-body   { --cbui-section-body-px: 1rem; }
.cbui-section-card .cbui-section-footer { padding-inline: 1rem; }

/* When the body is flush in prominent / card, drop the side padding too. */
.cbui-section-body-flush.cbui-section-prominent .cbui-section-body,
.cbui-section-body-flush.cbui-section-card .cbui-section-body { padding-inline: 0; }

/* ── A11y ────────────────────────────────────────────────────────────────── */
.a11y-reduced-motion .cbui-section-caret,
.a11y-reduced-motion .cbui-section-heading { transition: none; }
.a11y-high-contrast .cbui-section-default .cbui-section-heading,
.a11y-high-contrast .cbui-section-prominent .cbui-section-heading,
.a11y-high-contrast .cbui-section-card { border-color: var(--color-border-strong); }

/* cbui/components/secret-input */
/* cbui-secret-input — Input + reveal / copy buttons in the
 * padding-inline-end gutter.
 *
 * Pattern (matches NumberInput's "stepper in the padding" approach):
 *   - wrapper carries position-context
 *   - input reserves room on the end via padding-inline-end
 *   - actions are absolute-positioned to the inset-block:1px /
 *     inset-inline-end:1px corner (inside the input's 1px border)
 *
 * Action buttons follow touch-target sizing (≥ 2.5rem at md). Icon-only
 * variant (`+icon-only`) drops the tinted background so the buttons read
 * as pure glyphs hovering over the input chrome.
 */

@layer components {
  .cbui-secret-input {
    --cbui-secret-input-action-w: 2.5rem;
    --cbui-secret-input-glyph: 1rem;
    --cbui-secret-input-pad-x:  var(--input-padding-x-md);

    /* Action-button surface tokens — flavors can override these to opt
       into the icon-only treatment globally without each call-site
       passing `:iconOnly="true"`. Aero does so in variants/aero.css. */
    --cbui-secret-input-action-bg:        color-mix(in srgb, currentColor 7%, transparent);
    --cbui-secret-input-action-hover-bg:  color-mix(in srgb, currentColor 14%, transparent);
    --cbui-secret-input-action-active-bg: color-mix(in srgb, currentColor 22%, transparent);
    --cbui-secret-input-action-divider:   1px solid var(--color-border);

    position: relative;
    display: inline-flex;
    width: 100%;
    min-width: 0;
    font-variant-numeric: tabular-nums;
  }

  .cbui-secret-input-size-sm { --cbui-secret-input-action-w: 2.125rem; --cbui-secret-input-glyph: 0.875rem; --cbui-secret-input-pad-x: var(--input-padding-x-sm); }
  .cbui-secret-input-size-md { --cbui-secret-input-action-w: 2.5rem;   --cbui-secret-input-glyph: 1rem;     --cbui-secret-input-pad-x: var(--input-padding-x-md); }
  .cbui-secret-input-size-lg { --cbui-secret-input-action-w: 2.875rem; --cbui-secret-input-glyph: 1.125rem; --cbui-secret-input-pad-x: var(--input-padding-x-lg); }

  /* Reserve end-padding for N buttons + the inner gap. */
  .cbui-secret-input-buttons-1 > .cbui-input {
    padding-inline-end: calc(var(--cbui-secret-input-action-w) + var(--cbui-secret-input-pad-x));
  }
  .cbui-secret-input-buttons-2 > .cbui-input {
    padding-inline-end: calc(var(--cbui-secret-input-action-w) * 2 + var(--cbui-secret-input-pad-x));
  }

  .cbui-secret-input-actions {
    position: absolute;
    inset-block: 1px;
    inset-inline-end: 1px;
    display: inline-flex;
    align-items: stretch;
    pointer-events: auto;
    border-start-end-radius: calc(var(--input-radius) - 1px);
    border-end-end-radius:   calc(var(--input-radius) - 1px);
    overflow: hidden;
  }

  .cbui-secret-input:has(> .cbui-input-filled) .cbui-secret-input-actions {
    inset-block-start: 0;
    inset-block-end:   2px;
    inset-inline-end:  0;
    border-start-end-radius: var(--input-radius);
    border-end-end-radius:   0;
  }

  .cbui-secret-input:has(> .cbui-input-flush) .cbui-secret-input-actions {
    inset-block-start: 0;
    inset-block-end:   1px;
    inset-inline-end:  0;
    border-radius: 0;
  }

  .a11y-high-contrast .cbui-secret-input:has(> .cbui-input-default) .cbui-secret-input-actions {
    inset-block: 2px;
    inset-inline-end: 2px;
    border-start-end-radius: calc(var(--input-radius) - 2px);
    border-end-end-radius:   calc(var(--input-radius) - 2px);
  }

  /* Action button base — reads from the action-* tokens so flavors can
     opt into icon-only by overriding the tokens once on the host. */
  .cbui-secret-input-action {
    appearance: none;
    background: var(--cbui-secret-input-action-bg);
    border: 0;
    margin: 0;
    padding: 0;
    width: var(--cbui-secret-input-action-w);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-secondary);
    cursor: pointer;
    line-height: 0;
    transition: background-color 120ms ease, color 120ms ease;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }

  .cbui-secret-input-action:hover {
    background-color: var(--cbui-secret-input-action-hover-bg);
    color: var(--color-text);
  }

  .cbui-secret-input-action:active {
    background-color: var(--cbui-secret-input-action-active-bg);
  }

  .cbui-secret-input-action:focus-visible {
    outline: 2px solid var(--color-focus-ring, var(--color-primary));
    outline-offset: -2px;
    z-index: 1;
  }

  .cbui-secret-input-action:disabled,
  .cbui-secret-input-action[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
  }

  /* Icon-only variant — buttons drop their tinted bg, hover bumps a soft
     currentColor wash so the affordance is still discoverable. Achieved
     by overriding the action-* tokens; the rest of the styling cascades. */
  .cbui-secret-input-icon-only {
    --cbui-secret-input-action-bg:        transparent;
    --cbui-secret-input-action-hover-bg:  color-mix(in srgb, currentColor 8%, transparent);
    --cbui-secret-input-action-active-bg: color-mix(in srgb, currentColor 16%, transparent);
    --cbui-secret-input-action-divider:   0;
  }

  /* Separator between the two buttons when both are present. */
  .cbui-secret-input-buttons-2 .cbui-secret-input-reveal {
    border-inline-end: var(--cbui-secret-input-action-divider);
  }

  /* Disabled / readonly cascade — actions inherit the input's state. */
  .cbui-secret-input:has(> .cbui-input:disabled) .cbui-secret-input-actions,
  .cbui-secret-input:has(> .cbui-input-disabled)  .cbui-secret-input-actions,
  .cbui-secret-input:has(> .cbui-input:read-only) .cbui-secret-input-actions,
  .cbui-secret-input:has(> .cbui-input-readonly)  .cbui-secret-input-actions {
    opacity: 0.5;
    pointer-events: none;
  }

  /* On-demand: the masked dots are the literal `value` attribute of a
     type=password input, so the field reads as filled before any reveal
     fetch. The browser renders password dots regardless of the actual
     string, so the user sees "••••••••" without us hard-coding bullet
     glyphs. */

  /* Glyphs — painted via mask-image like NumberInput / ColorInput. */
  .cbui-secret-input-glyph {
    display: block;
    width: var(--cbui-secret-input-glyph);
    height: var(--cbui-secret-input-glyph);
    background-color: currentColor;
    -webkit-mask: no-repeat center / contain;
            mask: no-repeat center / contain;
  }
  .cbui-secret-input-glyph[hidden] { display: none; }

  /* eye */
  .cbui-secret-input-glyph-eye {
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 4C4.5 4 1.7 6.4 1 8c.7 1.6 3.5 4 7 4s6.3-2.4 7-4c-.7-1.6-3.5-4-7-4Z' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'/><circle cx='8' cy='8' r='1.8' fill='black'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 4C4.5 4 1.7 6.4 1 8c.7 1.6 3.5 4 7 4s6.3-2.4 7-4c-.7-1.6-3.5-4-7-4Z' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'/><circle cx='8' cy='8' r='1.8' fill='black'/></svg>");
  }
  /* eye-off */
  .cbui-secret-input-glyph-eye-off {
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 4C4.5 4 1.7 6.4 1 8c.7 1.6 3.5 4 7 4s6.3-2.4 7-4c-.7-1.6-3.5-4-7-4Z' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'/><circle cx='8' cy='8' r='1.8' fill='black'/><line x1='2' y1='13.5' x2='14' y2='2.5' stroke='black' stroke-width='1.6' stroke-linecap='round'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 4C4.5 4 1.7 6.4 1 8c.7 1.6 3.5 4 7 4s6.3-2.4 7-4c-.7-1.6-3.5-4-7-4Z' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'/><circle cx='8' cy='8' r='1.8' fill='black'/><line x1='2' y1='13.5' x2='14' y2='2.5' stroke='black' stroke-width='1.6' stroke-linecap='round'/></svg>");
  }
  /* copy */
  .cbui-secret-input-glyph-copy {
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='5' width='8' height='9' rx='1.5' fill='none' stroke='black' stroke-width='1.4'/><path d='M5 5V3.5A1.5 1.5 0 0 1 6.5 2H12a1.5 1.5 0 0 1 1.5 1.5V9' fill='none' stroke='black' stroke-width='1.4'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='5' width='8' height='9' rx='1.5' fill='none' stroke='black' stroke-width='1.4'/><path d='M5 5V3.5A1.5 1.5 0 0 1 6.5 2H12a1.5 1.5 0 0 1 1.5 1.5V9' fill='none' stroke='black' stroke-width='1.4'/></svg>");
  }
  /* check (copy-success) */
  .cbui-secret-input-glyph-check {
    color: var(--color-success);
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><polyline points='3 8.5 6.5 12 13 4.5' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><polyline points='3 8.5 6.5 12 13 4.5' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  }

  /* Width control. */
  .cbui-secret-input-w-full { width: 100%; }
  .cbui-secret-input-w-auto { width: auto; }
}

/* cbui/components/section-nav */
/* cbui-section-nav — generic section navigation for long detail views and
 * multi-step create flows.
 *
 * Anatomy:
 *   .cbui-section-nav                 root (variant class: -side / -stepper / -menu)
 *     .cbui-section-nav-primary         (side) wraps the <Nav> of <NavItem>
 *     .cbui-section-nav-menu            (side) the "Gehe zu" <Dropdown>, hidden
 *                                        until the strip is too narrow even for
 *                                        icon-only labels
 *     .cbui-section-nav-wizard-toolbar  (stepper + mode=wizard) Zurück/Weiter row
 *
 * Two collapse stages, driven by the ResizeObserver in
 * section-nav_controller.js (never @media — the host is often a draggable
 * window pane, not the viewport):
 *   .cbui-section-nav-labels-collapsed        NavItem labels hidden, icon only
 *   .cbui-section-nav-collapsed-to-menu       Nav hidden entirely, Dropdown shown
 *
 * CB-1154: `sticky` (pins the component's OWN root, independent of Nav's own
 * `sticky` prop — see the component docblock) and `tone="plain"` (a quiet
 * settings-list look; the tone-specific active/marker treatment lives in
 * nav.css / navbar.css alongside the other tones, `.cbui-nav-tone-plain` —
 * the state-marker dampening below is SectionNav-specific, keyed off
 * `.cbui-section-nav-tone-plain` on this component's own root).
 *
 * Tokens fall back exclusively to EXISTING tokens (--cbui-nav-*,
 * --cbui-stepper-*, --color-*, --radius-*, --font-size-*) — no new flavor
 * token family (see plan W3).
 */

@layer components {
  :where(.cbui-section-nav) {
    --cbui-section-nav-gap: var(--spacing-2, 0.5rem);
  }

  .cbui-section-nav {
    display: block;
    min-width: 0;
  }

  /* CB-1154: pins the WHOLE component (not just the inner Nav — see
   * docblock) to the top of its scroll container, with its own scroll once
   * there are more sections than fit. `align-self: start` matters when the
   * host lays the component out in a flex/grid row (the agent config
   * column) — without it a sticky child stretched to `align-items: stretch`
   * never actually detaches from the row's height. */
  .cbui-section-nav-sticky {
    position: sticky;
    top: 0;
    align-self: start;
    max-height: 100cqh;
    overflow-y: auto;
  }

  /* ── variant=side ──────────────────────────────────────────────────── */

  .cbui-section-nav-side .cbui-section-nav-primary {
    display: block;
    min-width: 0;
  }

  .cbui-section-nav-side .cbui-section-nav-menu {
    display: none;
  }

  /* Stage 2: the strip cannot even hold icon-only labels any more — swap
   * to the "Gehe zu" dropdown entirely. */
  .cbui-section-nav-side.cbui-section-nav-collapsed-to-menu .cbui-section-nav-primary {
    display: none;
  }

  .cbui-section-nav-side.cbui-section-nav-collapsed-to-menu .cbui-section-nav-menu {
    display: block;
  }

  /* Stage 1: labels hidden, icon remains. The label text stays in the DOM
   * (sr-only clip, not display:none) so the accessible name survives even
   * where the item carries no separate aria-label. */
  .cbui-section-nav-side.cbui-section-nav-labels-collapsed .cbui-nav-item-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .cbui-section-nav-side.cbui-section-nav-labels-collapsed .cbui-nav-item-description {
    display: none;
  }

  /* State markers on the side variant's NavItem rows. */
  .cbui-section-nav-item-complete .cbui-nav-item-icon-end {
    color: var(--color-success);
  }

  .cbui-section-nav-item-error .cbui-nav-item-icon-end {
    color: var(--color-danger);
  }

  /* `editing` (CB-1154) is not pass/fail like complete/error — a quiet,
   * secondary-coloured pencil, not a semantic tone. */
  .cbui-section-nav-item-editing .cbui-nav-item-icon-end {
    color: var(--color-text-secondary);
  }

  /* tone="plain" (CB-1154): the whole point is a quiet list — dampen the
   * state markers instead of letting success/danger read at full
   * saturation next to a chrome-less active bar. */
  .cbui-section-nav-tone-plain .cbui-section-nav-item-complete .cbui-nav-item-icon-end,
  .cbui-section-nav-tone-plain .cbui-section-nav-item-error .cbui-nav-item-icon-end,
  .cbui-section-nav-tone-plain .cbui-section-nav-item-editing .cbui-nav-item-icon-end {
    opacity: 0.65;
  }

  /* ── variant=stepper ───────────────────────────────────────────────── */

  .cbui-section-nav-stepper [data-cbui-section-nav-id] {
    cursor: pointer;
  }

  .cbui-section-nav-wizard-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--cbui-section-nav-gap);
    margin-block-start: var(--spacing-3, 0.75rem);
  }

  /* ── variant=menu (+ side's collapsed-to-menu) ────────────────────── */

  .cbui-section-nav-item-current {
    font-weight: var(--font-weight-medium, 500);
    background: var(--color-bg-subtle, transparent);
  }
}

/* cbui/components/progress-bar */
/* cbui-progress-bar — generic progress indicator.
 *
 * Determinate: fill width driven by `--cbui-progress-bar-pct` on the inner
 * `.cbui-progress-bar-fill`. Indeterminate: same fill is animated into a
 * sliding 40%-wide block that loops left→right (always at least partially
 * visible — keyframes never park it off-screen).
 *
 * Stacked: instead of one .cbui-progress-bar-fill, several
 * .cbui-progress-bar-segment children sit side by side inside the same
 * track, each carrying its own tone. The remaining area stays the empty
 * track. The legend below the bar is optional (`legend=true`) — when
 * present it lists each segment's swatch + label + value in a wrap-flex
 * row so it adapts to narrow viewports.
 *
 * Sizes (track-height):
 *   xs = 4px, sm = 6px, md = 10px, lg = 14px.
 */

@layer components {
  .cbui-progress-bar {
    --cbui-progress-bar-track-h:    0.625rem;        /* md */
    --cbui-progress-bar-track-bg:   var(--color-bg-muted);
    --cbui-progress-bar-radius:     9999px;
    --cbui-progress-bar-fill:       var(--color-primary);
    --cbui-progress-bar-label-fs:   var(--font-size-md, 1rem);
    --cbui-progress-bar-stripe-size:0.75rem;
    --cbui-progress-bar-segment-divider: 1px;

    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    width: 100%;
    min-width: 0;
    color: var(--color-text-secondary);
    font-size: var(--cbui-progress-bar-label-fs);
    font-variant-numeric: tabular-nums;
  }

  .cbui-progress-bar-size-xs { --cbui-progress-bar-track-h: 0.25rem;  }
  .cbui-progress-bar-size-sm { --cbui-progress-bar-track-h: 0.375rem; }
  .cbui-progress-bar-size-md { --cbui-progress-bar-track-h: 0.625rem; }
  .cbui-progress-bar-size-lg { --cbui-progress-bar-track-h: 0.875rem; }

  /* Top row: bar + inline label. */
  .cbui-progress-bar-row {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    min-width: 0;
    width: 100%;
  }

  .cbui-progress-bar-track {
    flex: 1 1 0;
    min-width: 0;
    height: var(--cbui-progress-bar-track-h);
    background-color: var(--cbui-progress-bar-track-bg);
    border-radius: var(--cbui-progress-bar-radius);
    overflow: hidden;
    position: relative;
    display: flex;
    align-items: stretch;
  }

  /* Single-fill mode. */
  .cbui-progress-bar-fill {
    height: 100%;
    width: var(--cbui-progress-bar-pct, 0%);
    min-width: 0;
    background-color: var(--cbui-progress-bar-fill);
    border-radius: inherit;
    transition: width 240ms ease;
  }

  /* Stacked-segments mode — segments sit in a row, hairline divider
     between them to make the borders read crisp even on touching same-
     family hues. */
  .cbui-progress-bar-segment {
    height: 100%;
    width: var(--cbui-progress-bar-seg-pct, 0%);
    min-width: 0;
    background-color: var(--cbui-progress-bar-fill);
    transition: width 240ms ease;
  }
  .cbui-progress-bar-segment + .cbui-progress-bar-segment {
    border-inline-start: var(--cbui-progress-bar-segment-divider) solid
      color-mix(in srgb, var(--color-bg-base) 70%, transparent);
  }

  .cbui-progress-bar-label {
    flex: 0 0 auto;
    white-space: nowrap;
    color: var(--color-text-secondary);
  }

  /* Legend below the bar. */
  .cbui-progress-bar-legend {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    font-size: var(--font-size-md, 1rem);
  }
  .cbui-progress-bar-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    color: var(--color-text-secondary);
  }
  .cbui-progress-bar-legend-swatch {
    display: inline-block;
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 0.1875rem;
    background-color: var(--cbui-progress-bar-fill);
    flex: 0 0 auto;
  }
  .cbui-progress-bar-legend-label {
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
  }
  .cbui-progress-bar-legend-value {
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
  }

  /* Table legend: swatch | label | amount | share, the two value columns right-aligned. */
  .cbui-progress-bar-legend-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--font-size-md, 1rem);
    font-variant-numeric: tabular-nums;
  }
  .cbui-progress-bar-legend-table th,
  .cbui-progress-bar-legend-table td {
    padding: 0.25rem 0;
    text-align: end;
    white-space: nowrap;
    color: var(--color-text-tertiary);
    font-weight: var(--font-weight-normal);
  }
  .cbui-progress-bar-legend-table td + td { padding-inline-start: 1rem; }
  .cbui-progress-bar-legend-table tbody th {
    text-align: start;
    width: 100%;
    white-space: normal;
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
  }
  .cbui-progress-bar-legend-table thead th {
    color: var(--color-text-secondary);
    border-block-end: 1px solid var(--color-border);
  }
  .cbui-progress-bar-legend-table thead th:first-child { text-align: start; }
  .cbui-progress-bar-legend-table thead th + th { padding-inline-start: 1rem; }
  .cbui-progress-bar-legend-table td.cbui-progress-bar-legend-table-swatch { width: 0; padding-inline-end: 0.5rem; }
  .cbui-progress-bar-legend-table-swatch .cbui-progress-bar-legend-swatch { vertical-align: middle; }

  /* Inline-legend variant: legend sits on the same row as the bar. */
  .cbui-progress-bar-legend-inline {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }
  .cbui-progress-bar-legend-inline .cbui-progress-bar-row { flex: 1 1 60%; }
  .cbui-progress-bar-legend-inline .cbui-progress-bar-legend { flex: 1 1 auto; }

  /* ── Semantic tones ─────────────────────────────────────────────── */
  .cbui-progress-bar-tone-primary  { --cbui-progress-bar-fill: var(--color-primary); }
  .cbui-progress-bar-tone-success  { --cbui-progress-bar-fill: var(--color-success); }
  .cbui-progress-bar-tone-warning  { --cbui-progress-bar-fill: var(--color-warning); }
  .cbui-progress-bar-tone-danger   { --cbui-progress-bar-fill: var(--color-danger);  }
  .cbui-progress-bar-tone-info     { --cbui-progress-bar-fill: var(--color-info);    }
  .cbui-progress-bar-tone-muted    { --cbui-progress-bar-fill: var(--color-text-tertiary); }
  .cbui-progress-bar-tone-neutral  { --cbui-progress-bar-fill: var(--color-text);    }
  .cbui-progress-bar-tone-accent   { --cbui-progress-bar-fill: var(--color-accent, var(--color-primary)); }

  /* ── Categorical tones (16 hues — same vocabulary as Badge) ────── */
  .cbui-progress-bar-tone-orange   { --cbui-progress-bar-fill: var(--color-orange);  }
  .cbui-progress-bar-tone-amber    { --cbui-progress-bar-fill: var(--color-amber);   }
  .cbui-progress-bar-tone-yellow   { --cbui-progress-bar-fill: var(--color-yellow);  }
  .cbui-progress-bar-tone-lime     { --cbui-progress-bar-fill: var(--color-lime);    }
  .cbui-progress-bar-tone-green    { --cbui-progress-bar-fill: var(--color-green);   }
  .cbui-progress-bar-tone-emerald  { --cbui-progress-bar-fill: var(--color-emerald); }
  .cbui-progress-bar-tone-teal     { --cbui-progress-bar-fill: var(--color-teal);    }
  .cbui-progress-bar-tone-cyan     { --cbui-progress-bar-fill: var(--color-cyan);    }
  .cbui-progress-bar-tone-sky      { --cbui-progress-bar-fill: var(--color-sky);     }
  .cbui-progress-bar-tone-blue     { --cbui-progress-bar-fill: var(--color-blue);    }
  .cbui-progress-bar-tone-indigo   { --cbui-progress-bar-fill: var(--color-indigo);  }
  .cbui-progress-bar-tone-violet   { --cbui-progress-bar-fill: var(--color-violet);  }
  .cbui-progress-bar-tone-purple   { --cbui-progress-bar-fill: var(--color-purple);  }
  .cbui-progress-bar-tone-fuchsia  { --cbui-progress-bar-fill: var(--color-fuchsia); }
  .cbui-progress-bar-tone-pink     { --cbui-progress-bar-fill: var(--color-pink);    }
  .cbui-progress-bar-tone-rose     { --cbui-progress-bar-fill: var(--color-rose);    }

  /* Striped variant — diagonal hatching on every fill / segment. */
  .cbui-progress-bar-striped .cbui-progress-bar-fill,
  .cbui-progress-bar-striped-animated .cbui-progress-bar-fill,
  .cbui-progress-bar-striped .cbui-progress-bar-segment,
  .cbui-progress-bar-striped-animated .cbui-progress-bar-segment {
    background-image: linear-gradient(
      45deg,
      color-mix(in srgb, #fff 18%, transparent) 25%,
      transparent 25%, transparent 50%,
      color-mix(in srgb, #fff 18%, transparent) 50%,
      color-mix(in srgb, #fff 18%, transparent) 75%,
      transparent 75%, transparent
    );
    background-size: var(--cbui-progress-bar-stripe-size) var(--cbui-progress-bar-stripe-size);
  }

  .cbui-progress-bar-striped-animated .cbui-progress-bar-fill,
  .cbui-progress-bar-striped-animated .cbui-progress-bar-segment {
    animation: cbui-progress-bar-stripes 1s linear infinite;
  }

  @keyframes cbui-progress-bar-stripes {
    from { background-position: 0 0; }
    to   { background-position: var(--cbui-progress-bar-stripe-size) 0; }
  }

  /* Indeterminate — sliding 40%-wide block. The keyframes are designed
     so the bar is ALWAYS at least partially visible inside the track;
     parking it fully off-screen would leave a blank frame between loops. */
  .cbui-progress-bar-indeterminate .cbui-progress-bar-fill {
    width: 40%;
    animation: cbui-progress-bar-slide 1.6s ease-in-out infinite;
    transition: none;
  }

  @keyframes cbui-progress-bar-slide {
    0%   { transform: translateX(-60%); }
    50%  { transform: translateX(160%); }
    100% { transform: translateX(-60%); }
  }

  /* Reduced motion — drop the slide / stripe animations. */
  @media (prefers-reduced-motion: reduce) {
    .cbui-progress-bar-indeterminate .cbui-progress-bar-fill,
    .cbui-progress-bar-striped-animated .cbui-progress-bar-fill,
    .cbui-progress-bar-striped-animated .cbui-progress-bar-segment {
      animation: none;
    }
    .cbui-progress-bar-indeterminate .cbui-progress-bar-fill {
      width: 100%;
      opacity: 0.6;
      transform: none;
    }
  }
  .a11y-reduced-motion .cbui-progress-bar-indeterminate .cbui-progress-bar-fill,
  .a11y-reduced-motion .cbui-progress-bar-striped-animated .cbui-progress-bar-fill,
  .a11y-reduced-motion .cbui-progress-bar-striped-animated .cbui-progress-bar-segment {
    animation: none;
  }
  .a11y-reduced-motion .cbui-progress-bar-indeterminate .cbui-progress-bar-fill {
    width: 100%;
    opacity: 0.6;
    transform: none;
  }

  /* High contrast — track gets a 1px text-coloured outline so the bar
     reads as a defined frame against any surface. */
  .a11y-high-contrast .cbui-progress-bar-track {
    box-shadow: inset 0 0 0 1px var(--color-text);
    background-color: transparent;
  }
  .a11y-high-contrast .cbui-progress-bar-tone-muted { --cbui-progress-bar-fill: var(--color-text); }

  /* Width control. */
  .cbui-progress-bar-w-full { width: 100%; }
  .cbui-progress-bar-w-auto { width: auto; }
}

/* cbui/components/breadcrumb */
/* cbui-breadcrumb — a horizontal path of clickable ancestor segments ending in
 * a non-link "current" segment. Configurable separator + overflow collapse.
 *
 * Anatomy:
 *   .cbui-breadcrumb              the <nav> landmark (aria-label)
 *     .cbui-breadcrumb-leading    optional content before the first segment
 *     .cbui-breadcrumb-list       flex strip (role=list) holding the segments
 *       .cbui-breadcrumb-item      a single segment (<a> link or <span> text)
 *         .cbui-breadcrumb-item-icon   leading icon slot
 *         .cbui-breadcrumb-item-label  the label text
 *       .cbui-breadcrumb-collapse  overflow=menu host (wraps the "…" Dropdown)
 *         .cbui-breadcrumb-ellipsis  the "…" trigger button (reuses item seat)
 *
 * Separator discipline — the separator is NEVER markup inside a segment. It is
 * drawn by CSS as a `::before` glyph on every VISIBLE segment that has a
 * preceding visible sibling, so it can never be doubled and never leads the
 * first segment. The `:not([hidden]) ~ :not([hidden])` selector makes the
 * overflow=menu collapse (which hides middle segments) Just Work — the glyph
 * follows the first still-visible segment after a hidden run.
 *
 * Variants:  plain (quiet text links) · pill (hover chip seat)
 * Separator: chevron (default, RTL-mirrored) · slash · dot · arrow (RTL-mirrored)
 * Tones:     neutral (default) + primary/accent/success/danger/warning/info/muted
 */

@layer components {
  /* ── Tokens (overridable by flavor) ───────────────────────────────────── */
  :where(.cbui-breadcrumb) {
    --cbui-breadcrumb-gap:           0.125rem;
    --cbui-breadcrumb-item-gap:      0.375rem;
    --cbui-breadcrumb-item-px:       0.375rem;
    --cbui-breadcrumb-item-py:       0.1875rem;
    --cbui-breadcrumb-item-radius:   var(--radius-md, 0.375rem);
    --cbui-breadcrumb-icon-size:     1em;
    --cbui-breadcrumb-font-size:     var(--font-size-md, 1rem);
    --cbui-breadcrumb-sep-size:      1em;
    --cbui-breadcrumb-sep-gap:       0.4375rem;

    /* Colour model — links are muted, current is the full text colour, the
       separator is the faintest. Tones redefine --cbui-breadcrumb-accent. */
    --cbui-breadcrumb-text:          var(--color-text);
    --cbui-breadcrumb-link:          var(--color-text-secondary);
    --cbui-breadcrumb-link-hover:    var(--color-text);
    --cbui-breadcrumb-current:       var(--color-text);
    --cbui-breadcrumb-sep-color:     var(--color-text-tertiary);
    --cbui-breadcrumb-accent:        var(--color-text);
    --cbui-breadcrumb-pill-hover:    color-mix(in srgb, currentColor 8%, transparent);
    --cbui-breadcrumb-pill-active:   color-mix(in srgb, currentColor 12%, transparent);

    font-size: var(--cbui-breadcrumb-font-size);
    color: var(--cbui-breadcrumb-text);
    min-width: 0;
  }

  /* ── width ──────────────────────────────────────────────────────────────
   * The <nav> itself is the flex parent for the (optional) leading slot + the
   * segment list so they share one baseline row. width controls whether the
   * nav shrink-wraps (auto) or fills its container (full). */
  .cbui-breadcrumb-width-auto { display: inline-flex; align-items: center; max-width: 100%; }
  .cbui-breadcrumb-width-full { display: flex;        align-items: center; width: 100%; }

  /* ── list strip ───────────────────────────────────────────────────────── */
  .cbui-breadcrumb-list {
    display: flex;
    align-items: center;
    /* No list gap: the separator's own symmetric margin is the SOLE inter-segment
       spacer (see the separator rule). A list gap would land only on the left of
       each ::before and re-introduce the asymmetry this design avoids. */
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
    min-width: 0;
  }
  .cbui-breadcrumb-leading {
    display: inline-flex;
    align-items: center;
    margin-inline-end: var(--cbui-breadcrumb-gap);
    flex-shrink: 0;
  }

  /* align — distributes the list within the nav. With width=full the list is
     allowed to take the leftover space (so overflow=scroll can clip + scroll
     inside the container, and align can push a shrink-wrapped list around). */
  .cbui-breadcrumb-width-full .cbui-breadcrumb-list { flex: 1 1 auto; }
  .cbui-breadcrumb-align-start  { justify-content: flex-start; }
  .cbui-breadcrumb-align-center { justify-content: center; }
  .cbui-breadcrumb-align-end    { justify-content: flex-end; }
  /* center / end only make sense when the list is shrink-wrapped — release the
     grow so justify-content actually has slack to distribute against. */
  .cbui-breadcrumb-width-full.cbui-breadcrumb-align-center .cbui-breadcrumb-list,
  .cbui-breadcrumb-width-full.cbui-breadcrumb-align-end .cbui-breadcrumb-list { flex: 0 1 auto; }

  /* ── segment item ─────────────────────────────────────────────────────── */
  .cbui-breadcrumb-item {
    /* Flex container only: holds the parent-drawn ::before separator + the
       .cbui-breadcrumb-item-seat. No padding/gap here so the separator's own
       symmetric margins are the sole inter-segment spacing. The visible surface
       (radius, pill background, focus ring) lives on the seat — so the separator
       is NEVER inside a tinted current / hover background. */
    display: inline-flex;
    align-items: center;
    gap: 0;
    border: 0;
    background: transparent;
    color: var(--cbui-breadcrumb-link);
    font: inherit;
    line-height: 1.25;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
  }
  /* The visible/interactive surface — icon + label + the pill seat. */
  .cbui-breadcrumb-item-seat {
    display: inline-flex;
    align-items: center;
    gap: var(--cbui-breadcrumb-item-gap);
    min-width: 0;
    padding-block: var(--cbui-breadcrumb-item-py);
    border-radius: var(--cbui-breadcrumb-item-radius);
    transition: background-color 150ms ease, color 150ms ease;
  }
  a.cbui-breadcrumb-item:hover { color: var(--cbui-breadcrumb-link-hover); }
  a.cbui-breadcrumb-item:active { color: var(--cbui-breadcrumb-link-hover); }
  .cbui-breadcrumb-item:focus-visible { outline: none; }
  .cbui-breadcrumb-item:focus-visible .cbui-breadcrumb-item-seat {
    box-shadow:
      0 0 0 var(--focus-ring-offset, 2px) var(--color-bg-base, white),
      0 0 0 calc(var(--focus-ring-offset, 2px) + var(--focus-ring-width, 2px)) var(--color-focus-ring);
  }

  /* current segment — emphasised (NOT muted): full text colour, slightly
     heavier weight, and not interactive (it is a <span>). */
  .cbui-breadcrumb-item-current {
    color: var(--cbui-breadcrumb-current);
    font-weight: var(--font-weight-semibold, 600);
    cursor: default;
  }

  /* disabled segment — inert + muted. */
  .cbui-breadcrumb-item-disabled,
  .cbui-breadcrumb-item[aria-disabled="true"] {
    color: var(--color-text-tertiary);
    pointer-events: none;
    cursor: default;
  }

  /* The overflow=menu controller folds middle segments by setting [hidden] on
     them. The item's explicit display:inline-flex outranks the UA
     [hidden]{display:none}, so without this the folded segments would still
     render (mashed, separator-less) and never actually collapse. A class+attr
     selector wins the specificity battle. (Same trap as the dropdown [hidden]
     note in the UI guidelines.) */
  .cbui-breadcrumb-item[hidden] { display: none; }

  /* segment slots */
  .cbui-breadcrumb-item-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--cbui-breadcrumb-icon-size);
    height: var(--cbui-breadcrumb-icon-size);
    flex-shrink: 0;
  }
  .cbui-breadcrumb-item-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
  }

  /* ── Separator — parent-drawn, decorative, never doubled ──────────────────
   * `:not([hidden]) ~ :not([hidden])` = every visible strip child that has a
   * preceding visible sibling → the separator never leads the first segment
   * and survives the overflow=menu collapse (hidden middle segments are
   * skipped, so the glyph re-appears on the first still-visible tail segment).
   */
  .cbui-breadcrumb-list > :not([hidden]) ~ :not([hidden])::before {
    content: var(--cbui-breadcrumb-sep-glyph, "/");
    display: inline-flex;
    align-items: center;
    align-self: center;
    flex-shrink: 0;
    margin-inline: var(--cbui-breadcrumb-sep-gap);
    color: var(--cbui-breadcrumb-sep-color);
    font-size: var(--cbui-breadcrumb-sep-size);
    line-height: 1;
    user-select: none;
    pointer-events: none;
  }
  /* Separator glyphs per variant. chevron + arrow mirror in RTL. */
  .cbui-breadcrumb-sep-chevron { --cbui-breadcrumb-sep-glyph: "\203A"; } /* › */
  .cbui-breadcrumb-sep-slash   { --cbui-breadcrumb-sep-glyph: "/"; }
  .cbui-breadcrumb-sep-dot     { --cbui-breadcrumb-sep-glyph: "\2022"; } /* • */
  .cbui-breadcrumb-sep-arrow   { --cbui-breadcrumb-sep-glyph: "\2192"; } /* → */
  /* dot reads better a touch smaller + dimmer. */
  .cbui-breadcrumb-sep-dot .cbui-breadcrumb-list > :not([hidden]) ~ :not([hidden])::before {
    font-size: 0.625em;
  }
  /* RTL — flip the directional glyphs so the path still reads inward. */
  [dir="rtl"] .cbui-breadcrumb-sep-chevron .cbui-breadcrumb-list > :not([hidden]) ~ :not([hidden])::before { content: "\2039"; } /* ‹ */
  [dir="rtl"] .cbui-breadcrumb-sep-arrow   .cbui-breadcrumb-list > :not([hidden]) ~ :not([hidden])::before { content: "\2190"; } /* ← */

  /* ── Adornment — a control that BELONGS TO the segment before it ─────────
   * A strip child marked `.cbui-breadcrumb-adornment` draws no leading
   * separator and sits tight against its segment, so it reads as part of that
   * segment rather than as a step of its own. The segment AFTER it keeps its
   * separator (the `~` rule above is a general sibling combinator, so the
   * adornment does not interrupt the chain).
   *
   * Values uses it for the × that clears the active collection: with the ×
   * simply appended after the path it landed behind the KEY and looked like it
   * would clear the value (Matthias, 2026-08-01: „Das x muss an die Collection —
   * nicht an den Key."). Belonging is positional here, so it has to be
   * expressible in the strip, not worked around outside it. */
  /* `:not([hidden])` is not decoration: the separator rule scores (0,3,1)
     through its two `:not([hidden])` parts, so a plain `.list > .adornment`
     (0,2,1) loses to it. This matches that score and sits after it. */
  .cbui-breadcrumb-list > .cbui-breadcrumb-adornment:not([hidden])::before { content: none; }
  .cbui-breadcrumb-adornment {
    display: inline-flex;
    align-items: center;
    align-self: center;
    flex-shrink: 0;
    margin-inline-start: var(--cbui-breadcrumb-adornment-gap, 0.25rem);
  }

  /* ── Variant: pill ────────────────────────────────────────────────────── */
  /* The hover-chip / current pill lives on the SEAT (which excludes the leading
     separator), so a tinted segment hugs only its own text — never the separator
     or the gap toward the previous segment. Inline padding only on the seat gives
     the chip room around the text; no negative margin needed, so the
     parent-drawn separators stay symmetric and nothing bleeds past the strip. */
  .cbui-breadcrumb-pill .cbui-breadcrumb-item-seat {
    padding-inline: var(--cbui-breadcrumb-item-px);
  }
  .cbui-breadcrumb-pill a.cbui-breadcrumb-item:hover .cbui-breadcrumb-item-seat {
    background-color: var(--cbui-breadcrumb-pill-hover);
  }
  .cbui-breadcrumb-pill a.cbui-breadcrumb-item:active .cbui-breadcrumb-item-seat {
    background-color: var(--cbui-breadcrumb-pill-active);
  }
  .cbui-breadcrumb-pill .cbui-breadcrumb-item-current .cbui-breadcrumb-item-seat {
    background-color: var(--cbui-breadcrumb-pill-active);
  }
  /* The "…" trigger keeps its chip on the button itself — its ::before separator
     lives on the collapse host, not the button, so nothing bleeds. */
  .cbui-breadcrumb-pill .cbui-breadcrumb-ellipsis {
    padding-inline: var(--cbui-breadcrumb-item-px);
    border-radius: var(--cbui-breadcrumb-item-radius);
  }
  .cbui-breadcrumb-pill .cbui-breadcrumb-ellipsis:hover {
    background-color: var(--cbui-breadcrumb-pill-hover);
    color: var(--cbui-breadcrumb-link-hover);
  }
  .cbui-breadcrumb-pill .cbui-breadcrumb-ellipsis:active {
    background-color: var(--cbui-breadcrumb-pill-active);
  }

  /* ── Tones — colour the links + current segment + accent ──────────────────
   * neutral keeps the muted-link / full-current model. Semantic tones tint the
   * current segment (and the link hover) so the trail picks up intent. */
  .cbui-breadcrumb-tone-neutral { --cbui-breadcrumb-current: var(--color-text); }
  .cbui-breadcrumb-tone-primary { --cbui-breadcrumb-current: var(--color-primary); --cbui-breadcrumb-link-hover: var(--color-primary); }
  .cbui-breadcrumb-tone-accent  { --cbui-breadcrumb-current: var(--color-accent);  --cbui-breadcrumb-link-hover: var(--color-accent); }
  .cbui-breadcrumb-tone-success { --cbui-breadcrumb-current: var(--color-success); --cbui-breadcrumb-link-hover: var(--color-success); }
  .cbui-breadcrumb-tone-danger  { --cbui-breadcrumb-current: var(--color-danger);  --cbui-breadcrumb-link-hover: var(--color-danger); }
  .cbui-breadcrumb-tone-warning { --cbui-breadcrumb-current: var(--color-warning); --cbui-breadcrumb-link-hover: var(--color-warning); }
  .cbui-breadcrumb-tone-info    { --cbui-breadcrumb-current: var(--color-info);    --cbui-breadcrumb-link-hover: var(--color-info); }
  .cbui-breadcrumb-tone-muted   { --cbui-breadcrumb-current: var(--color-text-secondary); }

  /* Per-segment tone overrides (status-coloured segment). tone="neutral" keeps
     color:inherit (rides the strip colour) — others tint the single segment. */
  .cbui-breadcrumb-item-tone-primary { color: var(--color-primary); }
  .cbui-breadcrumb-item-tone-accent  { color: var(--color-accent); }
  .cbui-breadcrumb-item-tone-success { color: var(--color-success); }
  .cbui-breadcrumb-item-tone-danger  { color: var(--color-danger); }
  .cbui-breadcrumb-item-tone-warning { color: var(--color-warning); }
  .cbui-breadcrumb-item-tone-info    { color: var(--color-info); }
  .cbui-breadcrumb-item-tone-muted   { color: var(--color-text-secondary); }

  /* ── Sizes ────────────────────────────────────────────────────────────── */
  .cbui-breadcrumb-size-sm {
    --cbui-breadcrumb-font-size: var(--font-size-md, 1rem);
    --cbui-breadcrumb-item-px: 0.3125rem;
    --cbui-breadcrumb-item-gap: 0.3125rem;
    --cbui-breadcrumb-sep-gap: 0.375rem;
  }
  .cbui-breadcrumb-size-lg {
    --cbui-breadcrumb-font-size: var(--font-size-md, 1rem);
    --cbui-breadcrumb-item-px: 0.4375rem;
    --cbui-breadcrumb-item-py: 0.25rem;
    --cbui-breadcrumb-item-gap: 0.4375rem;
    --cbui-breadcrumb-sep-gap: 0.5rem;
  }

  /* ── Density ──────────────────────────────────────────────────────────── */
  .cbui-breadcrumb-density-compact {
    --cbui-breadcrumb-item-py: 0.0625rem;
    --cbui-breadcrumb-item-px: 0.25rem;
    --cbui-breadcrumb-sep-gap: 0.3125rem;
  }

  /* ── Overflow ─────────────────────────────────────────────────────────── */
  /* wrap — segments break to a new line; the separators still sit between them. */
  .cbui-breadcrumb-overflow-wrap .cbui-breadcrumb-list {
    flex-wrap: wrap;
    row-gap: var(--cbui-breadcrumb-item-py);
  }

  /* scroll — single line, horizontal scroll, hidden scrollbar, edge fade. */
  .cbui-breadcrumb-overflow-scroll .cbui-breadcrumb-list {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    scroll-behavior: smooth;
  }
  .cbui-breadcrumb-overflow-scroll .cbui-breadcrumb-list::-webkit-scrollbar { display: none; }
  /* fade the inline-start / inline-end edges when there is hidden content. The
     controller toggles is-overflow-start / is-overflow-end on the nav. */
  .cbui-breadcrumb-overflow-scroll.is-overflow-start .cbui-breadcrumb-list {
    mask-image: linear-gradient(to right, transparent 0, black 2rem);
    -webkit-mask-image: linear-gradient(to right, transparent 0, black 2rem);
  }
  .cbui-breadcrumb-overflow-scroll.is-overflow-end .cbui-breadcrumb-list {
    mask-image: linear-gradient(to left, transparent 0, black 2rem);
    -webkit-mask-image: linear-gradient(to left, transparent 0, black 2rem);
  }
  .cbui-breadcrumb-overflow-scroll.is-overflow-start.is-overflow-end .cbui-breadcrumb-list {
    mask-image: linear-gradient(to right, transparent 0, black 2rem, black calc(100% - 2rem), transparent 100%);
    -webkit-mask-image: linear-gradient(to right, transparent 0, black 2rem, black calc(100% - 2rem), transparent 100%);
  }

  /* menu — single line; the controller hides middle segments and reveals the
     "…" collapse host. overflow:hidden makes scrollWidth>clientWidth a reliable
     "does it fit?" probe and stops any pre-collapse spill from showing. */
  .cbui-breadcrumb-overflow-menu .cbui-breadcrumb-list {
    flex-wrap: nowrap;
    overflow: hidden;
  }

  /* ── Collapse host (overflow=menu) ────────────────────────────────────── */
  .cbui-breadcrumb-collapse { display: inline-flex; align-items: center; }
  .cbui-breadcrumb-collapse[hidden] { display: none; }
  /* The Dropdown wrapper is the flex child; flatten it so the trigger sits
     inline with the segments. */
  .cbui-breadcrumb-collapse > .cbui-dropdown { display: inline-flex; align-items: center; }
  .cbui-breadcrumb-ellipsis {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--cbui-breadcrumb-link);
  }
  .cbui-breadcrumb-ellipsis:hover { color: var(--cbui-breadcrumb-link-hover); }
  .cbui-breadcrumb-ellipsis > svg {
    width: 1.125em;
    height: 1.125em;
  }

  /* Single-segment strip — nothing to separate / collapse (defensive: the
     selector already suppresses a lone segment's separator, this is just
     belt-and-braces against any injected sibling). */
  .cbui-breadcrumb-single .cbui-breadcrumb-list > :not([hidden]) ~ :not([hidden])::before {
    content: none;
  }

  /* ── Accessibility overrides ──────────────────────────────────────────── */
  .a11y-reduced-motion .cbui-breadcrumb-item,
  .a11y-reduced-motion .cbui-breadcrumb-overflow-scroll .cbui-breadcrumb-list {
    transition: none;
    scroll-behavior: auto;
  }
  .a11y-high-contrast .cbui-breadcrumb-list > :not([hidden]) ~ :not([hidden])::before {
    color: var(--color-text-secondary);
  }
  .a11y-high-contrast .cbui-breadcrumb-item-current { text-decoration: underline; }
}

/* cbui/components/copy */
/* cbui-copy — standalone copy-button chrome.
 *
 * The host span carries the Stimulus controller; the inner button is a
 * regular .cbui-button so the existing Button vocabulary (tone, size,
 * variant) applies. We only need the glyph styling here.
 */

@layer components {
  .cbui-copy { display: inline-flex; }

  .cbui-copy-glyph {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    background-color: currentColor;
    -webkit-mask: no-repeat center / contain;
            mask: no-repeat center / contain;
  }
  .cbui-copy-glyph[hidden] { display: none; }

  /* copy (two stacked rounded rects) */
  .cbui-copy-glyph-idle {
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='5' width='8' height='9' rx='1.5' fill='none' stroke='black' stroke-width='1.4'/><path d='M5 5V3.5A1.5 1.5 0 0 1 6.5 2H12a1.5 1.5 0 0 1 1.5 1.5V9' fill='none' stroke='black' stroke-width='1.4'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='5' width='8' height='9' rx='1.5' fill='none' stroke='black' stroke-width='1.4'/><path d='M5 5V3.5A1.5 1.5 0 0 1 6.5 2H12a1.5 1.5 0 0 1 1.5 1.5V9' fill='none' stroke='black' stroke-width='1.4'/></svg>");
  }

  /* check (copy succeeded) — coloured success-green */
  .cbui-copy-glyph-done {
    color: var(--color-success);
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><polyline points='3 8.5 6.5 12 13 4.5' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><polyline points='3 8.5 6.5 12 13 4.5' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  }

  .cbui-copy-label {
    margin-inline-start: 0.375rem;
  }
}

/* cbui/components/toast */
/* cbui-toast — transient feedback message.
 *
 * Toast inherits its visual surface from Alert (1:1 vocabulary). The
 * markup is `<div class="cbui-toast cbui-alert cbui-alert-{variant}
 * cbui-alert-tone-{tone} cbui-alert-mod-...">…</div>` so every Alert
 * variant, tone, modifier and flavor-CSS rule applies automatically.
 *
 * The toast-only rules below only add what Alert doesn't have:
 *   - the fixed-position region containers
 *   - in/out animations
 *   - the toast-specific close × button
 *   - the inline action node
 *
 * Region padding token:
 *   --overlay-viewport-padding controls the gap between toast regions
 *   and the viewport edges. Defaults to 1rem; can be tuned per flavor
 *   or per page.
 */

@layer components {

  /* ─────────────────────────────────────────────────────────────────── */
  /*  REGION — fixed container per position                              */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-toast-region {
    position: fixed;
    z-index: var(--cbui-z-toast, 1950);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    pointer-events: none; /* gaps don't catch clicks */
    padding: var(--overlay-viewport-padding, 1rem);
    max-width: min(28rem, calc(100vw - var(--overlay-viewport-padding, 1rem) * 2));
    width: 100%;
  }
  .cbui-toast-region-top-start    { top: 0; left: 0; }
  .cbui-toast-region-top-center   { top: 0; left: 50%; transform: translateX(-50%); }
  .cbui-toast-region-top-end      { top: 0; right: 0; }
  .cbui-toast-region-bottom-start { bottom: 0; left: 0; }
  .cbui-toast-region-bottom-center{ bottom: 0; left: 50%; transform: translateX(-50%); }
  .cbui-toast-region-bottom-end   { bottom: 0; right: 0; }
  .cbui-toast-region-bottom-start,
  .cbui-toast-region-bottom-center,
  .cbui-toast-region-bottom-end { flex-direction: column-reverse; }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  TOAST — overlay tweaks on top of the inherited Alert surface       */
  /* ─────────────────────────────────────────────────────────────────── */

  .cbui-toast {
    pointer-events: auto;
    /* Tighten Alert's default padding (toasts read better compact) and
       add a lift shadow so they read on busy pages. */
    padding: 0.625rem 0.75rem;
    box-shadow: var(--shadow-lg);
    min-width: 0;
    animation: cbui-toast-in 200ms cubic-bezier(0.32, 0.72, 0, 1);
    /* Backdrop blur is token-driven. Flavors that want a frosted-glass
       look (Aero / Glass) set --toast-backdrop-blur to a positive value;
       Clean / Sharp / Soft keep it at 0 so toasts are fully opaque. */
    backdrop-filter: blur(var(--toast-backdrop-blur, 0));
    -webkit-backdrop-filter: blur(var(--toast-backdrop-blur, 0));
  }
  .a11y-reduced-motion .cbui-toast { animation: none; }
  .a11y-reduced-transparency .cbui-toast {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* Opacity is applied via color-mix on the SOLID variant only — alpha
     blending here keeps the text fully opaque while making the surface
     slightly translucent. Outline / ghost variants are intentionally
     untouched (they already render their own surface). */
  .cbui-toast.cbui-alert-solid.cbui-alert-tone-neutral { background-color: color-mix(in srgb, var(--color-text)    var(--toast-bg-opacity-pct, 100%), transparent); }
  .cbui-toast.cbui-alert-solid.cbui-alert-tone-primary { background-color: color-mix(in srgb, var(--color-primary) var(--toast-bg-opacity-pct, 100%), transparent); }
  .cbui-toast.cbui-alert-solid.cbui-alert-tone-accent  { background-color: color-mix(in srgb, var(--color-accent)  var(--toast-bg-opacity-pct, 100%), transparent); }
  .cbui-toast.cbui-alert-solid.cbui-alert-tone-info    { background-color: color-mix(in srgb, var(--color-info)    var(--toast-bg-opacity-pct, 100%), transparent); }
  .cbui-toast.cbui-alert-solid.cbui-alert-tone-success { background-color: color-mix(in srgb, var(--color-success) var(--toast-bg-opacity-pct, 100%), transparent); }
  .cbui-toast.cbui-alert-solid.cbui-alert-tone-warning { background-color: color-mix(in srgb, var(--color-warning) var(--toast-bg-opacity-pct, 100%), transparent); }
  .cbui-toast.cbui-alert-solid.cbui-alert-tone-danger  { background-color: color-mix(in srgb, var(--color-danger)  var(--toast-bg-opacity-pct, 100%), transparent); }

  /* The inline action — anchor or button. */
  .cbui-toast-action {
    margin-inline-start: 0.5rem;
    color: inherit;
    text-decoration: underline;
    font-weight: var(--font-weight-medium, 500);
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    font: inherit;
  }
  .cbui-toast-action:hover { opacity: 0.85; }

  /* Two or more actions (CB-3071): a row below the message, first = primary. */
  .cbui-toast-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-sm);
    margin-block-start: var(--spacing-xs);
  }
  .cbui-toast-actions .cbui-toast-action { margin-inline-start: 0; }

  /* Toast-specific close × — positioned next to the body, smaller than
     Alert's own dismiss button so the toast stays compact. */
  .cbui-toast-close {
    flex-shrink: 0;
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    opacity: 0.6;
    cursor: pointer;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    align-self: flex-start;
    margin-block-start: 0.0625rem;
  }
  .cbui-toast-close:hover { opacity: 1; background-color: var(--cbui-shade-soft, rgb(0 0 0 / 0.05)); }
  .cbui-toast-close svg { width: 0.875rem; height: 0.875rem; }

  /* ─────────────────────────────────────────────────────────────────── */
  /*  Animation                                                           */
  /* ─────────────────────────────────────────────────────────────────── */

  @keyframes cbui-toast-in {
    from { opacity: 0; transform: translateY(-8px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .cbui-toast-region-bottom-start .cbui-toast,
  .cbui-toast-region-bottom-center .cbui-toast,
  .cbui-toast-region-bottom-end .cbui-toast {
    animation-name: cbui-toast-in-bottom;
  }
  @keyframes cbui-toast-in-bottom {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  .cbui-toast-leaving {
    animation: cbui-toast-out 220ms ease-in forwards;
  }
  @keyframes cbui-toast-out {
    to { opacity: 0; transform: translateY(-4px) scale(0.98); }
  }
}

/* cbui/components/skeleton */
/* cbui-skeleton — content placeholder.
 *
 * Building blocks (Skeleton.Text / Skeleton.Avatar / Skeleton.Block via the
 * `as` prop). Shimmer animation runs via background-position translation —
 * never via opacity (opacity-pulse competes with the user's reduced-motion
 * setting and reads as a "broken animation" on dimmed monitors).
 */

@layer components {

  :where(.cbui-skeleton) {
    --cbui-skeleton-base: var(--color-bg-muted, var(--color-bg-subtle));
    --cbui-skeleton-shine: color-mix(in srgb, var(--color-text) 8%, transparent);
  }
  .dark :where(.cbui-skeleton),
  [data-scheme="dark"] :where(.cbui-skeleton) {
    --cbui-skeleton-base: var(--color-bg-subtle);
    --cbui-skeleton-shine: rgb(255 255 255 / 0.06);
  }

  .cbui-skeleton {
    display: inline-block;
    background-color: var(--cbui-skeleton-base);
    border-radius: 4px;
    vertical-align: middle;
    /* Keep shape — never wrap onto multiple lines visually. */
    flex-shrink: 0;
  }
  .cbui-skeleton-text   { border-radius: 4px;     vertical-align: -0.15em; }
  .cbui-skeleton-avatar { border-radius: 9999px;  vertical-align: middle; }
  .cbui-skeleton-block  { border-radius: var(--radius-md, 0.5rem); display: block; }

  .cbui-skeleton-rounded-sm   { border-radius: var(--radius-sm, 0.25rem); }
  .cbui-skeleton-rounded-md   { border-radius: var(--radius-md, 0.5rem); }
  .cbui-skeleton-rounded-lg   { border-radius: var(--radius-lg, 0.75rem); }
  .cbui-skeleton-rounded-full { border-radius: 9999px; }

  /* Shimmer modifier — background-position translation. */
  .cbui-skeleton-mod-pulse {
    background-image: linear-gradient(
      90deg,
      transparent 0%,
      var(--cbui-skeleton-shine) 50%,
      transparent 100%
    );
    background-size: 200% 100%;
    background-position: -100% 0;
    background-repeat: no-repeat;
    animation: cbui-skeleton-shimmer 1.4s linear infinite;
  }

  @keyframes cbui-skeleton-shimmer {
    0%   { background-position: -100% 0; }
    100% { background-position: 100% 0; }
  }

  /* Reduced motion: keep base colour visible but stop the shimmer. */
  .a11y-reduced-motion .cbui-skeleton-mod-pulse {
    animation: none;
    background-image: none;
  }
}

/* cbui/components/visit-placeholder */
/* cbui-visit-placeholder — page placeholder shown while a Turbo visit is slow (CB-1491).
 *
 * The host region carries `cbui-visit-host`; the placeholder is its first child. It is
 * display:none until the visit is busy, then fades in after a 300 ms delay (Turbo's progress
 * bar owns the first stretch). Overlay recipe: .cbui-list-detail-skeleton.
 */

@layer components {

  /* isolation: the host becomes its own stacking context, so the placeholder's z-index only has to beat
     the page content inside it (a BlockEditor card sits at z-index 5-6, its float at 60) and can never
     reach over the sticky navbar outside it. */
  .cbui-visit-host {
    position: relative;
    isolation: isolate;
  }

  .cbui-visit-placeholder {
    display: none;
  }

  html[aria-busy="true"]:not([data-turbo-preview]) .cbui-visit-host > .cbui-visit-placeholder,
  .cbui-visit-host[aria-busy="true"] > .cbui-visit-placeholder {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    position: absolute;
    inset: 0;
    z-index: 100;
    padding: var(--spacing-md, 0.75rem);
    overflow: hidden;
    pointer-events: none;
    background-color: var(--color-surface, var(--color-bg-elevated));
    opacity: 0;
    animation: cbui-visit-placeholder-in 120ms ease-out 300ms forwards;
  }

  .cbui-visit-placeholder-title {
    width: 40%;
    height: 1.5rem;
  }
  .cbui-visit-placeholder-row {
    width: 100%;
    height: 4rem;
  }

  @keyframes cbui-visit-placeholder-in { to { opacity: 1; } }

  /* Reduced motion: no fade, but keep the delay so fast pages still never flash it. */
  @media (prefers-reduced-motion: reduce) {
    html[aria-busy="true"]:not([data-turbo-preview]) .cbui-visit-host > .cbui-visit-placeholder,
    .cbui-visit-host[aria-busy="true"] > .cbui-visit-placeholder {
      animation-duration: 0s;
    }
  }
  .a11y-reduced-motion .cbui-visit-host > .cbui-visit-placeholder {
    animation-duration: 0s;
  }
}

/* cbui/components/table */
/* cbui-table — floating-tiles data table (UiData, finalized Mai 2026).
 *
 * Design language: every cell carries its own surface, separated by a gap
 * on BOTH axes. No wrapper border, no per-cell ring (in the rest state).
 * The four corner cells of the table hug an imaginary outer rectangle of
 * the flavor radius via the Dropdown edge-corner formula:
 *
 *   corner-radius = max(cell-radius, table-radius − gap − 1px)
 *
 * Token model:
 *   --cbui-table-gap            border-spacing between cells (default 3 px,
 *                               overridable per <Table gap="…">).
 *   --cbui-table-cell-radius    inner radius on every cell (3 px).
 *   --cbui-table-corner-radius  outer radius on the four corner cells
 *                               (auto-derived from the flavor `table-radius`).
 *   --cbui-table-cell-px / -py  cell padding (py is driven by density).
 *   --cbui-table-font-size      cell and caption type size (default md; the font classes swap it).
 *   --cbui-table-head-font-size head, foot, column-group and card-label type size (default md).
 *   --cbui-table-body-bg        rest-state body cell fill.
 *   --cbui-table-head-bg        head cell fill (one tier stronger).
 *   --cbui-table-row-hover-bg   hover fill (text-overlay shift).
 *   --cbui-table-row-active-bg  active-row fill (solid; tone-overridable).
 *   --cbui-table-row-active-fg  active-row text colour (default white).
 *   --cbui-table-row-selected-bg            soft primary tint on selected rows.
 *   --cbui-table-row-selected-bar           leading accent-bar colour.
 *   --cbui-table-row-selected-bar-width     leading accent-bar width (3px).
 *   --cbui-table-row-selected-bar-on-solid  bar colour when the row is also
 *                                           active (solid bg) — light tone.
 *
 * This file is intentionally UNLAYERED. The dev Web Debug Toolbar injects
 * unlayered `table, tr, th, td { background: var(--base-0) }` rules; layered
 * `.cbui-table` would lose the background war regardless of specificity.
 * Class selectors (0,1,0) cleanly beat the WDT's bare-element selectors (0,0,1).
 */

  /* ── WDT defence — see file header ─────────────────────────────────────── */
  .cbui-table,
  .cbui-table-row,
  .cbui-table-cell,
  .cbui-table-header-cell,
  .cbui-table-colgroup {
    background-color: transparent;
    border: 0;
  }

  /* ── Stacking order of a scrolling table (one place, no loose numbers) ──────
   * From the bottom: normal cells (auto) < pinned body cells < normal head cells < pinned head cells. A pinned
   * column (first column, select column, actions, or any cell with .cbui-table-cell-pinned) therefore always lies
   * ABOVE the columns that scroll past it, in the head as in the body; a head cell of a scrolling column lies above
   * the body cells that scroll up under the sticky head, but below the pinned head cells. */
  .cbui-table {
    --cbui-table-z-pinned: 2;
    --cbui-table-z-head: 3;
    --cbui-table-z-head-pinned: 4;
  }

  /* ── Wrapper ──────────────────────────────────────────────────────────────
   * Just a scroll container + responsive container context. No border, no
   * radius, no fill — the cells themselves form the visual envelope. */
  .cbui-table-wrap {
    position: relative;
    width: 100%;
    overflow: auto;
    container-type: inline-size;
  }

  /* ── Table root + token defaults ─────────────────────────────────────── */
  .cbui-table {
    --cbui-table-gap:            3px;
    --cbui-table-cell-radius:    3px;
    --cbui-table-cell-px:        0.875rem;
    --cbui-table-cell-py:        0.625rem;

    --cbui-table-corner-radius:  max(
      var(--cbui-table-cell-radius),
      calc(var(--table-radius, var(--card-radius, 0.75rem)) - var(--cbui-table-gap) - 1px)
    );

    --cbui-table-body-bg:        var(--color-bg-subtle);
    /* The plane the table sits on — fills the gaps under a sticky head (CB-1930). */
    --cbui-table-plane-bg:       var(--color-surface, var(--color-bg-elevated));
    /* Head bg is a stronger tier than body — explicit color-mix so it's
       reliably darker than bg-subtle in both light and dark modes (bg-muted
       wasn't always a clear enough step from bg-subtle). */
    --cbui-table-head-bg:        color-mix(in srgb, var(--color-text) 10%, var(--color-bg-base));
    --cbui-table-head-fg:        var(--color-text);

    --cbui-table-row-hover-bg:   color-mix(in srgb, var(--color-text) 4%, var(--cbui-table-body-bg));

    --cbui-table-row-active-bg:  var(--color-primary);
    --cbui-table-row-active-fg:  #fff;

    /* Selected = soft primary tint on every cell + a solid accent bar on the
       row's leading edge (the conventional "marked row" pattern — Gmail /
       Linear / GitHub). Distinct from hover (neutral overlay) and active
       (solid fill). The tint is intentionally lighter than row-tone-primary
       (14%) so a selected row reads differently from a primary-toned one. */
    --cbui-table-row-selected-bg:            color-mix(in srgb, var(--color-primary) 16%, var(--cbui-table-body-bg));
    --cbui-table-row-selected-bar:           var(--color-primary);
    --cbui-table-row-selected-bar-width:     3px;
    /* On a solid (active) row keep the active fill; the bar switches to a
       light tone so it stays visible against the saturated background. */
    --cbui-table-row-selected-bar-on-solid:  #fff;
    /* Grabbed row (CB-2915, reorderable): the flat sortable list's 6 % wash and 35 % ring. */
    --cbui-table-row-grabbed-bg:             color-mix(in srgb, var(--color-primary) 6%, var(--cbui-table-body-bg));
    --cbui-table-row-grabbed-ring:           color-mix(in srgb, var(--color-primary) 35%, transparent);
    /* Type (CB-2920): the table's own tokens; the font classes below swap them. Default = md. */
    --cbui-table-font-size:                  var(--font-size-md, 1rem);
    --cbui-table-head-font-size:             var(--font-size-md, 1rem);

    width: 100%;
    margin: 0;
    border-collapse: separate;
    border-spacing: var(--cbui-table-gap);
    color: var(--color-text);
    font-size: var(--cbui-table-font-size);
    line-height: 1.45;
    text-align: start;
  }

  /* Narrow forms (cards, list): what separates one row from the next. Light keeps the
   * border colour and no card ring (unchanged). In the dark scheme the card surface and the
   * list pane behind it are only a few levels apart (measured 46 against 38, contrast 1.11), so
   * the rows ran together (Matthias, 2026-10-02): the card is lifted by a text overlay and gets a
   * ring, the list divider a stronger line. One place, read by both narrow forms. */
  .cbui-table {
    --cbui-table-narrow-edge: var(--color-border);
    --cbui-table-card-bg:     var(--cbui-table-body-bg);
    --cbui-table-card-ring:   transparent;
  }
  [data-scheme="dark"] .cbui-table,
  :root[data-color-scheme="dark"] .cbui-table,
  .dark .cbui-table {
    --cbui-table-narrow-edge: color-mix(in srgb, var(--color-text) 22%, var(--cbui-table-body-bg));
    --cbui-table-card-bg:     color-mix(in srgb, var(--color-text) 12%, var(--cbui-table-body-bg));
    --cbui-table-card-ring:   color-mix(in srgb, var(--color-text) 22%, var(--cbui-table-body-bg));
  }

  /* A table that IS the content of a ListDetail list pane gets the pane's inset, so the narrow
   * rows / cards do not touch the pane edge (the demos used to wrap the table in a padded div). */
  .cbui-list-detail-list > .cbui-table-wrap {
    padding: 0.5rem;
  }
  /* A table with scroller="parent" in a ListDetail list pane has THE PANE as its scroller. The pane
   * is `overflow-y: auto; overflow-x: hidden` (list-detail.css), so a table wider than the pane was
   * cut off and could not be scrolled sideways. The table asks for the axis it needs. */
  .cbui-list-detail-list:has(> .cbui-table-wrap-scroller-parent) {
    overflow-x: auto;
  }

  /* Dark-mode tweaks: the hover overlay (text 4 %) reads as "darken" in
     light mode and "lighten" in dark — both move the cell away from the
     rest state, which is the desired hover signal. */

  .cbui-table-layout-fixed { table-layout: fixed; }

  /* ── Caption ─────────────────────────────────────────────────────────── */
  .cbui-table-caption {
    caption-side: top;
    padding: 0 var(--cbui-table-cell-px) 0.625rem;
    color: var(--color-text-secondary);
    font-size: var(--cbui-table-font-size);
    text-align: start;
  }

  /* ── Cells — base recipe (applies to td / th in body and head) ───────── */
  .cbui-table-cell,
  .cbui-table-header-cell,
  .cbui-table-colgroup,
  .cbui-table > thead > tr > th,
  .cbui-table > thead > tr > td,
  .cbui-table > tbody > tr > td,
  .cbui-table > tbody > tr > th,
  .cbui-table > tfoot > tr > td,
  .cbui-table > tfoot > tr > th {
    padding: var(--cbui-table-cell-py) var(--cbui-table-cell-px);
    vertical-align: middle;
    text-align: start;
    border-radius: var(--cbui-table-cell-radius);
  }

  /* ── Body cells — rest-state fill ────────────────────────────────────── */
  .cbui-table > tbody > tr > td,
  .cbui-table > tbody > tr > th {
    background: var(--cbui-table-body-bg);
    transition: background-color 120ms ease, color 120ms ease, outline-color 120ms ease;
  }
  .cbui-table > tbody > tr > th {
    font-weight: var(--font-weight-semibold, 600);
  }

  /* ── Footer cells — same chrome as head ──────────────────────────────── */
  .cbui-table > tfoot > tr > td,
  .cbui-table > tfoot > tr > th {
    background: var(--cbui-table-head-bg);
    color: var(--color-text-secondary);
    font-weight: var(--font-weight-semibold, 600);
    font-size: var(--cbui-table-head-font-size);
  }

  /* ── Head cells — one tier stronger than body ────────────────────────── */
  .cbui-table > thead > tr > th,
  .cbui-table > thead > tr > td {
    background: var(--cbui-table-head-bg);
    color: var(--cbui-table-head-fg);
    font-size: var(--cbui-table-head-font-size);
    font-weight: var(--font-weight-semibold, 600);
    text-align: start;
    /* All headers of a row share the bottom edge (a header may wrap onto several lines). */
    vertical-align: bottom;
    white-space: nowrap;
  }
  /* Opt-in wrap (`TableHeaderCell nowrap=false`): the label wraps, clamped below. Not the
   * column groups and not the select-all cell. */
  .cbui-table > thead > tr > th.cbui-table-header-cell:not(.cbui-table-cell-nowrap):not(.cbui-table-cell-select) {
    white-space: normal;
  }

  /* ── Column-group cells — variant + tone ─────────────────────────────────
   * variant="filled" (default): bg-subtle fill (weaker than col-headers).
   * variant="outlined": transparent + 1 px inset ring, label tinted.
   * tone="X" tones the fill (filled) or the ring + label (outlined).
   *
   * Selectors specificity-matched to the head-cell rule
   * `.cbui-table > thead > tr > th` (0,1,3) — by going through the thead
   * chain and chaining a class we reach (0,2,3) and win the cascade. */
  .cbui-table-colgroup {
    text-align: center;
    font-weight: var(--font-weight-medium, 500);
    color: var(--color-text-secondary);
    font-size: var(--cbui-table-head-font-size);
  }
  /* variant="filled" — default fill (overrides the head-cell head-bg) */
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-filled {
    background: var(--color-bg-subtle);
  }
  /* variant="outlined" — drop the fill, add a 1 px neutral inset ring */
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-outlined {
    background: transparent;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-border) 70%, transparent);
  }
  /* Spacer — empty cell, no chrome regardless of variant */
  .cbui-table > thead > tr > th.cbui-table-colgroup-spacer {
    background: transparent;
    box-shadow: none;
  }

  /* tone — filled path tints the bg + label */
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-filled.cbui-table-colgroup-tone-primary { background: color-mix(in srgb, var(--color-primary) 14%, var(--color-bg-subtle)); color: color-mix(in srgb, var(--color-primary) 70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-filled.cbui-table-colgroup-tone-accent  { background: color-mix(in srgb, var(--color-accent)  14%, var(--color-bg-subtle)); color: color-mix(in srgb, var(--color-accent)  70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-filled.cbui-table-colgroup-tone-success { background: color-mix(in srgb, var(--color-success) 14%, var(--color-bg-subtle)); color: color-mix(in srgb, var(--color-success) 70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-filled.cbui-table-colgroup-tone-danger  { background: color-mix(in srgb, var(--color-danger)  14%, var(--color-bg-subtle)); color: color-mix(in srgb, var(--color-danger)  70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-filled.cbui-table-colgroup-tone-warning { background: color-mix(in srgb, var(--color-warning) 14%, var(--color-bg-subtle)); color: color-mix(in srgb, var(--color-warning) 70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-filled.cbui-table-colgroup-tone-info    { background: color-mix(in srgb, var(--color-info)    14%, var(--color-bg-subtle)); color: color-mix(in srgb, var(--color-info)    70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-filled.cbui-table-colgroup-tone-muted   { background: color-mix(in srgb, var(--color-text)    8%,  var(--color-bg-subtle)); color: var(--color-text-secondary); }

  /* tone — outlined path tints the ring + label, bg stays transparent */
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-outlined.cbui-table-colgroup-tone-primary { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 45%, transparent); color: color-mix(in srgb, var(--color-primary) 70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-outlined.cbui-table-colgroup-tone-accent  { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-accent)  45%, transparent); color: color-mix(in srgb, var(--color-accent)  70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-outlined.cbui-table-colgroup-tone-success { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-success) 45%, transparent); color: color-mix(in srgb, var(--color-success) 70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-outlined.cbui-table-colgroup-tone-danger  { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-danger)  45%, transparent); color: color-mix(in srgb, var(--color-danger)  70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-outlined.cbui-table-colgroup-tone-warning { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-warning) 45%, transparent); color: color-mix(in srgb, var(--color-warning) 70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-outlined.cbui-table-colgroup-tone-info    { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-info)    45%, transparent); color: color-mix(in srgb, var(--color-info)    70%, var(--color-text)); }
  .cbui-table > thead > tr > th.cbui-table-colgroup-variant-outlined.cbui-table-colgroup-tone-muted   { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-border)  60%, transparent); color: var(--color-text-secondary); }

  .cbui-table-colgroup-label {
    display: inline-block;
  }

  /* ── Outer corner radii — Dropdown edge-corner rule, re-applied ────────
   * Each cell defaults to inner radius on all 4 corners. The four corner
   * cells of the table override their respective outer corner with the
   * computed corner-radius. Pure CSS — no Twig logic per cell. */

  /* Top corners — first row of thead, OR first row of tbody if no thead */
  .cbui-table > thead:first-child > tr:first-child > *:first-child,
  .cbui-table > tbody:first-child > tr:first-child > *:first-child {
    border-top-left-radius: var(--cbui-table-corner-radius);
  }
  .cbui-table > thead:first-child > tr:first-child > *:last-child,
  .cbui-table > tbody:first-child > tr:first-child > *:last-child {
    border-top-right-radius: var(--cbui-table-corner-radius);
  }
  /* Bottom corners — last row of tfoot, OR last row of tbody if no tfoot */
  .cbui-table > tfoot:last-child > tr:last-child > *:first-child,
  .cbui-table > tbody:last-child > tr:last-child > *:first-child {
    border-bottom-left-radius: var(--cbui-table-corner-radius);
  }
  .cbui-table > tfoot:last-child > tr:last-child > *:last-child,
  .cbui-table > tbody:last-child > tr:last-child > *:last-child {
    border-bottom-right-radius: var(--cbui-table-corner-radius);
  }

  /* ── surface="rows" — a continuous band per row (CB-1915) ───────────────
   * Default `surface="cells"` gives every cell its own floating tile with a
   * gap on both axes (this file's whole design language, see header). Some
   * hosts (e.g. the Files Bringer's Details view) instead want ONE
   * continuous surface per row, with the gap only BETWEEN rows. Redirecting
   * the row-radius and corner-radius tokens plus zeroing the cell radius and
   * the horizontal border-spacing gets there without touching any other
   * rule in this file — the corner-radius rule above still applies, now
   * against the row radius instead of the cell radius. */
  .cbui-table.cbui-table-surface-rows {
    --cbui-table-row-radius: var(--radius-md);
    --cbui-table-cell-radius: 0px;
    --cbui-table-corner-radius: var(--cbui-table-row-radius);
    border-spacing: 0 var(--cbui-table-gap);
  }
  .cbui-table-surface-rows > * > tr > *:first-child {
    border-start-start-radius: var(--cbui-table-row-radius);
    border-end-start-radius: var(--cbui-table-row-radius);
  }
  .cbui-table-surface-rows > * > tr > *:last-child {
    border-start-end-radius: var(--cbui-table-row-radius);
    border-end-end-radius: var(--cbui-table-row-radius);
  }

  /* ── On a recessed ListDetail list pane (CB-1930) ────────────────────────
   * The pane is the recessed plane (240 in light); bg-subtle is 245 there and
   * "five values is not a difference" (list-detail.css). Cells go to the front
   * plane instead. The tokens live ON .cbui-table, so an ancestor rule cannot
   * reach them: the selector must end in .cbui-table. Stack / single-pane put
   * the list back on the front plane, so they are excluded. */
  .cbui-list-detail-recessed:not(.is-stack):not(.is-single) > .cbui-list-detail-panes > .cbui-list-detail-list .cbui-table {
    --cbui-table-plane-bg: var(--color-bg-canvas, var(--color-bg-base));
    --cbui-table-body-bg: var(--color-surface, var(--color-bg-elevated));
  }
  [data-scheme="dark"] .cbui-list-detail-recessed:not(.is-stack):not(.is-single) > .cbui-list-detail-panes > .cbui-list-detail-list .cbui-table,
  .dark .cbui-list-detail-recessed:not(.is-stack):not(.is-single) > .cbui-list-detail-panes > .cbui-list-detail-list .cbui-table,
  :root[data-color-scheme="dark"] .cbui-list-detail-recessed:not(.is-stack):not(.is-single) > .cbui-list-detail-panes > .cbui-list-detail-list .cbui-table,
  .cbui-dark .cbui-list-detail-recessed:not(.is-stack):not(.is-single) > .cbui-list-detail-panes > .cbui-list-detail-list .cbui-table {
    --cbui-table-body-bg: var(--color-bg-subtle);
  }

  /* ── scroller="parent" — the table doesn't scroll itself (CB-1915) ──────
   * Default `scroller="self"` (above) makes `.cbui-table-wrap` the scroll
   * container. With `scroller="parent"` an ancestor scrolls instead, so a
   * `stickyHeader` sticks to THAT viewport rather than scrolling away with
   * a wrapper that never actually scrolls vertically. */
  .cbui-table-wrap.cbui-table-wrap-scroller-parent {
    overflow: visible;
  }

  /* ── Row states ──────────────────────────────────────────────────────── */
  .cbui-table-row-clickable { cursor: pointer; }

  /* Hover — text-overlay shift on body rows. Skip header / footer / skeleton / empty. */
  .cbui-table.cbui-table-hover > tbody > tr.cbui-table-row:not(.cbui-table-row-skeleton):not(.cbui-table-row-empty):not(.cbui-table-row-active):hover > td,
  .cbui-table.cbui-table-hover > tbody > tr.cbui-table-row:not(.cbui-table-row-skeleton):not(.cbui-table-row-empty):not(.cbui-table-row-active):hover > th {
    background: var(--cbui-table-row-hover-bg);
  }

  /* Active — solid fill + light text. Default tone = primary; tone="X" picks
     a different colour family. The bg & fg ride on CSS vars so a host app
     can re-paint without overriding the cell selectors.
     Selectors are deliberately specificity-matched to the body-cell rule
     `.cbui-table > tbody > tr > td` (0,1,3) — by adding the row class we
     reach (0,2,3) and win the cascade. */
  .cbui-table > tbody > tr.cbui-table-row-active > td,
  .cbui-table > tbody > tr.cbui-table-row-active > th {
    background: var(--cbui-table-row-active-bg);
    color: var(--cbui-table-row-active-fg);
  }
  .cbui-table-row.cbui-table-row-active.cbui-table-row-tone-primary { --cbui-table-row-active-bg: var(--color-primary); }
  .cbui-table-row.cbui-table-row-active.cbui-table-row-tone-accent  { --cbui-table-row-active-bg: var(--color-accent);  }
  .cbui-table-row.cbui-table-row-active.cbui-table-row-tone-success { --cbui-table-row-active-bg: var(--color-success); }
  .cbui-table-row.cbui-table-row-active.cbui-table-row-tone-danger  { --cbui-table-row-active-bg: var(--color-danger);  }
  .cbui-table-row.cbui-table-row-active.cbui-table-row-tone-warning { --cbui-table-row-active-bg: var(--color-warning); }
  .cbui-table-row.cbui-table-row-active.cbui-table-row-tone-info    { --cbui-table-row-active-bg: var(--color-info);    }
  .cbui-table-row.cbui-table-row-active.cbui-table-row-tone-muted   { --cbui-table-row-active-bg: var(--color-text); --cbui-table-row-active-fg: var(--color-bg-base); }

  /* Active, soft look (`activeLook="soft"`) — the row whose detail is open keeps its normal text and gets the soft
     selected tint plus the leading accent bar, so badges, avatars and tones inside it stay readable. */
  .cbui-table.cbui-table-active-soft > tbody > tr.cbui-table-row-active {
    --cbui-table-row-active-bg: var(--cbui-table-row-selected-bg);
    --cbui-table-row-active-fg: var(--color-text);
  }
  .cbui-table.cbui-table-active-soft > tbody > tr.cbui-table-row-active > *:first-child {
    box-shadow: inset var(--cbui-table-row-selected-bar-width) 0 0 0 var(--cbui-table-row-selected-bar);
  }

  /* Selected — soft primary tint on every cell + a solid accent bar on the
     row's leading edge. The conventional "marked row" pattern; clearly
     distinct from hover (neutral overlay) and active (solid fill).
     The :nth-of-type(n) + three :not()s push the tint to (0,7,3) so it wins
     over the striped even-row rule (0,6,3) — a selected row stays tinted in
     a striped table regardless of source order. */
  .cbui-table > tbody > tr.cbui-table-row.cbui-table-row-selected:nth-of-type(n):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > td,
  .cbui-table > tbody > tr.cbui-table-row.cbui-table-row-selected:nth-of-type(n):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > th {
    background: var(--cbui-table-row-selected-bg);
  }
  /* Leading accent bar — inset box-shadow on the row's FIRST cell (the
     checkbox cell when bulk-select is on, else the first data cell). */
  .cbui-table > tbody > tr.cbui-table-row-selected:not(.cbui-table-row-active) > *:first-child {
    box-shadow: inset var(--cbui-table-row-selected-bar-width) 0 0 0 var(--cbui-table-row-selected-bar);
  }
  /* On a solid (active) row keep the active fill; the bar switches to a light
     tone so the selection cue stays visible. */
  .cbui-table > tbody > tr.cbui-table-row-selected.cbui-table-row-active > *:first-child {
    box-shadow: inset var(--cbui-table-row-selected-bar-width) 0 0 0 var(--cbui-table-row-selected-bar-on-solid);
  }

  /* aria-selected alias (CB-1906) — a consumer that owns selection itself
   * (ListDetail driving a Table the way it drives a list/tree) marks the
   * selected row with aria-selected="true" rather than the `selected` prop
   * (which also renders aria-selected, but ListDetail's rows are plain DOM
   * elements it toggles directly). Same specificity-raising chain as the
   * `.cbui-table-row-selected` rules above, so a row is visually identical
   * regardless of which mechanism marked it. */
  .cbui-table > tbody > tr.cbui-table-row[aria-selected="true"]:nth-of-type(n):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > td,
  .cbui-table > tbody > tr.cbui-table-row[aria-selected="true"]:nth-of-type(n):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > th {
    background: var(--cbui-table-row-selected-bg);
  }
  .cbui-table > tbody > tr.cbui-table-row[aria-selected="true"]:not(.cbui-table-row-active) > *:first-child {
    box-shadow: inset var(--cbui-table-row-selected-bar-width) 0 0 0 var(--cbui-table-row-selected-bar);
  }

  /* Grabbed (CB-2915, reorderable) — keyboard pickup or mouse drag. Same specificity chain as the
     selected rules above and placed after them and after hover, so a grabbed row wins. The layered
     sortable-list rule also reaches the tr; its box-shadow is neutralised here. */
  .cbui-table > tbody > tr.cbui-table-row.cbui-sortable-list-row-grabbed:nth-of-type(n):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > td,
  .cbui-table > tbody > tr.cbui-table-row.cbui-sortable-list-row-grabbed:nth-of-type(n):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > th {
    background: var(--cbui-table-row-grabbed-bg);
  }
  .cbui-table > tbody > tr.cbui-sortable-list-row-grabbed {
    box-shadow: none;
    outline: 1px solid var(--cbui-table-row-grabbed-ring);
    outline-offset: -1px;
  }

  /* Disabled — dimmed, no interaction. */
  .cbui-table-row-disabled {
    opacity: 0.55;
  }
  .cbui-table-row-disabled,
  .cbui-table-row-disabled > td { cursor: not-allowed; }

  /* Keyboard focus — the controller puts a roving tabindex on body rows. */
  .cbui-table-row:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -2px;
    position: relative;
    z-index: 1;
  }

  /* ── Whole-row tone (soft tint, gap preserved) ────────────────────────────
   * Each cell of the row gets the tint individually, so the gap stays alive
   * and the row never reads as one solid bar. Skipped when the row is also
   * `active` (active wins — that's the solid variant).
   * Selectors specificity-matched to the body-cell rule. */
  .cbui-table > tbody > tr.cbui-table-row-tone-primary:not(.cbui-table-row-active) > td,
  .cbui-table > tbody > tr.cbui-table-row-tone-primary:not(.cbui-table-row-active) > th { background: color-mix(in srgb, var(--color-primary) 14%, var(--cbui-table-body-bg)); }
  .cbui-table > tbody > tr.cbui-table-row-tone-accent:not(.cbui-table-row-active)  > td,
  .cbui-table > tbody > tr.cbui-table-row-tone-accent:not(.cbui-table-row-active)  > th { background: color-mix(in srgb, var(--color-accent)  14%, var(--cbui-table-body-bg)); }
  .cbui-table > tbody > tr.cbui-table-row-tone-success:not(.cbui-table-row-active) > td,
  .cbui-table > tbody > tr.cbui-table-row-tone-success:not(.cbui-table-row-active) > th { background: color-mix(in srgb, var(--color-success) 14%, var(--cbui-table-body-bg)); }
  .cbui-table > tbody > tr.cbui-table-row-tone-danger:not(.cbui-table-row-active)  > td,
  .cbui-table > tbody > tr.cbui-table-row-tone-danger:not(.cbui-table-row-active)  > th { background: color-mix(in srgb, var(--color-danger)  14%, var(--cbui-table-body-bg)); }
  .cbui-table > tbody > tr.cbui-table-row-tone-warning:not(.cbui-table-row-active) > td,
  .cbui-table > tbody > tr.cbui-table-row-tone-warning:not(.cbui-table-row-active) > th { background: color-mix(in srgb, var(--color-warning) 14%, var(--cbui-table-body-bg)); }
  .cbui-table > tbody > tr.cbui-table-row-tone-info:not(.cbui-table-row-active)    > td,
  .cbui-table > tbody > tr.cbui-table-row-tone-info:not(.cbui-table-row-active)    > th { background: color-mix(in srgb, var(--color-info)    14%, var(--cbui-table-body-bg)); }
  .cbui-table > tbody > tr.cbui-table-row-tone-muted:not(.cbui-table-row-active)   > td,
  .cbui-table > tbody > tr.cbui-table-row-tone-muted:not(.cbui-table-row-active)   > th { background: color-mix(in srgb, var(--color-text)    6%,  var(--cbui-table-body-bg)); color: var(--color-text-secondary); }

  /* ── Per-cell tone — the canonical status-column hook ──────────────────
   * Specificity-matched (chained class) so we beat the body-cell rule. */
  .cbui-table-cell.cbui-table-cell-tone-primary { background: color-mix(in srgb, var(--color-primary) 14%, var(--cbui-table-body-bg)); color: color-mix(in srgb, var(--color-primary) 70%, var(--color-text)); font-weight: var(--font-weight-medium, 500); }
  .cbui-table-cell.cbui-table-cell-tone-accent  { background: color-mix(in srgb, var(--color-accent)  14%, var(--cbui-table-body-bg)); color: color-mix(in srgb, var(--color-accent)  70%, var(--color-text)); font-weight: var(--font-weight-medium, 500); }
  .cbui-table-cell.cbui-table-cell-tone-success { background: color-mix(in srgb, var(--color-success) 14%, var(--cbui-table-body-bg)); color: color-mix(in srgb, var(--color-success) 70%, var(--color-text)); font-weight: var(--font-weight-medium, 500); }
  .cbui-table-cell.cbui-table-cell-tone-danger  { background: color-mix(in srgb, var(--color-danger)  14%, var(--cbui-table-body-bg)); color: color-mix(in srgb, var(--color-danger)  70%, var(--color-text)); font-weight: var(--font-weight-medium, 500); }
  .cbui-table-cell.cbui-table-cell-tone-warning { background: color-mix(in srgb, var(--color-warning) 14%, var(--cbui-table-body-bg)); color: color-mix(in srgb, var(--color-warning) 70%, var(--color-text)); font-weight: var(--font-weight-medium, 500); }
  .cbui-table-cell.cbui-table-cell-tone-info    { background: color-mix(in srgb, var(--color-info)    14%, var(--cbui-table-body-bg)); color: color-mix(in srgb, var(--color-info)    70%, var(--color-text)); font-weight: var(--font-weight-medium, 500); }
  .cbui-table-cell.cbui-table-cell-tone-muted   { color: var(--color-text-secondary); }

  /* ── Cell alignment helpers ──────────────────────────────────────────────
   * Two classes deep ON PURPOSE: the base recipe above sets `text-align: start`
   * on `.cbui-table > tbody > tr > td` (0,1,3), which a single helper class
   * (0,1,0) lost against — align="end" / "center" never reached the cell. */
  .cbui-table .cbui-table-cell-align-center { text-align: center; }
  .cbui-table .cbui-table-cell-align-end    { text-align: end; }
  /* A rich cell (icon / truncate / link) lays its content out as a flex row, which
   * text-align cannot move: align the row itself. */
  .cbui-table .cbui-table-cell-align-center > .cbui-table-cell-content { justify-content: center; }
  .cbui-table .cbui-table-cell-align-end    > .cbui-table-cell-content { justify-content: flex-end; }
  .cbui-table-cell-valign-top    { vertical-align: top; }
  .cbui-table-cell-valign-bottom { vertical-align: bottom; }
  .cbui-table-cell-nowrap { white-space: nowrap; }

  /* ── Select column (CB-38) — narrow, centered checkbox ─────────────────── */
  .cbui-table-cell-select {
    width: var(--cbui-table-select-col-width, 2.75rem);
    padding-inline: 0;
    text-align: center;
    white-space: nowrap;
  }
  .cbui-table-cell-select .cbui-checkbox {
    display: inline-flex;
    vertical-align: middle;
  }

  /* ── Handle column (CB-2915, reorderable) — narrow, centered drag handle ─ */
  .cbui-table-cell-handle {
    width: var(--cbui-table-handle-col-width, 2.25rem);
    padding-inline: 0;
    text-align: center;
    white-space: nowrap;
  }
  .cbui-table-cell-handle .cbui-sortable-list-handle {
    margin-block-start: 0;
    vertical-align: middle;
  }
  /* The head cell carries a visually hidden label only. */
  .cbui-table-handle-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* Sortable headers center / end-align their inner button too. */
  .cbui-table-header-cell.cbui-table-cell-align-center .cbui-table-sort { justify-content: center; }
  .cbui-table-header-cell.cbui-table-cell-align-end    .cbui-table-sort { justify-content: flex-end; }

  /* ── Striped — via outline cells ──────────────────────────────────────────
   * Opt-in (cbui-table-striped). Odd body rows = filled (default).
   * Even body rows = transparent fill + 1 px inset ring in the SAME colour
   * as the filled cells — outlined rows read as "ghost echoes" of the
   * filled tiles. No second bg-color needed. */
  .cbui-table-striped > tbody > tr.cbui-table-row:nth-of-type(even):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > td,
  .cbui-table-striped > tbody > tr.cbui-table-row:nth-of-type(even):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > th {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--cbui-table-body-bg);
  }

  /* ── Sortable header button ──────────────────────────────────────────── */
  .cbui-table > thead > tr > th.cbui-table-header-cell-sortable {
    padding: 0;
  }

  .cbui-table-sort {
    display: flex;
    /* The arrow stays beside the LAST line of a wrapped label (its box is one line high). */
    align-items: flex-end;
    gap: 0.375rem;
    width: 100%;
    padding: var(--cbui-table-cell-py) var(--cbui-table-cell-px);
    margin: 0;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: var(--cbui-table-head-font-size);
    font-weight: var(--font-weight-semibold, 600);
    text-transform: inherit;
    text-align: start;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease;
  }
  .cbui-table-sort:hover {
    background-color: color-mix(in srgb, var(--color-text) 5%, transparent);
    color: var(--color-text);
  }
  .cbui-table-sort:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -2px;
  }

  .cbui-table-sort-icon {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    /* One line of the button's text (line-height 1.45 of the head font): the glyph is
     * centred on the last line, whatever the label height. */
    height: 1.45em;
  }
  /* The label gives way to the arrow instead of running under it: it may shrink below its
   * text and is cut with an ellipsis (one line, the default) ... */
  .cbui-table-sort-label {
    min-width: 0;
    flex: 0 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* ... or wraps onto --cbui-table-head-lines lines (nowrap=false), then is cut. */
  .cbui-table-header-cell:not(.cbui-table-cell-nowrap) .cbui-table-sort-label,
  .cbui-table-head-label {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: var(--cbui-table-head-lines, 3);
    line-clamp: var(--cbui-table-head-lines, 3);
    overflow: hidden;
    /* Never break INSIDE a word: a head is as wide as its longest word at least
     * (auto layout does that by itself; with fixed columns give the column that much). */
    overflow-wrap: normal;
    word-break: normal;
    hyphens: manual;
    white-space: normal;
    min-width: min-content;
  }
  .cbui-table-header-cell.cbui-table-cell-align-center .cbui-table-sort-label { text-align: center; }
  .cbui-table-header-cell.cbui-table-cell-align-end    .cbui-table-sort-label { text-align: end; }
  .cbui-table-sort-glyph {
    width: 0.875rem;
    height: 0.875rem;
  }
  .cbui-table-sort-glyph-asc,
  .cbui-table-sort-glyph-desc { display: none; }
  .cbui-table-sort-glyph-none { opacity: 0.4; transition: opacity 120ms ease; }
  .cbui-table-sort:hover .cbui-table-sort-glyph-none { opacity: 0.75; }

  .cbui-table > thead > tr > th[aria-sort="ascending"],
  .cbui-table > thead > tr > th[aria-sort="descending"] { color: var(--color-text); }
  .cbui-table > thead > tr > th[aria-sort="ascending"]  .cbui-table-sort,
  .cbui-table > thead > tr > th[aria-sort="descending"] .cbui-table-sort { color: var(--color-primary); }
  .cbui-table > thead > tr > th[aria-sort="ascending"]  .cbui-table-sort-glyph-none,
  .cbui-table > thead > tr > th[aria-sort="descending"] .cbui-table-sort-glyph-none { display: none; }
  .cbui-table > thead > tr > th[aria-sort="ascending"]  .cbui-table-sort-glyph-asc,
  .cbui-table > thead > tr > th[aria-sort="descending"] .cbui-table-sort-glyph-desc {
    display: inline-block;
    color: var(--color-primary);
  }

  /* ── Sticky header — position: sticky on each th ──────────────────────── */
  .cbui-table-sticky-header > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: var(--cbui-table-z-head);
  }
  /* Fill the border-spacing around each pinned head tile with the plane colour
   * so scrolled rows do not show through the gaps (CB-1930). Not in rows mode:
   * there the horizontal spacing is 0 and the ring would paint over the
   * neighbouring head cell. */
  .cbui-table-sticky-header:not(.cbui-table-surface-rows) > thead > tr > th {
    outline: var(--cbui-table-gap) solid var(--cbui-table-plane-bg);
    outline-offset: 0;
  }
  /* Subtle elevation when scrolled (the head sits 1 gap below the wrapper top). */
  .cbui-table-sticky-header > thead > tr:last-child > th {
    box-shadow: 0 4px 8px -6px rgb(0 0 0 / 0.18);
  }

  /* ── stickyFirstColumn — the first data column stays put sideways ───────────
   * `.cbui-table-sticky-first`: the first cell of every row is pinned to the left
   * edge of whatever scrolls sideways (the wrapper, or an ancestor with
   * scroller="parent"). With a bulk-select column (`-select`) that checkbox cell is
   * the first cell and the first DATA cell is the second: it is pinned right behind
   * it, at the select column's width plus one gap. Not the empty / group-header /
   * skeleton rows (a single colspan cell) and not column-group header cells.
   * Every pinned cell is OPAQUE (its own fill, or the plane under a ghost striped
   * row), so the scrolled cells never show through. The divider below appears only
   * while the table is scrolled sideways (data-scrolled-x, set by the controller). */
  .cbui-table-sticky-first-select {
    /* Where the first DATA cell is pinned: right behind the pinned select cell. The controller
     * writes the select cell's MEASURED width here (its box differs between fixed and auto
     * layout, and with `flush` the cells have no spacing between them); this is the fallback. */
    --cbui-table-sticky-data-start: var(--cbui-table-select-col-width, 2.75rem);
  }
  .cbui-table-sticky-first-select:not(.cbui-table-flush):not(.cbui-table-surface-rows) {
    --cbui-table-sticky-data-start: calc(var(--cbui-table-select-col-width, 2.75rem) + var(--cbui-table-gap));
  }
  /* The select cell is pinned too: otherwise the gap in front of the pinned data cell is a hole
   * in which the scrolled columns run past. */
  .cbui-table-sticky-first-select > thead > tr > th:first-child:not(.cbui-table-colgroup),
  .cbui-table-sticky-first-select > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :first-child {
    position: sticky;
    left: 0;
    z-index: var(--cbui-table-z-pinned);
  }
  .cbui-table-sticky-first > thead > tr > th:first-child:not(.cbui-table-colgroup),
  .cbui-table-sticky-first > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :first-child,
  .cbui-table-sticky-first-select > thead > tr > th:nth-child(2):not(.cbui-table-colgroup),
  .cbui-table-sticky-first-select > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :nth-child(2) {
    position: sticky;
    left: 0;
    z-index: var(--cbui-table-z-pinned);
  }
  /* Fill the gap around a pinned tile with the plane (as the pinned head does) so the cells that scroll
   * past never show in the 3 px between the pinned column and its neighbour. Not in rows mode (no
   * horizontal spacing there); `flush` fills with its border instead (below). */
  .cbui-table-sticky-first:not(.cbui-table-flush):not(.cbui-table-surface-rows) > thead > tr > th:first-child:not(.cbui-table-colgroup),
  .cbui-table-sticky-first:not(.cbui-table-flush):not(.cbui-table-surface-rows) > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :first-child,
  .cbui-table-sticky-first-select:not(.cbui-table-flush):not(.cbui-table-surface-rows) > thead > tr > th:nth-child(-n+2):not(.cbui-table-colgroup),
  .cbui-table-sticky-first-select:not(.cbui-table-flush):not(.cbui-table-surface-rows) > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :nth-child(-n+2) {
    outline: var(--cbui-table-gap) solid var(--cbui-table-plane-bg);
    outline-offset: 0;
  }
  .cbui-table-sticky-first-select > thead > tr > th:nth-child(2):not(.cbui-table-colgroup),
  .cbui-table-sticky-first-select > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :nth-child(2) {
    left: var(--cbui-table-sticky-data-start);
  }
  /* The top-left corner: pinned in both directions, above the other head cells (2) and
   * the pinned body cells (1). */
  .cbui-table-sticky-first.cbui-table-sticky-header > thead > tr > th:first-child:not(.cbui-table-colgroup),
  .cbui-table-sticky-first-select.cbui-table-sticky-header > thead > tr > th:nth-child(-n+2):not(.cbui-table-colgroup) {
    z-index: var(--cbui-table-z-head-pinned);
  }
  .cbui-table-sticky-first-select.cbui-table-sticky-header > thead > tr > th:nth-child(-n+2) {
    top: 0;
  }
  /* Ghost (striped) rows have no fill of their own: give the pinned cell the plane. */
  .cbui-table-sticky-first.cbui-table-striped > tbody > tr.cbui-table-row:nth-of-type(even):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > :first-child,
  .cbui-table-sticky-first-select.cbui-table-striped > tbody > tr.cbui-table-row:nth-of-type(even):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > :nth-child(-n+2) {
    background-color: var(--cbui-table-plane-bg);
  }
  /* The divider sits on the right edge of the LAST pinned cell. */
  .cbui-table-sticky-first:not(.cbui-table-sticky-first-select)[data-scrolled-x] > thead > tr > th:first-child:not(.cbui-table-colgroup)::after,
  .cbui-table-sticky-first:not(.cbui-table-sticky-first-select)[data-scrolled-x] > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :first-child::after,
  .cbui-table-sticky-first-select[data-scrolled-x] > thead > tr > th:nth-child(2):not(.cbui-table-colgroup)::after,
  .cbui-table-sticky-first-select[data-scrolled-x] > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :nth-child(2)::after {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    width: 1px;
    background: var(--color-border);
    box-shadow: 4px 0 6px -3px color-mix(in srgb, var(--color-text) 22%, transparent);
    pointer-events: none;
  }
  /* ── stickyActions — the actions cell stays at the right edge ───────────────
   * `.cbui-table-sticky-actions`: the last cell of every row that is a TableActionsCell is pinned to the right edge of
   * whatever scrolls sideways (the wrapper, or an ancestor with scroller="parent"); the header cell above it too. The
   * cells are opaque (their own fill), so what scrolls past never shows through. The divider + shadow on the left edge
   * is drawn only while columns lie underneath (the controller sets data-scrollable-end). */
  .cbui-table-sticky-actions > thead > tr > th:last-child:not(.cbui-table-colgroup),
  .cbui-table-sticky-actions > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > td.cbui-table-actions-cell:last-child {
    position: sticky;
    right: 0;
    z-index: var(--cbui-table-z-pinned);
  }
  .cbui-table-sticky-actions.cbui-table-sticky-header > thead > tr > th:last-child:not(.cbui-table-colgroup) {
    z-index: var(--cbui-table-z-head-pinned);
  }
  .cbui-table-sticky-actions:not(.cbui-table-flush):not(.cbui-table-surface-rows) > thead > tr > th:last-child:not(.cbui-table-colgroup),
  .cbui-table-sticky-actions:not(.cbui-table-flush):not(.cbui-table-surface-rows) > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > td.cbui-table-actions-cell:last-child {
    outline: var(--cbui-table-gap) solid var(--cbui-table-plane-bg);
    outline-offset: 0;
  }
  .cbui-table-flush.cbui-table-sticky-actions > thead > tr > th:last-child,
  .cbui-table-flush.cbui-table-sticky-actions > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > td.cbui-table-actions-cell:last-child {
    border-color: var(--cbui-table-plane-bg);
  }
  .cbui-table-sticky-actions.cbui-table-striped > tbody > tr.cbui-table-row:nth-of-type(even):not(.cbui-table-row-active):not(.cbui-table-row-empty):not(.cbui-table-row-skeleton) > td.cbui-table-actions-cell:last-child {
    background-color: var(--cbui-table-plane-bg);
  }
  .cbui-table-sticky-actions[data-scrollable-end] > thead > tr > th:last-child:not(.cbui-table-colgroup)::before,
  .cbui-table-sticky-actions[data-scrollable-end] > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > td.cbui-table-actions-cell:last-child::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: 1px;
    background: var(--color-border);
    box-shadow: -4px 0 6px -3px color-mix(in srgb, var(--color-text) 22%, transparent);
    pointer-events: none;
  }
  /* ── .cbui-table-cell-pinned — any one cell column that stays at the start edge ──────────────
   * A pinned column other than the table's first cell (stickyFirstColumn pins exactly that one): put the class on
   * the header cell and on every body cell of the column. `--cbui-table-pin-start` is its offset from the start
   * edge (0 for the first pinned column, the width of the pinned columns in front of it for the next).
   * `.cbui-table-cell-pinned-edge` on the LAST pinned column draws the divider while the table is scrolled
   * sideways. `--cbui-table-pin-position: static` releases a pinned column (and `--cbui-table-pin-edge-display: none`
   * its divider) for a host that has no room for it. Pinned cells are opaque (their own fill), the stacking order is
   * the one defined at the top. */
  .cbui-table > thead > tr > th.cbui-table-cell-pinned,
  .cbui-table > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > td.cbui-table-cell-pinned {
    position: var(--cbui-table-pin-position, sticky);
    inset-inline-start: var(--cbui-table-pin-start, 0px);
    z-index: var(--cbui-table-z-pinned);
  }
  .cbui-table-sticky-header > thead > tr > th.cbui-table-cell-pinned {
    z-index: var(--cbui-table-z-head-pinned);
  }
  .cbui-table-flush > thead > tr > th.cbui-table-cell-pinned,
  .cbui-table-flush > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > td.cbui-table-cell-pinned {
    border-color: var(--cbui-table-plane-bg);
  }
  .cbui-table:not(.cbui-table-flush):not(.cbui-table-surface-rows) > thead > tr > th.cbui-table-cell-pinned,
  .cbui-table:not(.cbui-table-flush):not(.cbui-table-surface-rows) > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > td.cbui-table-cell-pinned {
    outline: var(--cbui-table-gap) solid var(--cbui-table-plane-bg);
    outline-offset: 0;
  }
  .cbui-table[data-scrolled-x] > thead > tr > th.cbui-table-cell-pinned-edge::after,
  .cbui-table[data-scrolled-x] > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > td.cbui-table-cell-pinned-edge::after {
    content: "";
    display: var(--cbui-table-pin-edge-display, block);
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    width: 1px;
    background: var(--color-border);
    box-shadow: 4px 0 6px -3px color-mix(in srgb, var(--color-text) 22%, transparent);
    pointer-events: none;
  }
  /* ── flush — 1 px to the container on every side ────────────────────────────
   * The table normally has the full `gap` around it too (border-spacing runs along the
   * outside). `flush` swaps the spacing for a transparent border of half the gap on each
   * cell: between two cells that adds up to the gap again, at the table's four outer edges
   * only the half is left, and there it is cut to 1 px. Fills are clipped to the padding box
   * so the border stays transparent. `!important` is needed once: every fill rule above is a
   * `background:` shorthand, which resets the clip, at specificities up to (0,7,3). */
  .cbui-table.cbui-table-flush {
    --cbui-table-flush-half: calc(var(--cbui-table-gap) / 2);
    --cbui-table-flush-edge: 1px;
    border-spacing: 0;
  }
  .cbui-table-flush > * > tr > * {
    border: var(--cbui-table-flush-half) solid transparent;
    background-clip: padding-box !important;
  }
  /* surface="rows": the band is continuous across the row, only the rows are spaced apart. */
  .cbui-table-flush.cbui-table-surface-rows > * > tr > * {
    border-inline-width: 0;
  }
  .cbui-table-flush > :first-child > tr:first-child > * { border-block-start-width: var(--cbui-table-flush-edge); }
  .cbui-table-flush > :last-child > tr:last-child > *   { border-block-end-width: var(--cbui-table-flush-edge); }
  .cbui-table-flush > * > tr > :nth-child(1 of :not([data-col-hidden])) {
    border-inline-start-width: var(--cbui-table-flush-edge);
  }
  .cbui-table-flush > * > tr > :nth-last-child(1 of :not(.cbui-table-cell-collapsed):not([data-col-hidden])) {
    border-inline-end-width: var(--cbui-table-flush-edge);
  }
  /* A pinned cell must hide what scrolls under it, border included: plane colour, not
   * transparent (the head ring above is not needed then). */
  .cbui-table-flush.cbui-table-sticky-header > thead > tr > th {
    outline: 0;
    border-color: var(--cbui-table-plane-bg);
  }
  .cbui-table-flush.cbui-table-sticky-first > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :first-child,
  .cbui-table-flush.cbui-table-sticky-first > thead > tr > th:first-child,
  .cbui-table-flush.cbui-table-sticky-first-select > tbody > tr:not(.cbui-table-row-empty):not(.cbui-table-row-skeleton):not(.cbui-table-group-header) > :nth-child(-n+2),
  .cbui-table-flush.cbui-table-sticky-first-select > thead > tr > th:nth-child(-n+2) {
    border-color: var(--cbui-table-plane-bg);
  }
  /* A ListDetail list pane insets a table by 0.5rem; a flush table wants none. */
  .cbui-list-detail-list > .cbui-table-wrap.cbui-table-wrap-flush {
    padding: 0;
  }

  /* ── Empty + loading rows ────────────────────────────────────────────── */
  .cbui-table-empty-cell {
    padding: 2.25rem var(--cbui-table-cell-px);
    text-align: center;
    color: var(--color-text-secondary);
    background: transparent;
  }
  .cbui-table-row-empty > td,
  .cbui-table-row-empty > th {
    background: transparent;
  }
  .cbui-table-row-skeleton > td,
  .cbui-table-row-skeleton > th {
    background: var(--cbui-table-body-bg);
    vertical-align: middle;
  }

  /* ── Density tiers — mutex axis, runtime-swappable ───────────────────── */
  .cbui-table-density-compact     { --cbui-table-cell-py: 0.25rem; }
  .cbui-table-density-comfortable { --cbui-table-cell-py: 0.5625rem; }
  .cbui-table-density-spacious    { --cbui-table-cell-py: 0.875rem; }
  .cbui-table-density-spacious    { line-height: 1.55; }
  /* ── Font size — the table's OWN type tokens ─────────────────────────────
   * The default is md (normal); sm is the user's choice at the display switch,
   * never the author's. Density never touches the type (padding only).
   * `fontSize` / the runtime switch only swap a class that sets the two table
   * tokens; the global `--font-size-*` scale is never re-pointed, so badges and
   * buttons in cells keep their own size.
   *   sm: cells sm, head sm
   *   md: cells md, head md (also the look without a font class)
   *   lg: cells lg, head md */
  .cbui-table.cbui-table-font-sm {
    --cbui-table-font-size: var(--font-size-sm, 0.875rem);
    --cbui-table-head-font-size: var(--font-size-sm, 0.875rem);
  }
  .cbui-table.cbui-table-font-md {
    --cbui-table-font-size: var(--font-size-md, 1rem);
    --cbui-table-head-font-size: var(--font-size-md, 1rem);
  }
  .cbui-table.cbui-table-font-lg {
    --cbui-table-font-size: var(--font-size-lg, 1.125rem);
    --cbui-table-head-font-size: var(--font-size-md, 1rem);
  }

  /* ── Responsive: cards — each row collapses to a stacked card ────────────
   * Container-query, not viewport — works with the Storybook responsive-preview
   * slider (which resizes the wrapper, not the viewport). */
  @container (max-width: 38rem) {
    .cbui-table-responsive-cards {
      display: block;
      border-spacing: 0;
    }
    .cbui-table-responsive-cards > thead { display: none; }
    .cbui-table-responsive-cards > tbody { display: block; }
    .cbui-table-responsive-cards > tbody > tr > td.cbui-table-cell-handle { display: none; }
    .cbui-table-responsive-cards > caption { display: block; padding-inline: 0; }

    .cbui-table-responsive-cards > tbody > tr.cbui-table-row {
      display: block;
      margin-bottom: var(--cbui-table-gap);
      background: var(--cbui-table-card-bg);
      box-shadow: inset 0 0 0 1px var(--cbui-table-card-ring);
      border-radius: var(--cbui-table-corner-radius);
      overflow: hidden;
    }
    .cbui-table-responsive-cards > tbody > tr.cbui-table-row:last-child { margin-bottom: 0; }

    .cbui-table-responsive-cards > tbody > tr > td,
    .cbui-table-responsive-cards > tbody > tr > th {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 1rem;
      padding: 0.5rem 0.75rem;
      background: transparent;
      border-radius: 0;
      text-align: end;
    }
    .cbui-table-responsive-cards > tbody > tr > td[data-label]::before,
    .cbui-table-responsive-cards > tbody > tr > th[data-label]::before {
      content: attr(data-label);
      flex-shrink: 0;
      color: var(--color-text-secondary);
      font-size: var(--cbui-table-head-font-size);
      font-weight: var(--font-weight-semibold, 600);
      text-align: start;
    }
    /* A card has the whole line for its actions (label on the left, actions on the
     * right), so the box may take more than the table default of 30cqi. */
    .cbui-table-responsive-cards {
      --cbui-table-actions-max: 60cqi;
    }
    /* The actions td is a flex row here: its 1% column width would squeeze it. */
    .cbui-table-responsive-cards > tbody > tr > td.cbui-table-actions-cell {
      width: auto;
    }
    /* The active accent moves to a top edge inside the card. */
    .cbui-table-responsive-cards > tbody > tr.cbui-table-row-active > td,
    .cbui-table-responsive-cards > tbody > tr.cbui-table-row-active > th {
      background: transparent;
      color: var(--cbui-table-row-active-fg);
    }
    .cbui-table-responsive-cards > tbody > tr.cbui-table-row-active {
      background: var(--cbui-table-row-active-bg);
    }

    .cbui-table-responsive-cards > tbody > tr.cbui-table-row-empty,
    .cbui-table-responsive-cards > tbody > tr.cbui-table-row-skeleton {
      background: transparent;
      border-radius: 0;
      box-shadow: none;
    }
    .cbui-table-responsive-cards .cbui-table-empty-cell { display: block; }
    .cbui-table-responsive-cards > tbody > tr.cbui-table-row-skeleton > td {
      display: block;
    }
  }

  /* ── Column choice (CB-1954) ─────────────────────────────────────────────
   * The controller marks a switched-off column on its header cell and on every
   * body cell of that column. Two-class specificity so it also beats the flex
   * display the cards fallback gives a cell. */
  .cbui-table [data-col-hidden] {
    display: none;
  }

  /* The four outer corners round on :first-child / :last-child (see above).
   * With an edge column switched off the VISIBLE edge cell carries them, so the
   * controller flags it (data-col-first / data-col-last). */
  .cbui-table[data-columns-filtered] > thead:first-child > tr:first-child > [data-col-first],
  .cbui-table[data-columns-filtered] > tbody:first-child > tr:first-child > [data-col-first] {
    border-top-left-radius: var(--cbui-table-corner-radius);
  }
  .cbui-table[data-columns-filtered] > thead:first-child > tr:first-child > [data-col-last],
  .cbui-table[data-columns-filtered] > tbody:first-child > tr:first-child > [data-col-last] {
    border-top-right-radius: var(--cbui-table-corner-radius);
  }
  .cbui-table[data-columns-filtered] > tfoot:last-child > tr:last-child > [data-col-first],
  .cbui-table[data-columns-filtered] > tbody:last-child > tr:last-child > [data-col-first] {
    border-bottom-left-radius: var(--cbui-table-corner-radius);
  }
  .cbui-table[data-columns-filtered] > tfoot:last-child > tr:last-child > [data-col-last],
  .cbui-table[data-columns-filtered] > tbody:last-child > tr:last-child > [data-col-last] {
    border-bottom-right-radius: var(--cbui-table-corner-radius);
  }

  /* ── Responsive: list — opt-in, a row becomes a ListItem (CB-1954) ───────
   * Same 38rem CONTAINER threshold as cards. The caller supplies the ListItem
   * (TableRow block listItem), rendered as one extra trailing td that stays
   * hidden while the table is wide; narrow, the data cells hide and the ListItem
   * plus the row's own TableActionsCell form a flex row (the ListItem row
   * hierarchy: label, description, trailing badge). */
  .cbui-table > tbody > tr > td.cbui-table-list-item-cell { display: none; }
  /* The host is a ListGroup root (`cbui-list`) so the ListItem inside finds its own
   * tokens (item padding, base-size label); the table's small font would
   * otherwise win. */
  .cbui-table-list-item-host {
    list-style: none;
    margin: 0;
    padding: 0;
  }
  /* The hidden list-item td is the row's :last-child, so the VISIBLE edge cell
   * carries the right-hand outer corners. */
  .cbui-table > tbody:first-child > tr:first-child > td:has(+ td.cbui-table-list-item-cell) {
    border-top-right-radius: var(--cbui-table-corner-radius);
  }
  .cbui-table > tbody:last-child > tr:last-child > td:has(+ td.cbui-table-list-item-cell) {
    border-bottom-right-radius: var(--cbui-table-corner-radius);
  }
  @container (max-width: 38rem) {
    .cbui-table-responsive-list {
      display: block;
      border-spacing: 0;
    }
    .cbui-table-responsive-list > thead { display: none; }
    .cbui-table-responsive-list > tbody { display: block; }
    .cbui-table-responsive-list > caption { display: block; padding-inline: 0; }

    .cbui-table-responsive-list > tbody > tr.cbui-table-row {
      display: flex;
      align-items: center;
      background: transparent;
      border-bottom: 1px solid var(--cbui-table-narrow-edge);
    }
    .cbui-table-responsive-list > tbody > tr.cbui-table-row:last-child { border-bottom: 0; }
    .cbui-table-responsive-list > tbody > tr.cbui-table-row-clickable,
    .cbui-table-responsive-list > tbody > tr.cbui-table-row[data-action] { cursor: pointer; }

    .cbui-table-responsive-list > tbody > tr > td,
    .cbui-table-responsive-list > tbody > tr > th {
      display: none;
    }
    .cbui-table-responsive-list > tbody > tr > td.cbui-table-list-item-cell {
      display: block;
      order: 1;
      flex: 1 1 auto;
      min-width: 0;
      padding: 0;
      background: transparent;
      border-radius: 0;
      box-shadow: none;
    }
    .cbui-table-responsive-list > tbody > tr > td.cbui-table-actions-cell,
    .cbui-table-responsive-list > tbody > tr > td.cbui-table-cell-select {
      display: block;
      flex: 0 0 auto;
      width: auto;
      background: transparent;
      border-radius: 0;
      box-shadow: none;
    }
    /* The ListItem comes first, the actions sit at the trailing edge (the DOM
     * puts the data cells, the actions cell and the list-item cell in this order). */
    .cbui-table-responsive-list > tbody > tr > td.cbui-table-actions-cell { order: 2; }

    /* Hover / active / selected read like a ListItem: a wash on the row. */
    .cbui-table-hover .cbui-table-responsive-list > tbody > tr.cbui-table-row:not(.cbui-table-row-skeleton):not(.cbui-table-row-empty):hover {
      background: var(--cbui-table-row-hover-bg);
    }
    .cbui-table-responsive-list > tbody > tr.cbui-table-row[aria-selected="true"],
    .cbui-table-responsive-list > tbody > tr.cbui-table-row.cbui-table-row-selected {
      background: color-mix(in srgb, var(--color-primary) 14%, transparent);
    }
    [data-scheme="dark"] .cbui-table-responsive-list > tbody > tr.cbui-table-row[aria-selected="true"],
    [data-scheme="dark"] .cbui-table-responsive-list > tbody > tr.cbui-table-row.cbui-table-row-selected,
    :root[data-color-scheme="dark"] .cbui-table-responsive-list > tbody > tr.cbui-table-row[aria-selected="true"],
    :root[data-color-scheme="dark"] .cbui-table-responsive-list > tbody > tr.cbui-table-row.cbui-table-row-selected,
    .dark .cbui-table-responsive-list > tbody > tr.cbui-table-row[aria-selected="true"],
    .dark .cbui-table-responsive-list > tbody > tr.cbui-table-row.cbui-table-row-selected {
      background: color-mix(in srgb, var(--color-primary) 26%, transparent);
    }
    .cbui-table-responsive-list > tbody > tr.cbui-table-row-active {
      background: var(--cbui-table-row-active-bg);
      color: var(--cbui-table-row-active-fg);
    }

    .cbui-table-responsive-list > tbody > tr.cbui-table-row-empty,
    .cbui-table-responsive-list > tbody > tr.cbui-table-row-skeleton {
      display: block;
      padding: 0;
      border-bottom: 0;
    }
    .cbui-table-responsive-list .cbui-table-empty-cell { display: block; }
    .cbui-table-responsive-list > tbody > tr.cbui-table-row-skeleton > td { display: block; padding: 0.5rem 0.75rem; }
  }

  /* ── responsive="priority" — measured column collapse (CB-1917) ─────────
   * The controller toggles `.cbui-table-cell-collapsed` on individual cells
   * (head + body, kept in index sync) once the table stops fitting its
   * wrapper or the `fill` column drops below `--cbui-table-fill-min`. Pure
   * `display: none` — no width is guessed here, the JS measures the real
   * layout. `--cbui-table-fill-min` is overridable per table via inline
   * style same as `--cbui-table-gap`. */
  .cbui-table-responsive-priority { --cbui-table-fill-min: 10rem; }
  .cbui-table-responsive-priority .cbui-table-cell-collapsed { display: none; }
  .cbui-table-header-cell-fill { width: 100%; }
  /* With `surface="rows"` the trailing radius sits on the LAST cell — which
     may now be collapsed. Re-target it at the last cell that is actually
     visible so the row still rounds off correctly regardless of which
     columns are hidden. */
  .cbui-table-responsive-priority.cbui-table-surface-rows > * > tr > :nth-last-child(1 of :not(.cbui-table-cell-collapsed)) {
    border-start-end-radius: var(--cbui-table-row-radius);
    border-end-end-radius: var(--cbui-table-row-radius);
  }

  /* ── Rich cell content — icon / truncate / link / trail (CB-1917) ───────
   * Only present when TableCell renders its "rich" branch (icon, truncate,
   * href or a trail slot are set) — the plain default path stays untouched
   * text with no wrapper at all. */
  .cbui-table-cell-truncate { max-width: 0; white-space: nowrap; }
  .cbui-table-cell-content { display: flex; align-items: center; gap: 0.5em; min-width: 0; }
  .cbui-table-cell-link { color: inherit; text-decoration: none; }
  .cbui-table-cell-link:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; border-radius: var(--radius-sm, 0.25rem); }
  .cbui-table-cell-icon { flex: none; width: 1.25em; height: 1.25em; color: var(--color-text-secondary); }
  .cbui-table-row-active .cbui-table-cell-icon, .cbui-table-row-active .cbui-table-cell-trail { color: inherit; }
  .cbui-table-cell-text { min-width: 0; }
  .cbui-table-cell-truncate .cbui-table-cell-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cbui-table-cell-trail { flex: none; display: inline-flex; color: var(--color-text-tertiary); }

  /* ── In-place cell editor (CB-1922) ─────────────────────────────────────
   * TableCell's `editor` slot lays a control OVER the text of a rich cell.
   * Text and trail keep their box (visibility, never display) so the row
   * height and all other rows cannot move; the icon stays visible.
   * The 1.25em / 0.5em below must equal the icon width and the
   * content gap above. */
  .cbui-table-cell:has(> .cbui-table-cell-editor) { position: relative; }
  .cbui-table-cell:has(> .cbui-table-cell-editor) > .cbui-table-cell-content > .cbui-table-cell-text,
  .cbui-table-cell:has(> .cbui-table-cell-editor) > .cbui-table-cell-content > .cbui-table-cell-trail { visibility: hidden; }
  .cbui-table-cell-editor { position: absolute; inset-block: 2px; inset-inline: var(--cbui-table-cell-px); display: flex; align-items: center; min-width: 0; }
  .cbui-table-cell:has(> .cbui-table-cell-content > .cbui-table-cell-icon) > .cbui-table-cell-editor { inset-inline-start: calc(var(--cbui-table-cell-px) + 1.25em + 0.5em); }
  .cbui-table-cell-editor > * { flex: 1 1 auto; min-width: 0; max-block-size: 100%; }

  /* ── Grid mode (CB-4108) ────────────────────────────────────────────────
   * The cell is the focus stop. :focus (not :focus-visible) so the cell
   * cursor also shows after a mouse click, like in a spreadsheet. */
  .cbui-table-grid [data-cbui-grid-col]:focus { outline: 2px solid var(--color-focus-ring); outline-offset: -2px; }
  .cbui-table-grid [data-cbui-grid-state="saving"] > .cbui-table-cell-editor { opacity: .6; cursor: progress; }
  .cbui-table-grid-hint-danger { background-color: var(--color-danger); color: var(--color-text-on-danger); }

  /* ── Accessibility ───────────────────────────────────────────────────── */
  .a11y-reduced-motion .cbui-table > tbody > tr.cbui-table-row > td,
  .a11y-reduced-motion .cbui-table > tbody > tr.cbui-table-row > th,
  .a11y-reduced-motion .cbui-table-sort { transition: none; }

  /* High contrast — re-add a SOLID (not dashed) ring on every cell plus a
     wider accent bar, so selection is unmistakable. */
  .a11y-high-contrast .cbui-table > tbody > tr.cbui-table-row-selected > td,
  .a11y-high-contrast .cbui-table > tbody > tr.cbui-table-row-selected > th {
    outline: 2px solid var(--color-primary);
    outline-offset: -2px;
  }
  .a11y-high-contrast .cbui-table-row-selected > *:first-child {
    --cbui-table-row-selected-bar-width: 5px;
  }
  .a11y-high-contrast .cbui-table-row-active > td,
  .a11y-high-contrast .cbui-table-row-active > th {
    --cbui-table-row-active-fg: #fff;
  }

  /* ── Actions cell (CB-1697, CB-1954) ──────────────────────────────────────
   * The actions are ALWAYS visible (no hover reveal): ListItem's own classes and
   * both Stimulus controllers do the folding, this section only gives the box a
   * width that does NOT depend on its content. Why: with table-layout:auto a
   * content-sized box re-runs the column widths whenever it changes (measured on
   * hover, 98 → 218 px, every other column shifted). The box width is therefore
   * a function of the table container only: the natural width of N actions,
   * capped at --cbui-table-actions-max (default 30cqi of .cbui-table-wrap), floor
   * --cbui-table-actions-floor (default one action + "…"; a host that wants the "…" alone sets it to one icon button). The overflow controller observes the box and folds or
   * unfolds as the container grows or shrinks. */
  .cbui-table-actions-cell {
    width: 1%;
  }
  .cbui-table-actions-cell > .cbui-list-item-actions {
    display: flex;
  }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow {
    --cbui-table-actions-n: 1;
    box-sizing: border-box;
    width: min(
      calc(
        var(--cbui-table-actions-n) * var(--cbui-button-icon-size, 2.5rem)
        + (var(--cbui-table-actions-n) - 1) * var(--cbui-list-item-actions-gap, 0.375rem)
        + var(--cbui-list-item-px, 0.75rem)
      ),
      max(
        var(--cbui-table-actions-floor, calc(
          2 * var(--cbui-button-icon-size, 2.5rem)
          + var(--cbui-list-item-actions-gap, 0.375rem)
          + var(--cbui-list-item-px, 0.75rem)
        )),
        var(--cbui-table-actions-max, 30cqi)
      )
    );
    /* ListItem's cap (calc(100% - label-min)) goes negative inside a td. */
    max-width: 100%;
    margin-inline-start: auto;
    /* Rows far outside the viewport are not measured yet (the overflow controller
     * measures what is near the viewport), so their actions still sit unfolded and
     * would overflow this fixed-width box to the right and widen the whole table
     * wrapper (a horizontal scroll region, and a jump when the row is measured).
     * Clip them; the controller reads scrollWidth, which clip does not change. */
    overflow: clip;
    overflow-clip-margin: 0.375rem;
    /* NOT plain flex-end: start-side overflow is invisible to scrollWidth, so
     * nothing would ever fold. */
    justify-content: safe flex-end;
  }
  /* N = the number of action sources. A source may be a Tooltip span wrapper;
   * the generated "…" trigger is excluded. Rows carry up to 10 actions. */
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(2 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 2; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(3 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 3; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(4 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 4; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(5 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 5; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(6 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 6; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(7 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 7; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(8 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 8; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(9 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 9; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(10 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 10; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(11 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 11; }
  .cbui-table-actions-cell > .cbui-list-item-actions-overflow:has(> :nth-child(12 of :not([data-list-actions-generated]))) { --cbui-table-actions-n: 12; }

  /* ── RecordTable (CB-2258) ──────────────────────────────────────────────
   * The root of UiData:RecordTable is the scroll container of its Table (scroller="parent"), so the sticky header
   * sticks to it. The consumer gives it a height (a flex child, or `maxHeight`). */
  .cbui-record-table {
    position: relative;
    min-width: 0;
    min-height: 0;
    max-width: 100%;
    overflow: auto;
  }

/* CB-2610 — the header line of a group of rows (`groupHeaders`). Drawn by
 * `controllers/ui-data/group_headers.js` as a one-cell row between the body rows. */
.cbui-table > tbody > tr.cbui-table-group-header {
  display: table-row;
}
.cbui-table > tbody > tr.cbui-table-group-header > td,
.cbui-table > tbody > tr.cbui-table-group-header > th {
  text-align: start;
  padding: var(--spacing-sm, 0.5rem) var(--spacing-md, 0.75rem) var(--spacing-xs, 0.25rem);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--cbui-table-head-font-size);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-text-secondary);
  user-select: none;
  pointer-events: none;
}

/* TableColumnPicker — three column states and the count on the button (priority tables). */
/* A scoped picker exists for the display „tabelle“ only: its controller sets this attribute on the bar form and on the twin in the
   ⋯ menu (the overflow controller owns `hidden` and the inline display of both). */
.cbui-table-column-picker[data-cbui-display-hidden],
[data-overflow-key="columns"][data-cbui-display-hidden] {
  display: none !important;
}
/* Icon-only in a menubar: the count "5 von 12" stays in the tooltip and the accessible name. */
[data-ui-data--column-picker-scope-value] .cbui-table-column-picker-count {
  display: none;
}
.cbui-table-column-picker-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.125rem;
}
.cbui-table-column-picker-count {
  font-size: var(--font-size-md, 1rem);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-secondary);
  white-space: nowrap;
  padding-inline-end: 0.25rem;
}
.cbui-table-column-picker-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  min-width: 0;
}
.cbui-table-column-picker-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cbui-table-column-picker-readout { font-size: var(--font-size-md, 1rem); }
.cbui-table-column-picker-notes {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
}
.cbui-table-column-picker-nospace,
.cbui-table-column-picker-pin {
  display: none;
  align-items: center;
  gap: 0.25rem;
  font-size: var(--font-size-md, 1rem);
  white-space: nowrap;
}
.cbui-table-column-picker-nospace { color: var(--color-text-secondary); }
.cbui-table-column-picker-nospace svg,
.cbui-table-column-picker-pin svg { width: 0.875rem; height: 0.875rem; }
.cbui-table-column-picker-pin {
  padding: 0.0625rem 0.375rem;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  color: var(--color-text);
  cursor: pointer;
}
.cbui-table-column-picker-pin:hover { background: color-mix(in srgb, var(--color-text) 8%, transparent); }
[data-col-state="nospace"] .cbui-table-column-picker-nospace,
[data-col-state="nospace"] .cbui-table-column-picker-pin,
[data-col-pinned]:not([data-col-state="off"]) .cbui-table-column-picker-pin { display: inline-flex; }
[data-col-pinned] .cbui-table-column-picker-pin {
  border-color: var(--color-primary);
  color: var(--color-primary);
}
[data-col-state="nospace"] .cbui-table-column-picker-name { opacity: 0.6; }
[data-col-state="nospace"] .cbui-dropdown-item-check { opacity: 0.45; }

/* Tree of rows (TableRow depth / expanded + TableBranchToggle): the indent is the row's depth in steps of the chevron's
 * width, the chevron turns right while the branch is folded. A folded-away row is [hidden]. */
.cbui-table > tbody > tr.cbui-table-row[hidden] { display: none; }
.cbui-table-branch {
  --cbui-table-branch-size: 1.5rem;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  padding-inline-start: calc(var(--cbui-table-branch-depth, 0) * var(--cbui-table-branch-size));
}
.cbui-table-branch-spacer { display: inline-block; width: var(--cbui-table-branch-size); height: var(--cbui-table-branch-size); }
.cbui-table-branch-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--cbui-table-branch-size);
  height: var(--cbui-table-branch-size);
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm, 0.25rem);
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
}
.cbui-table-branch-toggle:hover { background: var(--color-bg-muted); color: var(--color-text); }
.cbui-table-branch-toggle:focus-visible { outline: 2px solid var(--color-focus-ring, var(--color-primary)); outline-offset: 1px; }
.cbui-table-branch-toggle[aria-expanded="false"] .cbui-table-branch-icon { transform: rotate(-90deg); }

/* cbui/components/timeline */
/* cbui-timeline — a vertical event / activity feed (UiData, Wave 4).
 *
 * A single continuous connector rail is drawn once, as a ::before on the
 * .cbui-timeline <ol>, spanning the whole list (including grouped items).
 * Markers sit on top of the rail; their surface fill visually breaks the
 * line so the rail reads as "connecting" the events.
 *
 * Token model (overridable by the consumer — e.g. when the timeline sits on
 * a tinted Card, set --cbui-timeline-marker-bg to that surface):
 *   --cbui-timeline-gutter        left column reserved for marker + rail
 *   --cbui-timeline-marker-size   marker diameter
 *   --cbui-timeline-marker-bg     marker fill that "cuts" the rail
 *   --cbui-timeline-item-gap      vertical space between events (density)
 */

@layer components {

  .cbui-timeline {
    --cbui-timeline-gutter:      2.75rem;
    --cbui-timeline-marker-size: 2rem;
    --cbui-timeline-marker-bg:   var(--color-bg-base);
    --cbui-timeline-rail-color:  var(--color-border);
    --cbui-timeline-item-gap:    1.25rem;

    position: relative;
    margin: 0;
    padding: 0;
    list-style: none;
    color: var(--color-text);
  }

  /* The continuous connector rail. */
  .cbui-timeline::before {
    content: '';
    position: absolute;
    inset-block: 0;
    inset-inline-start: calc(var(--cbui-timeline-marker-size) / 2 - 1px);
    width: 2px;
    background-color: var(--cbui-timeline-rail-color);
    border-radius: 1px;
  }
  .cbui-timeline-connector-dashed::before {
    background-color: transparent;
    background-image: repeating-linear-gradient(
      to bottom,
      var(--cbui-timeline-rail-color) 0,
      var(--cbui-timeline-rail-color) 4px,
      transparent 4px,
      transparent 8px
    );
  }
  .cbui-timeline-connector-none::before { display: none; }

  /* The rail stops cleanly at the first and last marker centre. */
  .cbui-timeline > .cbui-timeline-item:first-child,
  .cbui-timeline > .cbui-timeline-group:first-child { padding-top: 0; }

  /* ── Density ───────────────────────────────────────────────────────────── */
  .cbui-timeline-density-compact     { --cbui-timeline-item-gap: 0.75rem; }
  .cbui-timeline-density-comfortable { --cbui-timeline-item-gap: 1.25rem; }
  .cbui-timeline-density-spacious    { --cbui-timeline-item-gap: 1.875rem; }

  /* ── Item ──────────────────────────────────────────────────────────────── */
  .cbui-timeline-item {
    position: relative;
    display: block;
    padding-inline-start: var(--cbui-timeline-gutter);
    padding-block-end: var(--cbui-timeline-item-gap);
  }
  .cbui-timeline-item:last-child { padding-block-end: 0; }
  /* A nested group's last item still needs trailing space inside the group. */
  .cbui-timeline-group-items .cbui-timeline-item:last-child {
    padding-block-end: var(--cbui-timeline-item-gap);
  }
  .cbui-timeline-group:last-child .cbui-timeline-group-items .cbui-timeline-item:last-child {
    padding-block-end: 0;
  }

  /* ── Marker ────────────────────────────────────────────────────────────── */
  .cbui-timeline-item-marker {
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: 0;
    width: var(--cbui-timeline-marker-size);
    height: var(--cbui-timeline-marker-size);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 9999px;
    color: var(--color-text-secondary);
  }
  /* dot + ring draw a smaller shape via ::before, centred in the marker box,
     with a surface-coloured ring so they cut the rail cleanly. */
  .cbui-timeline-item-marker-dot .cbui-timeline-item-marker::before,
  .cbui-timeline-item-marker-ring .cbui-timeline-item-marker::before {
    content: '';
    box-sizing: border-box;
    border-radius: 9999px;
    box-shadow: 0 0 0 4px var(--cbui-timeline-marker-bg);
  }
  .cbui-timeline-item-marker-dot .cbui-timeline-item-marker::before {
    width: 0.625rem;
    height: 0.625rem;
    background-color: var(--cbui-timeline-tone, var(--color-text-tertiary, var(--color-text-secondary)));
  }
  .cbui-timeline-item-marker-ring .cbui-timeline-item-marker::before {
    width: 0.875rem;
    height: 0.875rem;
    background-color: var(--cbui-timeline-marker-bg);
    border: 2px solid var(--cbui-timeline-tone, var(--color-text-tertiary, var(--color-text-secondary)));
  }
  /* icon + avatar fill the whole marker box. */
  .cbui-timeline-item-marker-icon .cbui-timeline-item-marker {
    background-color: var(--cbui-timeline-marker-bg);
    border: 1.5px solid color-mix(in srgb, var(--cbui-timeline-tone, var(--color-border)) 55%, var(--color-border));
    color: var(--cbui-timeline-tone, var(--color-text-secondary));
    box-shadow: 0 0 0 4px var(--cbui-timeline-marker-bg);
  }
  .cbui-timeline-item-marker-glyph {
    width: 1.05rem;
    height: 1.05rem;
  }
  .cbui-timeline-item-marker-avatar .cbui-timeline-item-marker {
    background-color: var(--cbui-timeline-marker-bg);
    box-shadow: 0 0 0 4px var(--cbui-timeline-marker-bg);
    overflow: hidden;
  }
  .cbui-timeline-item-marker-avatar .cbui-timeline-item-marker > img,
  .cbui-timeline-item-marker-avatar .cbui-timeline-item-marker > svg {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 9999px;
  }

  /* ── Marker tones ──────────────────────────────────────────────────────── */
  .cbui-timeline-item-tone-primary { --cbui-timeline-tone: var(--color-primary); }
  .cbui-timeline-item-tone-accent  { --cbui-timeline-tone: var(--color-accent); }
  .cbui-timeline-item-tone-success { --cbui-timeline-tone: var(--color-success); }
  .cbui-timeline-item-tone-danger  { --cbui-timeline-tone: var(--color-danger); }
  .cbui-timeline-item-tone-warning { --cbui-timeline-tone: var(--color-warning); }
  .cbui-timeline-item-tone-info    { --cbui-timeline-tone: var(--color-info); }
  .cbui-timeline-item-tone-muted   { --cbui-timeline-tone: var(--color-text-secondary); }
  /* icon markers get a tone-tinted fill so they read as semantic. */
  .cbui-timeline-item-marker-icon.cbui-timeline-item-tone-primary .cbui-timeline-item-marker,
  .cbui-timeline-item-marker-icon.cbui-timeline-item-tone-accent  .cbui-timeline-item-marker,
  .cbui-timeline-item-marker-icon.cbui-timeline-item-tone-success .cbui-timeline-item-marker,
  .cbui-timeline-item-marker-icon.cbui-timeline-item-tone-danger  .cbui-timeline-item-marker,
  .cbui-timeline-item-marker-icon.cbui-timeline-item-tone-warning .cbui-timeline-item-marker,
  .cbui-timeline-item-marker-icon.cbui-timeline-item-tone-info    .cbui-timeline-item-marker {
    background-color: color-mix(in srgb, var(--cbui-timeline-tone) 14%, var(--cbui-timeline-marker-bg));
  }

  /* ── Content ───────────────────────────────────────────────────────────── */
  .cbui-timeline-item-content {
    display: flex;
    flex-direction: column;
    gap: 0.1875rem;
    /* Pull the content's optical top in line with the marker centre. */
    padding-block-start: calc((var(--cbui-timeline-marker-size) - 1.4em) / 2);
    color: inherit;
    text-decoration: none;
  }

  .cbui-timeline-item-title {
    font-weight: var(--font-weight-semibold, 600);
    color: var(--color-text);
    line-height: 1.4;
  }
  .cbui-timeline-item-meta {
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
  }
  .cbui-timeline-item-description {
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
    line-height: 1.5;
  }
  .cbui-timeline-item-detail {
    margin-block-start: 0.375rem;
    padding: 0.5rem 0.75rem;
    border-radius: var(--radius-md, 0.5rem);
    background-color: var(--color-bg-subtle);
    border: 1px solid var(--cbui-timeline-rail-color);
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text-secondary);
  }
  /* A head row helper — title + meta on one line, meta pushed to the end. */
  .cbui-timeline-item-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
  }

  /* Entry actions (CB-1958) — icon buttons at the end of the head row, always visible
     (no hover reveal). The head row aligns on the baseline; buttons are taller than
     text, so the group centres itself on the row. */
  .cbui-timeline-item-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    flex-shrink: 0;
    align-self: center;
  }

  /* Head row rendered by the component (CB-2906, title prop and actions slot): it may wrap, so a
     long title breaks inside its own box (never under the actions) and, when the container is too
     narrow for title plus actions, the group drops under the title — still at the right edge.
     The actions never shrink. Container-driven by flex-wrap, not by viewport. */
  .cbui-timeline-item-head-own {
    flex-wrap: wrap;
    align-items: center;
    row-gap: var(--space-1, 0.25rem);
  }
  .cbui-timeline-item-head-own > .cbui-timeline-item-title {
    flex: 1 1 10rem;
    min-width: 0;
    overflow-wrap: anywhere;
  }
  .cbui-timeline-item-head-own > .cbui-timeline-item-actions {
    margin-inline-start: auto;
  }

  /* Prose body (CB-1958) — rich text (paragraphs, lists, headings) rendered inside an
     event. Token-only; the first/last child margins are removed so the block sits flush. */
  .cbui-timeline-item-body {
    font-size: var(--font-size-md, 1rem);
    color: var(--color-text);
    line-height: 1.55;
    overflow-wrap: anywhere;
  }
  .cbui-timeline-item-body :is(p, ul, ol) { margin: 0 0 0.375rem; }
  .cbui-timeline-item-body :is(ul, ol) { padding-inline-start: 1.25rem; }
  /* The reset strips list markers; a rich-text list must keep them. */
  .cbui-timeline-item-body ul { list-style: disc; }
  .cbui-timeline-item-body ol { list-style: decimal; }
  .cbui-timeline-item-body li > p { margin: 0; }
  .cbui-timeline-item-body :is(h1, h2, h3) {
    margin: 0.5rem 0 0.25rem;
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-semibold, 600);
    line-height: 1.4;
  }
  .cbui-timeline-item-body :is(h2, h3) { font-size: var(--font-size-md, 1rem); }
  .cbui-timeline-item-body > :first-child { margin-block-start: 0; }
  .cbui-timeline-item-body > :last-child { margin-block-end: 0; }

  /* ── Clickable item ────────────────────────────────────────────────────── */
  .cbui-timeline-item-clickable .cbui-timeline-item-content {
    margin: calc((var(--cbui-timeline-marker-size) - 1.4em) / 2 * -1) -0.625rem 0;
    padding-inline: 0.625rem;
    padding-block-end: 0.5rem;
    border-radius: var(--radius-md, 0.5rem);
    transition: background-color 120ms ease;
    cursor: pointer;
  }
  .cbui-timeline-item-clickable .cbui-timeline-item-content:hover {
    background-color: color-mix(in srgb, var(--color-text) 4.5%, transparent);
  }
  .cbui-timeline-item-clickable .cbui-timeline-item-content:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 0;
  }

  /* ── Group ─────────────────────────────────────────────────────────────── */
  .cbui-timeline-group { display: block; }
  .cbui-timeline-group-label {
    padding-inline-start: var(--cbui-timeline-gutter);
    padding-block: 0.25rem var(--cbui-timeline-item-gap);
    font-size: var(--font-size-md, 1rem);
    font-weight: var(--font-weight-semibold, 600);
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--color-text-tertiary, var(--color-text-secondary));
  }
  .cbui-timeline > .cbui-timeline-group:first-child > .cbui-timeline-group-label {
    padding-block-start: 0;
  }
  .cbui-timeline-group-items {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  /* ── Empty ─────────────────────────────────────────────────────────────── */
  .cbui-timeline-empty {
    padding-inline-start: var(--cbui-timeline-gutter);
    color: var(--color-text-secondary);
  }
  .cbui-timeline:has(> .cbui-timeline-empty)::before { display: none; }

  /* ── Pulse marker (animated "in progress" / running step) ──────────────── */
  /* A filled tone centre plus an expanding halo ring. The centre reuses the
     ring/dot rhythm; the halo is an absolutely-positioned ::after so it never
     shifts the marker box. */
  .cbui-timeline-item-marker-pulse .cbui-timeline-item-marker::before {
    content: '';
    box-sizing: border-box;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 9999px;
    background-color: var(--cbui-timeline-tone, var(--color-primary));
    box-shadow: 0 0 0 4px var(--cbui-timeline-marker-bg);
  }
  .cbui-timeline-item-marker-pulse .cbui-timeline-item-marker::after {
    content: '';
    position: absolute;
    top: 50%;
    inset-inline-start: 50%;
    width: 0.75rem;
    height: 0.75rem;
    margin: -0.375rem 0 0 -0.375rem;
    border-radius: 9999px;
    border: 2px solid var(--cbui-timeline-tone, var(--color-primary));
    animation: cbui-timeline-pulse 1.6s ease-out infinite;
  }
  @keyframes cbui-timeline-pulse {
    0%   { transform: scale(1);   opacity: 0.65; }
    100% { transform: scale(2.6); opacity: 0; }
  }

  /* ── Run-step status treatments ────────────────────────────────────────── */
  /* status resolves marker/tone/icon defaults in the template; these rules add
     the few shapes the base marker variants don't already cover. */
  /* pending: a hollow, quiet dot — "not started yet". */
  .cbui-timeline-item-status-pending .cbui-timeline-item-marker::before {
    box-sizing: border-box;
    background-color: var(--cbui-timeline-marker-bg);
    border: 2px solid var(--cbui-timeline-tone, var(--color-text-tertiary, var(--color-text-secondary)));
  }
  /* skipped: a dashed hollow dot + dimmed body — "ran past, not executed". */
  .cbui-timeline-item-status-skipped .cbui-timeline-item-marker::before {
    box-sizing: border-box;
    background-color: var(--cbui-timeline-marker-bg);
    border: 1.5px dashed var(--cbui-timeline-tone, var(--color-text-tertiary, var(--color-text-secondary)));
  }
  .cbui-timeline-item-status-skipped .cbui-timeline-item-content { opacity: 0.62; }
  /* paused: a "waiting" hold, NOT an error — soft warning-tinted fill. */
  .cbui-timeline-item-status-paused .cbui-timeline-item-marker {
    background-color: color-mix(in srgb, var(--cbui-timeline-tone) 18%, var(--cbui-timeline-marker-bg));
  }

  /* ── Duration chip (helper) ────────────────────────────────────────────── */
  /* A small tabular elapsed-time pill. Pairs with .cbui-timeline-item-head so
     it can sit opposite the title; tabular-nums keeps columns of times aligned. */
  .cbui-timeline-item-duration {
    flex: none;
    align-self: center;
    padding: 0.0625rem 0.375rem;
    border-radius: var(--radius-sm, 0.25rem);
    background-color: var(--color-bg-subtle);
    font-family: var(--font-family-mono, ui-monospace, SFMono-Regular, monospace);
    font-size: var(--font-size-md, 1rem);
    font-variant-numeric: tabular-nums;
    color: var(--color-text-secondary);
  }

  /* ── Center align (rail down the middle; items placed left/right) ───────── */
  .cbui-timeline-align-center::before {
    inset-inline-start: calc(50% - 1px);
  }
  .cbui-timeline-align-center .cbui-timeline-item {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: calc(var(--cbui-timeline-marker-size) + 1rem);
    padding-inline-start: 0;
  }
  .cbui-timeline-align-center .cbui-timeline-item-marker {
    inset-inline-start: 50%;
    transform: translateX(-50%);
  }
  .cbui-timeline-align-center .cbui-timeline-item-side-start .cbui-timeline-item-content {
    grid-column: 1;
    align-items: flex-end;
    text-align: end;
  }
  .cbui-timeline-align-center .cbui-timeline-item-side-end .cbui-timeline-item-content {
    grid-column: 2;
    align-items: flex-start;
    text-align: start;
  }
  /* keep the title↔meta head row readable on whichever side it lands. */
  .cbui-timeline-align-center .cbui-timeline-item-side-start .cbui-timeline-item-head { flex-direction: row-reverse; }

  /* ── Live append animation ─────────────────────────────────────────────── */
  .cbui-timeline-item-appended {
    animation: cbui-timeline-appear 320ms ease;
  }
  @keyframes cbui-timeline-appear {
    from { opacity: 0; transform: translateY(-0.5rem); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .a11y-reduced-motion .cbui-timeline-item-appended { animation: none; }
  .a11y-reduced-motion .cbui-timeline-item-clickable .cbui-timeline-item-content { transition: none; }
  /* Pulse falls back to a static tone ring — the "running" meaning survives. */
  .a11y-reduced-motion .cbui-timeline-item-marker-pulse .cbui-timeline-item-marker::after { animation: none; opacity: 1; transform: none; }

  /* ── High contrast ─────────────────────────────────────────────────────── */
  .a11y-high-contrast .cbui-timeline { --cbui-timeline-rail-color: var(--color-border-strong, var(--color-border)); }
}

/* cbui/components/editor */
/* ──────────────────────────────────────────────────────────────────────────
   UiEditor:Editor — the shared CBUI editor scaffold (Layer 4, CB-94).

   Token-only (no hardcoded colours) so it adapts Light / Dark / Aero. The
   scaffold owns the shell, the edit-mode toggle, the save-status and the
   composable chrome (toolbar / bubble / gutter / slash); the concrete engine
   (CB-96 / CB-95) fills the body and the formatting slots.
   ────────────────────────────────────────────────────────────────────────── */

.cbui-editor {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--color-surface);
    color: var(--color-text);
}

/* ── Body / editable surface ──────────────────────────────────────────── */

.cbui-editor-body {
    padding: 1.75rem 2rem;
}

.cbui-editor-has-gutter .cbui-editor-body {
    padding-left: 3.25rem;
}

[data-cbui-editor-surface] {
    outline: none;
    min-height: 4rem;
}

/* The formatting set lives behind a wrapper so Read mode can drop it without
   touching the consumer's block content.

   CB-138 — this wrapper is ALSO the priority-plus-overflow root: it carries the
   ui-component--overflow controller, which measures scrollWidth > clientWidth on
   THIS element. For that to ever fire, the root must be allowed to be narrower
   than its content — so it is a flex-nowrap row that may shrink (flex 1 1 auto +
   min-width:0). The controller pins each item flex-shrink:0, so a too-narrow row
   OVERFLOWS (measurable) instead of squashing the buttons, then folds the
   lowest-priority items into the "More" trigger one by one. */
.cbui-editor-format {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.125rem;
    flex: 1 1 auto;
    min-width: 0;
    /* Clip the measuring frame so a momentary pre-collapse overflow (one frame,
       before recalc) never paints a scrollbar; the controller then folds. */
    overflow: hidden;
}

/* Let the Toolbar's start zone shrink in the EDITOR context only — by default
   .cbui-bar-start is flex-shrink:0 (it grows to fit, pushing the end zone off
   and clipping). Scoped here so the shared UiComponent bar.css is untouched:
   the start zone shrinks, the format root inside it reports overflow, the
   controller collapses — and the end-zone chrome (mode toggle · save status)
   stays put. */
.cbui-editor .cbui-bar-toolbar-start {
    min-width: 0;
    flex: 1 1 auto;
}

/* Each collapsible bar item — inline-flex so the controller's display toggle
   ('' ↔ 'none') restores a clean inline box, and so the icon button centres. */
.cbui-editor-format-item { display: inline-flex; align-items: center; }

/* The "More" (⋯) overflow trigger sits flush with the format buttons. The
   controller flips its inline display; it starts hidden (style="display:none")
   so it never flashes before the first measure. */
.cbui-editor-format-overflow { display: inline-flex; align-items: center; flex-shrink: 0; }

/* Read mode = a clean reading view: every editing affordance disappears and
   the surface is not editable (the controller sets contenteditable=false). */
[data-cbui-editor-mode="read"] .cbui-editor-format,
[data-cbui-editor-mode="read"] .cbui-editor-bubble,
[data-cbui-editor-mode="read"] .cbui-editor-slash,
[data-cbui-editor-mode="read"] .cbui-editor-gutter-handle { display: none !important; }

/* Default editor prose (demo content + a sensible baseline for engines that
   don't ship their own typography). Scoped to .cbui-editor-doc so it never
   leaks onto an engine's own markup. */
.cbui-editor-doc {
    max-width: 44rem;
    margin: 0 auto;
    color: var(--color-text);
    line-height: 1.65;
}
.cbui-editor-doc h2 {
    margin: 1.25rem 0 0.5rem;
    font-size: 1.2rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}
.cbui-editor-doc h2:first-child { margin-top: 0; }
.cbui-editor-doc p { margin: 0 0 1rem; }
.cbui-editor-doc ul {
    margin: 0 0 1rem;
    padding-left: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}
.cbui-editor-doc blockquote {
    margin: 0 0 1.25rem;
    padding: 0.4rem 0 0.4rem 1rem;
    border-left: 3px solid var(--color-primary);
    color: var(--color-text-secondary);
    font-style: italic;
}
.cbui-editor-doc ::selection {
    background: color-mix(in srgb, var(--color-primary) 28%, transparent);
}

/* A statically-highlighted phrase (showcase of a live selection). */
.cbui-editor-sel {
    background: color-mix(in srgb, var(--color-primary) 20%, transparent);
    border-radius: var(--radius-sm);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-primary) 35%, transparent);
    padding: 0.05em 0.1em;
}

/* Extra prose elements the demo execCommand stand-in can produce. */
.cbui-editor-doc h1 { margin: 1.25rem 0 0.5rem; font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; }
.cbui-editor-doc h1:first-child { margin-top: 0; }
.cbui-editor-doc h3 { margin: 1rem 0 0.4rem; font-size: 1.02rem; font-weight: 600; }
.cbui-editor-doc ol { margin: 0 0 1rem; padding-left: 1.25rem; display: flex; flex-direction: column; gap: 0.35rem; }
.cbui-editor-doc pre { margin: 0 0 1rem; padding: 0.75rem 0.9rem; background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: var(--radius-md); font-family: ui-monospace, monospace; font-size: var(--font-size-md); overflow: auto; }
.cbui-editor-doc code { background: var(--color-bg-subtle); padding: 0.1em 0.3em; border-radius: var(--radius-sm); font-family: ui-monospace, monospace; font-size: 1em; }

/* ── Edit-mode toggle (segmented) ─────────────────────────────────────── */

.cbui-editor-modetoggle {
    display: inline-flex;
    gap: 2px;
    padding: 2px;
    background: var(--color-bg-subtle);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}
.cbui-editor-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.6rem;
    border: 0;
    background: transparent;
    color: var(--color-text-secondary);
    font: inherit;
    font-size: var(--font-size-md);
    cursor: pointer;
    border-radius: var(--radius-sm);
    transition: background 0.12s ease, color 0.12s ease;
}
.cbui-editor-toggle:hover { color: var(--color-text); }
.cbui-editor-toggle-active {
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}
.cbui-editor-toggle svg { width: 1em; height: 1em; }

/* ── Save status ──────────────────────────────────────────────────────── */

.cbui-editor-status {
    display: inline-flex;
    align-items: center;
    font-size: var(--font-size-md);
    white-space: nowrap;
}
.cbui-editor-status-item {
    display: none;
    align-items: center;
    gap: 0.375rem;
}
.cbui-editor-status-item svg { width: 1.05em; height: 1.05em; }
[data-cbui-editor-state="saved"]  .cbui-editor-status-item[data-state="saved"],
[data-cbui-editor-state="dirty"]  .cbui-editor-status-item[data-state="dirty"],
[data-cbui-editor-state="saving"] .cbui-editor-status-item[data-state="saving"],
[data-cbui-editor-state="error"]  .cbui-editor-status-item[data-state="error"] {
    display: inline-flex;
}
.cbui-editor-status-item[data-state="saved"]  { color: var(--color-success); }
.cbui-editor-status-item[data-state="dirty"],
.cbui-editor-status-item[data-state="saving"] { color: var(--color-text-secondary); }
.cbui-editor-status-item[data-state="error"]  { color: var(--color-danger); }

/* ── Separators ───────────────────────────────────────────────────────── */

.cbui-editor-bar-sep,
.cbui-editor-bubble-sep {
    width: 1px;
    align-self: stretch;
    min-height: 1.25rem;
    margin: 0.2rem 0.15rem;
    background: var(--color-border);
}

/* ── Bubble menu (chrome: bubble) ─────────────────────────────────────── */

.cbui-editor-bubble {
    position: absolute;
    z-index: 30;
    top: 4rem;
    left: 2rem;
    display: inline-flex;
    align-items: center;
    gap: 0.125rem;
    padding: 0.25rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: 0 10px 28px -8px rgba(0, 0, 0, 0.32);
    white-space: nowrap;
}
.cbui-editor-bubble[hidden] { display: none; }

/* ── Gutter handle (chrome: gutter — engine renders the handles) ───────── */

.cbui-editor-gutter-handle {
    position: absolute;
    left: -2.75rem;
    top: 0;
    display: inline-flex;
    align-items: center;
    gap: 0.1rem;
    color: var(--color-text-secondary);
}
.cbui-editor-gutter-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.6rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    cursor: pointer;
    border-radius: var(--radius-sm);
    font-size: 1rem;
}
.cbui-editor-gutter-btn:last-child { cursor: grab; }
.cbui-editor-gutter-btn:hover { background: var(--color-bg-subtle); color: var(--color-text); }

/* ── Slash menu (chrome: slash) ───────────────────────────────────────── */

.cbui-editor-prompt { color: var(--color-text-secondary); }
.cbui-editor-prompt-slash { color: var(--color-primary); font-weight: 600; }
.cbui-editor-caret {
    display: inline-block;
    width: 1px;
    height: 1.1em;
    vertical-align: -0.2em;
    margin: 0 0.05rem;
    background: var(--color-text);
}

.cbui-editor-slash {
    position: absolute;
    z-index: 30;
    top: 8rem;
    left: 2rem;
    width: 21rem;
    max-width: calc(100% - 2rem);
    padding: 0.375rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: 0 14px 36px -10px rgba(0, 0, 0, 0.34);
    max-height: 22rem;
    overflow: auto;
}
.cbui-editor-slash[hidden] { display: none; }
.cbui-editor-slash-group {
    padding: 0.3rem 0.5rem 0.15rem;
    font-size: var(--font-size-md);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-secondary);
}
.cbui-editor-slash-item {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    width: 100%;
    padding: 0.4rem 0.5rem;
    border: 0;
    background: transparent;
    border-radius: var(--radius-md);
    cursor: pointer;
    text-align: left;
    color: var(--color-text);
    font: inherit;
}
.cbui-editor-slash-item:hover,
.cbui-editor-slash-item-active {
    background: color-mix(in srgb, var(--color-primary) 12%, transparent);
}
.cbui-editor-slash-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    flex: none;
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    border: 1px solid var(--color-border);
}
.cbui-editor-slash-icon svg { width: 1.05rem; height: 1.05rem; }
.cbui-editor-slash-body { flex: 1; min-width: 0; }
.cbui-editor-slash-label { display: block; font-size: var(--font-size-md); font-weight: 500; }
.cbui-editor-slash-desc { display: block; font-size: var(--font-size-md); color: var(--color-text-secondary); }

/* cbui/components/block-editor */
/* ──────────────────────────────────────────────────────────────────────────
   UiBlockEditor:BlockEditor — the typed block editor (CBUI Layer 4, CB-95).

   Token-only (no hardcoded colours) so it adapts Light / Dark / Aero. Layout
   follows the perfected legacy Studio model: the row carries padding-left per
   scope depth; the number badge, drag handle, scope lines and row actions are
   ALL absolute overlays so nothing shifts on hover and read/edit geometry is
   identical. The card sits above the scope lines (z-index) and clips them into
   the "line from under the card" connector.
   ────────────────────────────────────────────────────────────────────────── */

.cbui-block-doc {
    --cbui-block-indent: 1.25rem;   /* per-depth indent */
    --cbui-block-pl: 1.4rem;        /* card left base (room for handle) */
    --cbui-block-pt: 0.15rem;       /* row top padding */
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-height: 0;
}
.cbui-block-doc-numbered { --cbui-block-pl: 1.95rem; --cbui-block-pt: 0.55rem; --cbui-block-handle-x: 1.15rem; }

/* Visually-hidden helper. CBUI is token-based, not Tailwind-utility-based, so the
   `sr-only` utility is absent in some surfaces (e.g. Storybook) and would render
   the text visibly — keep our own. */
.cbui-block-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
/* Shift the numbered card's content right so the corner badge never covers the icon. */
.cbui-block-doc-numbered .cbui-block-card { padding-left: 1.05rem; }

.cbui-block-list {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}

/* ── Row ──────────────────────────────────────────────────────────────── */

.cbui-block-row {
    position: relative;
    padding-left: calc(var(--cbui-block-pl) + var(--cbui-block-depth, 0) * var(--cbui-block-indent));
    padding-top: var(--cbui-block-pt);
    border-radius: var(--radius-md);
    outline: none;
    scroll-margin: 0.5rem;
}

/* ── Scope nesting lines — at the card's left border, behind the cards ──── */

.cbui-block-scopelines { position: absolute; inset: 0 auto 0 0; pointer-events: none; z-index: 1; }
.cbui-block-scope-line {
    position: absolute;
    left: calc(var(--cbui-block-pl) + var(--cbui-scope-line-index, 0) * var(--cbui-block-indent));
    top: -1.1rem;
    bottom: -0.7rem;
    width: 2px;
    border-radius: 1px;
    background: color-mix(in srgb, var(--cbui-scope-line-color, var(--color-primary)) 60%, transparent);
}

/* ── Number badge — on the card's top-left corner (legacy model) ───────── */

.cbui-block-number {
    position: absolute;
    top: -0.3rem;
    left: calc(1.15rem + var(--cbui-block-depth, 0) * var(--cbui-block-indent));
    z-index: 5;
    display: none;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.5rem;
    padding: 0 0.25rem;
    border-radius: 999px;
    font-size: var(--font-size-md);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: #fff;
    background: var(--cbui-block-badge, var(--cbui-block-accent, var(--color-primary)));
    border: 2px solid var(--color-surface);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
    pointer-events: none;
    user-select: none;
}
.cbui-block-doc-numbered .cbui-block-number { display: inline-flex; }

/* ── Drag handle — absolute overlay; appears on hover (no layout shift) ── */

.cbui-block-handle {
    position: absolute;
    /* Vertically centred on the block, below the corner number badge, so the
       badge stays visible on hover (Matthias 2026-06-18). */
    top: 50%;
    transform: translateY(-50%);
    left: calc(var(--cbui-block-handle-x, 0.15rem) + var(--cbui-block-depth, 0) * var(--cbui-block-indent));
    z-index: 6;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 0;
    background: var(--color-surface);
    color: var(--color-text-tertiary, var(--color-text-secondary));
    border-radius: var(--radius-sm);
    cursor: grab;
    opacity: 0;
    transition: opacity 0.12s ease;
}
.cbui-block-row:hover .cbui-block-handle,
.cbui-block-row:focus-within .cbui-block-handle { opacity: 1; }
.cbui-block-handle:hover { color: var(--color-text); }
.cbui-block-handle svg { width: 1.05rem; height: 1.05rem; }
.cbui-block-doc-single .cbui-block-handle { display: none; }

/* ── Card — above the scope lines; layout base + appearance ───────────── */

.cbui-block-card {
    position: relative;
    z-index: 2;
    display: flex;
    align-items: flex-start;
    gap: 0.55rem;
    padding: 0.55rem 0.7rem;
    border-radius: var(--radius-md);
    cursor: text;
    transition: border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}

/* Framed — surface cards (playbook / instruction lists). Per-type bg via theme. */
.cbui-block-doc-framed .cbui-block-card {
    background: var(--cbui-block-bg, var(--color-surface));
    border: 1px solid var(--color-border);
}
.cbui-block-doc-framed .cbui-block-row:hover .cbui-block-card { border-color: var(--color-border-strong, var(--color-border)); }
.cbui-block-doc-framed .cbui-block-row:focus .cbui-block-card,
.cbui-block-doc-framed .cbui-block-row:focus-visible .cbui-block-card {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-primary) 30%, transparent);
}

/* Frameless — clean rows, no boxes (records / profiles / CRM / single). */
.cbui-block-doc-frameless .cbui-block-card {
    background: var(--cbui-block-bg, transparent);
    border: 1px solid transparent;
}
.cbui-block-doc-frameless .cbui-block-row:hover .cbui-block-card { background: var(--cbui-block-bg, var(--color-bg-subtle)); }
.cbui-block-doc-frameless .cbui-block-row:focus .cbui-block-card,
.cbui-block-doc-frameless .cbui-block-row:focus-visible .cbui-block-card {
    border-color: color-mix(in srgb, var(--color-primary) 40%, transparent);
    background: color-mix(in srgb, var(--color-primary) 5%, transparent);
}
.cbui-block-doc-frameless .cbui-block-icon { background: transparent; }

.cbui-block-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.55rem;
    height: 1.55rem;
    border-radius: var(--radius-sm);
    color: var(--cbui-block-accent, var(--color-text-secondary));
    background: color-mix(in srgb, var(--cbui-block-accent, var(--color-text-secondary)) 12%, transparent);
}
.cbui-block-icon svg { width: 1.05rem; height: 1.05rem; }
.cbui-block-card-content { flex: 1 1 auto; min-width: 0; }

/* ── Row actions — absolute overlay (top-right); never change card width ── */

.cbui-block-actions {
    position: absolute;
    top: calc(var(--cbui-block-pt) + 0.25rem);
    right: 0.3rem;
    z-index: 4;
    display: inline-flex;
    align-items: center;
    gap: var(--cbui-action-gap, 0.625rem);
    padding: 0.1rem;
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--color-surface) 86%, transparent);
    backdrop-filter: blur(2px);
    opacity: 0;
    transition: opacity 0.12s ease;
}
.cbui-block-row:hover .cbui-block-actions,
.cbui-block-row:focus-within .cbui-block-actions,
.cbui-block-row:focus .cbui-block-actions { opacity: 1; }
/* Short blocks (e.g. End section, Divider): centre the actions vertically; tall
   blocks keep them top-aligned (engine sets data-block-short by height). */
.cbui-block-row[data-block-short] .cbui-block-actions { top: 50%; transform: translateY(-50%); }
/* The block menu lays out inline with the Edit button; keep the cluster up while
   its menu is open (focus moves into the panel, but :has makes it explicit). */
.cbui-block-actions .cbui-dropdown { display: inline-flex; }
.cbui-block-row:has(.cbui-dropdown-panel:not([hidden])) .cbui-block-actions { opacity: 1; }
/* Single-block mode has no list — only the corner Edit, no block menu. */
.cbui-block-doc-single .cbui-block-actions .cbui-dropdown { display: none; }

/* ── Card bodies (per-type, from the block card templates) ────────────── */

.cbui-block-card-body { min-width: 0; }
.cbui-block-card-paragraph { margin: 0; line-height: 1.55; color: var(--color-text); white-space: pre-wrap; }
.cbui-block-card-placeholder { color: var(--color-text-tertiary, var(--color-text-secondary)); font-style: italic; }

/* Rich block card — renders the ProseMirror doc so marks/structure are visible. */
.cbui-block-card-rich { line-height: 1.55; color: var(--color-text); min-width: 0; }
.cbui-block-card-rich > :first-child { margin-top: 0; }
.cbui-block-card-rich > :last-child { margin-bottom: 0; }
.cbui-block-card-rich p { margin: 0 0 0.4rem; }
.cbui-block-card-rich strong { font-weight: 650; }
.cbui-block-card-rich em { font-style: italic; }
.cbui-block-card-rich u { text-decoration: underline; }
.cbui-block-card-rich s { text-decoration: line-through; }
.cbui-block-card-rich a { color: var(--color-primary); text-decoration: underline; }
.cbui-block-card-rich code {
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 1em;
    padding: 0.05em 0.3em;
    border-radius: var(--radius-xs, 0.25rem);
    background: var(--color-bg-subtle);
}
.cbui-block-card-rich h1, .cbui-block-card-rich h2, .cbui-block-card-rich h3 {
    margin: 0 0 0.3rem; font-weight: 650; line-height: 1.3;
}
.cbui-block-card-rich ul, .cbui-block-card-rich ol { margin: 0 0 0.4rem; padding-left: 1.4rem; }
.cbui-block-card-rich li { margin: 0.1rem 0; }
.cbui-block-card-rich blockquote {
    margin: 0 0 0.4rem; padding-left: 0.75rem;
    border-left: 2px solid var(--color-border-strong, var(--color-border));
    color: var(--color-text-secondary);
}
.cbui-block-card-rich pre {
    margin: 0 0 0.4rem; padding: 0.5rem 0.7rem; overflow-x: auto;
    border-radius: var(--radius-sm, 0.375rem); background: var(--color-bg-subtle);
    font-family: var(--font-mono, ui-monospace, monospace); font-size: 1em;
}

.cbui-block-card-heading { display: flex; align-items: baseline; gap: 0.5rem; font-weight: 650; color: var(--color-text); letter-spacing: -0.01em; }
.cbui-block-card-heading-h1 { font-size: 1.35rem; }
.cbui-block-card-heading-h2 { font-size: 1.15rem; }
.cbui-block-card-heading-h3 { font-size: 1.02rem; }
.cbui-block-card-meta {
    font-size: var(--font-size-md);
    font-weight: 600;
    color: var(--color-text-secondary);
    background: var(--color-bg-subtle);
    border-radius: var(--radius-sm);
    padding: 0.05rem 0.35rem;
    letter-spacing: 0.02em;
}

.cbui-block-card-list { margin: 0; padding-left: 1.2rem; display: flex; flex-direction: column; gap: 0.2rem; color: var(--color-text); }
.cbui-block-card-list-bulleted { list-style: disc; }
.cbui-block-card-list-ordered { list-style: decimal; }
.cbui-block-card-list li { margin: 0; }

.cbui-block-card-divider { padding: 0.4rem 0; }
.cbui-block-card-rule { border: 0; border-top: 1px solid var(--color-border-strong, var(--color-border)); margin: 0; }

.cbui-block-card-section { display: flex; align-items: baseline; gap: 0.6rem; }
.cbui-block-card-section-label {
    font-size: var(--font-size-md);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--cbui-block-accent, var(--color-primary));
}
.cbui-block-card-section-title { font-weight: 600; color: var(--color-text); }

.cbui-block-card-section-end { display: inline-flex; align-items: center; gap: 0.4rem; font-size: var(--font-size-md); color: var(--color-text-secondary); }
.cbui-block-card-section-end-icon { width: 1rem; height: 1rem; color: var(--cbui-block-accent, var(--color-primary)); }

/* Closer / divider = slim markers (no icon chip). */
.cbui-block-row-close .cbui-block-icon { display: none; }
.cbui-block-doc-framed .cbui-block-row-close .cbui-block-card {
    padding: 0.3rem 0.7rem;
    background: color-mix(in srgb, var(--cbui-block-accent, var(--color-primary)) 5%, var(--color-surface));
    border-style: dashed;
    cursor: default;
}

/* ── Add: header toggle · add-mode · end placeholder · floating picker ──── */

.cbui-block-empty { padding: 0.5rem 0; }

/* Header "Add block" toggle (in the scaffold header, next to the scope toggle).
   Hidden in read mode — but the header bar keeps its height, so read/edit never
   shift (Matthias 2026-06-18: the old bottom add-button caused the jump). */
.cbui-block-addtoggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.7rem;
    border: 1px solid color-mix(in srgb, var(--color-primary) 35%, var(--color-border));
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--color-primary) 8%, transparent);
    color: var(--color-primary);
    font: inherit;
    font-size: var(--font-size-md);
    font-weight: 600;
    cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease;
}
.cbui-block-addtoggle:hover { background: color-mix(in srgb, var(--color-primary) 14%, transparent); }
.cbui-block-addtoggle[aria-pressed="true"] { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
.cbui-block-addtoggle svg { width: 1em; height: 1em; }
.cbui-editor[data-cbui-editor-mode="read"] .cbui-block-addtoggle { display: none; }

/* End-of-list placeholder — only in add-mode; clicking it opens the insert
   placeholder at the end. A "phantom block": same left edge + width as the block
   cards (no full-bleed), so it reads as one more row, not a banner. */
.cbui-block-addslot {
    display: none;
    align-items: center;
    gap: 0.4rem;
    margin: 0.4rem 0.3rem 0
        calc(var(--cbui-block-pl) + var(--cbui-block-depth, 0) * var(--cbui-block-indent));
    padding: 0.55rem 0.7rem;
    border: 1px dashed color-mix(in srgb, var(--color-primary) 45%, var(--color-border));
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--color-primary) 4%, transparent);
    color: var(--color-primary);
    font: inherit;
    font-size: var(--font-size-md);
    font-weight: 600;
    cursor: pointer;
}
.cbui-block-doc-adding .cbui-block-addslot { display: flex; }
.cbui-block-addslot:hover { background: color-mix(in srgb, var(--color-primary) 11%, transparent); border-style: solid; }
.cbui-block-addslot svg { width: 1em; height: 1em; }

/* Add-mode — every block becomes an "insert before me" target with an add cursor.
   No horizontal insert lines (Matthias 2026-06-19): the inline placeholder IS the
   feedback, opening a real slot where the new block lands. */
.cbui-block-doc-adding .cbui-block-card { cursor: copy; }
/* In add-mode the whole row means "insert before me" — hide drag/edit affordances. */
.cbui-block-doc-adding .cbui-block-handle,
.cbui-block-doc-adding .cbui-block-actions { display: none !important; }
.cbui-block-doc-adding .cbui-block-row:hover > .cbui-block-card {
    border-color: color-mix(in srgb, var(--color-primary) 45%, var(--color-border));
}

/* ── Inline insert placeholder (the single add affordance) ──────────────── */
/* A real slot in the list: quick popular-type icons + "Pick a block type". Sits
   in the block content column (same left edge as cards). */
.cbui-block-insert {
    margin: 0.3rem 0.3rem 0.3rem
        calc(var(--cbui-block-pl) + var(--cbui-block-depth, 0) * var(--cbui-block-indent));
    padding: 0.4rem;
    border: 1px dashed color-mix(in srgb, var(--color-primary) 50%, var(--color-border));
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--color-primary) 5%, var(--color-surface));
    animation: cbui-block-insert-in 0.12s ease;
}
@keyframes cbui-block-insert-in { from { opacity: 0; transform: translateY(-2px); } }
@media (prefers-reduced-motion: reduce) { .cbui-block-insert { animation: none; } }
.cbui-block-insert-quick { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; }
.cbui-block-insert-fav {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2rem; height: 2rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--cbui-pi-accent, var(--color-text-secondary));
    cursor: pointer;
    transition: border-color 0.1s ease, background 0.1s ease, transform 0.1s ease;
}
.cbui-block-insert-fav:hover {
    border-color: color-mix(in srgb, var(--cbui-pi-accent, var(--color-primary)) 55%, var(--color-border));
    background: color-mix(in srgb, var(--cbui-pi-accent, var(--color-primary)) 9%, var(--color-surface));
    transform: translateY(-1px);
}
.cbui-block-insert-fav svg { width: 1.15rem; height: 1.15rem; }
.cbui-block-insert-more {
    display: inline-flex; align-items: center; gap: 0.35rem;
    margin-left: auto;
    padding: 0.3rem 0.65rem; height: 2rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font: inherit; font-size: var(--font-size-md); font-weight: 600;
    cursor: pointer;
    transition: color 0.1s ease, border-color 0.1s ease;
}
.cbui-block-insert-more:hover { color: var(--color-text); border-color: var(--color-border-strong, var(--color-border)); }
.cbui-block-insert-more svg { width: 1.05rem; height: 1.05rem; }
.cbui-block-insert-fav:focus-visible,
.cbui-block-insert-more:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--color-primary) 50%, transparent); outline-offset: 1px;
}

/* ── Floating block-type picker (3 styles: list / grid / command) ───────── */

.cbui-block-pickerhost {
    width: 19rem;
    max-width: calc(100vw - 1rem);
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    overflow: visible;
}
.cbui-block-pickerhost:popover-open { position: fixed; inset: auto; }
.cbui-block-picker {
    display: flex;
    flex-direction: column;
    max-height: min(22rem, 70vh);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: 0 18px 48px -16px rgba(0, 0, 0, 0.45);
    overflow: hidden;
}
.cbui-block-picker-head {
    padding: 0.5rem 0.7rem 0.4rem;
    font-size: var(--font-size-md);
    font-weight: 600;
    color: var(--color-text-secondary);
}
.cbui-block-picker-search {
    margin: 0.5rem 0.6rem 0.35rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    color: var(--color-text);
    font: inherit;
    font-size: var(--font-size-md);
}
.cbui-block-picker-search:focus-visible { outline: 2px solid color-mix(in srgb, var(--color-primary) 50%, transparent); outline-offset: 1px; }
.cbui-block-picker-body { overflow-y: auto; padding: 0.3rem; }
.cbui-block-picker-grouplabel {
    padding: 0.35rem 0.45rem 0.2rem;
    font-size: var(--font-size-md);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-tertiary, var(--color-text-secondary));
}
.cbui-block-picker-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    padding: 0.4rem 0.45rem;
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--color-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.cbui-block-picker-item:hover,
.cbui-block-picker-item:focus-visible { background: var(--color-bg-subtle); outline: none; }
.cbui-block-picker-item-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.7rem;
    height: 1.7rem;
    border-radius: var(--radius-sm);
    color: var(--cbui-pi-accent, var(--color-text-secondary));
    background: color-mix(in srgb, var(--cbui-pi-accent, var(--color-text-secondary)) 14%, transparent);
}
.cbui-block-picker-item-icon svg { width: 1.05rem; height: 1.05rem; }
.cbui-block-picker-item-text { display: flex; flex-direction: column; min-width: 0; gap: 0.05rem; }
.cbui-block-picker-item-label { font-weight: 600; line-height: 1.2; }
.cbui-block-picker-item-desc { font-size: var(--font-size-md); color: var(--color-text-secondary); line-height: 1.2; }

/* Grid style — compact icon tiles. */
.cbui-block-picker-grid .cbui-block-picker-items { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.25rem; }
.cbui-block-picker-grid .cbui-block-picker-item { flex-direction: column; gap: 0.3rem; padding: 0.55rem 0.3rem; text-align: center; }
.cbui-block-picker-grid .cbui-block-picker-item-icon { width: 2rem; height: 2rem; }
.cbui-block-picker-grid .cbui-block-picker-item-text { align-items: center; }
.cbui-block-picker-grid .cbui-block-picker-item-desc { display: none; }

/* Command style — search + flat list (the slash-menu feel). */
.cbui-block-picker-command .cbui-block-picker-grouplabel { display: none; }
.cbui-block-picker-item[hidden] { display: none; }
.cbui-block-picker-empty { padding: 0.6rem 0.7rem; color: var(--color-text-secondary); font-size: var(--font-size-md); }

/* ── Sidebar catalogue (catalogStyle="sidebar") ─────────────────────────────
   The alternative to the floating command picker: a panel docked against the
   editor's right edge (the engine positions the host fixed, full editor height),
   with a header, a search box and a MULTI-COLUMN grid of grouped block tiles.
   Reuses the .cbui-block-picker-item visuals; only the layout differs. */
.cbui-block-cataloghost {
    z-index: 60;            /* above the editor chrome; below top-layer popovers */
    width: 20rem;
    max-width: calc(100vw - 1rem);
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    /* Slide in from the right; the engine adds -in on the next frame. */
    transform: translateX(0.75rem);
    opacity: 0;
    transition: transform 0.16s cubic-bezier(0.2, 0, 0, 1), opacity 0.16s ease;
}
.cbui-block-cataloghost.cbui-block-cataloghost-in { transform: translateX(0); opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    .cbui-block-cataloghost { transition: none; transform: none; opacity: 1; }
}

.cbui-block-catalog-sidebar {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: -18px 0 48px -20px rgba(0, 0, 0, 0.45);
    overflow: hidden;
}
.cbui-block-catalog-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.7rem 0.5rem 0.6rem 0.85rem;
    border-bottom: 1px solid var(--color-border);
}
.cbui-block-catalog-title {
    font-size: var(--font-size-md);
    font-weight: 700;
    color: var(--color-text);
}
.cbui-block-catalog-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: color 0.12s ease, background 0.12s ease;
}
.cbui-block-catalog-close:hover { color: var(--color-text); background: var(--color-bg-subtle); }
.cbui-block-catalog-close:focus-visible { outline: 2px solid color-mix(in srgb, var(--color-primary) 50%, transparent); outline-offset: 1px; }
.cbui-block-catalog-close svg { width: 1.05rem; height: 1.05rem; }

.cbui-block-catalog-searchwrap { padding: 0.6rem 0.6rem 0.4rem; }
.cbui-block-catalog-search {
    width: 100%;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    color: var(--color-text);
    font: inherit;
    font-size: var(--font-size-md);
}
.cbui-block-catalog-search:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--color-primary) 50%, transparent);
    outline-offset: 1px;
}

.cbui-block-catalog-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 0.3rem 0.5rem 0.6rem;
}
.cbui-block-catalog-group { margin-top: 0.3rem; }
.cbui-block-catalog-group[hidden] { display: none; }
.cbui-block-catalog-grouplabel {
    padding: 0.4rem 0.15rem 0.3rem;
    font-size: var(--font-size-md);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-tertiary, var(--color-text-secondary));
}
/* The multi-column grid — two columns of tiles, the group label spans both. */
.cbui-block-catalog-items {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.3rem;
}
/* Tiles reuse the picker-item visuals but stack icon-over-text in the grid cell. */
.cbui-block-catalog-item {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.35rem;
    padding: 0.55rem 0.5rem;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
}
.cbui-block-catalog-item:hover,
.cbui-block-catalog-item:focus-visible {
    border-color: color-mix(in srgb, var(--cbui-pi-accent, var(--color-primary)) 55%, var(--color-border));
    background: color-mix(in srgb, var(--cbui-pi-accent, var(--color-primary)) 8%, var(--color-surface));
    outline: none;
}
.cbui-block-catalog-item .cbui-block-picker-item-desc {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ── Scope-nesting toggle — lives in the scaffold header (next to Read/Edit) ─ */

/* Only meaningful when the document actually has nesting — the engine sets
   cbui-editor-has-scope on the scaffold whenever an open scope is present. */
.cbui-editor:not(.cbui-editor-has-scope) .cbui-block-scopetoggle { display: none; }

.cbui-block-scopetoggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    color: var(--color-text-secondary);
    font: inherit;
    font-size: var(--font-size-md);
    cursor: pointer;
    transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease;
}
.cbui-block-scopetoggle:hover { color: var(--color-text); }
.cbui-block-scopetoggle[aria-pressed="true"] {
    color: var(--color-primary);
    border-color: color-mix(in srgb, var(--color-primary) 40%, var(--color-border));
}
.cbui-block-scopetoggle svg { width: 1em; height: 1em; }

/* ── Action toolbar (opt-in, actionToolbar="true") ──────────────────────────
   Replaces the scaffold's Read/Edit toggle with ONE Edit/Update button + a
   primary-action Tabnav (Edit | + Add block). The Tabnav reuses the scaffold's
   .cbui-editor-modetoggle / .cbui-editor-toggle visuals (segmented control); only
   the single mode button needs its own neutral/primary styling below.

   The default scaffold Read/Edit toggle is suppressed in this variant, so the
   primary-action Tabnav is the only .cbui-editor-modetoggle in the header. */
.cbui-block-primarynav[hidden] { display: none; }

/* Single mode button — read='Edit' (neutral outline), edit='Update' (primary). */
.cbui-block-modebtn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.8rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text);
    font: inherit;
    font-size: var(--font-size-md);
    font-weight: 600;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.cbui-block-modebtn svg { width: 1em; height: 1em; display: block; }
.cbui-block-modebtn [data-cbui-block-mode-icon-edit],
.cbui-block-modebtn [data-cbui-block-mode-icon-update] { display: inline-flex; }
.cbui-block-modebtn [hidden] { display: none; }
.cbui-block-modebtn:hover { border-color: var(--color-border-strong, var(--color-border)); }
/* Neutral (read mode → "Edit"): quiet outline button. */
.cbui-block-modebtn[data-tone="neutral"]:hover {
    background: var(--color-bg-subtle);
}
/* Primary (edit mode → "Update"): committing is the primary action. */
.cbui-block-modebtn[data-tone="primary"] {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #fff;
}
.cbui-block-modebtn[data-tone="primary"]:hover {
    background: color-mix(in srgb, var(--color-primary) 88%, #000);
    border-color: color-mix(in srgb, var(--color-primary) 88%, #000);
}
.cbui-block-modebtn:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--color-primary) 50%, transparent);
    outline-offset: 1px;
}

/* ── Move-mode (opt-in global reorder mode, moveMode="true") ─────────────────
   A header toggle (next to Add-block + scope toggles) that flips the WHOLE editor
   into a reorder state — like the legacy playbook editor, reordering is the primary
   interaction. The engine sets cbui-block-doc-move-active on the doc (+ a mirror
   cbui-editor-move-active on the scaffold) when the mode is ON. The three default
   reorder ways (drag handle, keyboard pickup, ⋯-menu Move up/down) stay available
   always; move-mode just makes reordering PROMINENT. Geometry is untouched — only
   overlay visibility / cursor / tone change, never a row's footprint. */

/* The header toggle — mirrors the scope toggle visuals; turns primary when armed. */
.cbui-block-movetoggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    color: var(--color-text-secondary);
    font: inherit;
    font-size: var(--font-size-md);
    cursor: pointer;
    transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease;
}
.cbui-block-movetoggle:hover { color: var(--color-text); }
.cbui-block-movetoggle[aria-pressed="true"] {
    background: var(--color-primary);
    color: #fff;
    border-color: var(--color-primary);
}
.cbui-block-movetoggle svg { width: 1em; height: 1em; }
/* Read mode never shows the move toggle (no reordering in read). */
.cbui-editor[data-cbui-editor-mode="read"] .cbui-block-movetoggle { display: none; }

/* While move-mode is active, every block reads as a draggable target: the handle
   is always visible (no hover gate), and a grab cursor on the card. */
.cbui-block-doc-move-active .cbui-block-handle { opacity: 1; }
.cbui-block-doc-move-active .cbui-block-card { cursor: grab; }
.cbui-block-doc-move-active .cbui-block-row-grabbed .cbui-block-card { cursor: grabbing; }

/* Dampen edit/add/delete affordances — you rearrange, not edit, while moving.
   (Hidden, not removed, so geometry stays identical — overlays only.) */
.cbui-block-doc-move-active .cbui-block-actions { display: none !important; }

/* A subtle "drop here" frame on hover so the click-to-move target reads clearly
   (token-only; works light + dark). The grabbed block keeps its own grabbed ring. */
.cbui-block-doc-move-active .cbui-block-row:hover > .cbui-block-card {
    border-color: color-mix(in srgb, var(--color-primary) 50%, var(--color-border));
    background: color-mix(in srgb, var(--color-primary) 6%, var(--color-surface));
}
.cbui-block-doc-move-active .cbui-block-row-grabbed:hover > .cbui-block-card {
    background: var(--color-surface);
}

/* ── Flat scope visualisation (toggle) ────────────────────────────────── */

.cbui-block-doc-flat .cbui-block-row { padding-left: var(--cbui-block-pl); }
.cbui-block-doc-flat .cbui-block-scopelines { display: none; }
.cbui-block-doc-flat .cbui-block-number { left: 1.15rem; }
.cbui-block-doc-flat .cbui-block-handle { left: var(--cbui-block-handle-x, 0.15rem); }

/* ── Read mode — identical geometry; only editing affordances hidden ──── */

.cbui-block-doc-read .cbui-block-handle,
.cbui-block-doc-read .cbui-block-actions,
.cbui-block-doc-read .cbui-block-addslot { display: none !important; }
.cbui-block-doc-read .cbui-block-card { cursor: default; }
.cbui-block-doc-frameless.cbui-block-doc-read .cbui-block-row:hover .cbui-block-card { background: var(--cbui-block-bg, transparent); }

/* ── Single-block — Edit pinned in the corner, Delete off, optional border ─ */

.cbui-block-doc-single .cbui-block-list { gap: 0; }
.cbui-block-doc-single .cbui-block-actions { opacity: 1; }
.cbui-block-doc-no-delete .cbui-block-actions [data-action*="deleteBlock"] { display: none; }
/* Optional primary-tinted frame around the single block. */
.cbui-block-doc-single.cbui-block-doc-bordered .cbui-block-card {
    border: 1px solid color-mix(in srgb, var(--color-primary) 45%, var(--color-border));
    background: var(--cbui-block-bg, var(--color-surface));
}

/* ── Edit surface (the per-block LiveComponent editor) ────────────────── */

.cbui-block-editor-surface { min-width: 0; }
.cbui-block-editor-surface[hidden] { display: none; }
/* While editing, the card gives way to the editor in the SAME footprint. */
.cbui-block-row-editing > .cbui-block-card { display: none; }
.cbui-block-row-editing > .cbui-block-actions { display: none; }

.cbui-block-edit {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 0.875rem;
    background: var(--color-surface);
    border: 1px solid var(--color-primary);
    border-radius: var(--radius-md);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-primary) 25%, transparent);
}
@keyframes cbui-block-spin { to { transform: rotate(360deg); } }
.cbui-block-edit-field { display: flex; flex-direction: column; gap: 0.3rem; }
.cbui-block-edit-label {
    font-size: var(--font-size-md);
    font-weight: 600;
    color: var(--color-text-secondary);
    letter-spacing: 0.01em;
}
.cbui-block-edit-hint { font-weight: 400; color: var(--color-text-tertiary, var(--color-text-secondary)); }
/* Explanation above the field (text block `lead`): readable, not a whisper. */
.cbui-block-edit-lead { margin: 0 0 0.25rem; color: var(--color-text-secondary); }
.cbui-block-edit-radios { display: inline-flex; flex-wrap: wrap; gap: 0.875rem; }
.cbui-block-edit-error { margin: 0; color: var(--color-danger); font-size: var(--font-size-md); }
.cbui-block-edit-foot { display: flex; align-items: center; gap: var(--cbui-action-gap, 0.625rem); padding-top: 0.15rem; }
/* CB-3183: the dictation control sits at the end of the footer row. */
.cbui-block-edit-foot .cbui-dictation { margin-inline-start: auto; }
.cbui-block-edit textarea { resize: vertical; width: 100%; }

/* ── Date block (CB-95) — card + editor row ──────────────────────────── */
.cbui-block-date { display: flex; align-items: center; min-width: 0; }
.cbui-block-date-value,
.cbui-block-date-empty {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    min-width: 0;
}
.cbui-block-date-value { color: var(--color-text); }
.cbui-block-date-empty { color: var(--color-text-tertiary, var(--color-text-secondary)); font-style: italic; }
.cbui-block-date-icon { flex: none; color: var(--color-text-tertiary); }
.cbui-block-date-value > span,
.cbui-block-date-empty > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cbui-block-date-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.cbui-block-date-row .cbui-input { max-width: 16rem; }

/* ── Scalar field blocks (boolean · number · duration, CB-296) ─────────── */
/* Card read view — icon + value, italic empty state (mirrors the date card),
   plus an inline value+unit editor row that wraps on a narrow window. */
.cbui-block-scalar { display: flex; align-items: center; gap: 0.5rem; min-width: 0; flex-wrap: wrap; }
.cbui-block-scalar-caption { color: var(--color-text-secondary); font-size: var(--font-size-md); }
.cbui-block-scalar-value,
.cbui-block-scalar-empty {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    min-width: 0;
}
.cbui-block-scalar-value { color: var(--color-text); }
.cbui-block-scalar-empty { color: var(--color-text-tertiary, var(--color-text-secondary)); font-style: italic; }
.cbui-block-scalar-icon { flex: none; color: var(--color-text-tertiary); }
.cbui-block-scalar-value > span,
.cbui-block-scalar-empty > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Value | unit on ONE line (Matthias 24.07.): the unit picker is sized to its
   content, not the full row. `.cbui-select` is width:100% by default, which made
   the wrap fill the row and flex-wrap push it onto its own line. */
.cbui-block-scalar-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: nowrap; }
.cbui-block-scalar-row .cbui-input { max-width: 8rem; }
.cbui-block-scalar-row .cbui-select-wrap { flex: 0 0 auto; width: auto; }
.cbui-block-scalar-row .cbui-select { width: auto; }

/* ── Map field (key/value rows, CB-313) ─────────────────────────────────── */
/* Card read view: the overflow badge line under the KeyValue block. */
.cbui-block-map-more { margin: 0.25rem 0 0; }
/* Editor: FIXED grid tracks — key | value | remove. The action track is a fixed
   2rem (= .cbui-button-icon-sm) instead of `auto`, so the column header row and
   every data row share the same three column widths, and no row changes width
   when its content changes. */
.cbui-block-map-rows { display: flex; flex-direction: column; gap: 0.35rem; }
.cbui-block-map-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 2rem;
    gap: 0.35rem;
    align-items: center;
}
/* The header sticks so it survives scrolling a long map; its surface matches the
   editor's so rows scroll behind it. */
.cbui-block-map-head {
    position: sticky;
    top: 0;
    z-index: 1;
    padding-bottom: 0.15rem;
    background: var(--color-surface);
}
/* Single-column mode (CB-3507): one input track + the remove button. The wrapper
   div that carries the row-list controller must not add a box of its own. */
.cbui-block-map-body { display: contents; }
.cbui-block-map-words { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; margin: 0; }
.cbui-block-map-single .cbui-block-map-row { grid-template-columns: minmax(0, 1fr) 2rem; }
.cbui-block-map-row.is-invalid {
    border-radius: var(--radius-sm, 0.375rem);
    background: color-mix(in srgb, var(--color-danger) 8%, transparent);
    box-shadow: 0 0 0 0.15rem color-mix(in srgb, var(--color-danger) 8%, transparent);
}
/* A long map must not blow up the popover height; the gutter is reserved so the
   appearing scrollbar does not narrow the rows out of sync with the header. */
.cbui-block-map-scroll {
    max-height: 16rem;
    overflow-y: auto;
    scrollbar-gutter: stable;
}
.cbui-block-map-foot { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
.cbui-block-map-count { color: var(--color-text-tertiary, var(--color-text-secondary)); font-size: var(--font-size-md); }
/* The error line is always rendered (only its text changes), so it reserves its
   own height — otherwise the footer jumps on every validation change. */
.cbui-block-map-error { min-height: 1lh; }
/* Two input columns need more room than the default field popover (22rem). */
.cbui-block-float:has(.cbui-block-map) { width: 30rem; }
/* No breakpoint on purpose: both value tracks are `minmax(0, …)`, so the row
   stays two-column and simply narrows down to a docked window / phone width.
   Stacking key over value was tried and dropped — it hides the column header,
   which is the only visible thing telling the two fields apart. */

/* ── Rich-text field (embedded UiRichTextEditor, CB-96) ─────────────────── */
/* The embedded editor drops its own chrome (header=false) and reads as a single
   bordered field that grows with content, scrolling past a sensible cap. */
.cbui-block-richwrap > .cbui-editor {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm, 0.375rem);
    background: var(--color-surface);
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.cbui-block-richwrap > .cbui-editor:focus-within {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-primary) 25%, transparent);
}
.cbui-block-richwrap .cbui-editor-body {
    min-height: 4.5rem;
    max-height: 18rem;
    overflow-y: auto;
    padding: 0.5rem 0.7rem;
}
/* Rich blocks need a little more room than the default field popover. */
.cbui-block-float:has(.cbui-block-richwrap) { width: 27rem; }

/* ── Popover host shell `.cbui-block-float` (top-layer; the editor IS the surface). Used by the popover
   surface and as the shared host class of the Bringers' own field-editor popovers. ── */

.cbui-block-float {
    width: 22rem;
    max-width: calc(100vw - 1rem);
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    overflow: visible;
}
.cbui-block-float:popover-open { position: fixed; inset: auto; }
.cbui-block-float .cbui-block-edit { width: 100%; box-shadow: 0 18px 48px -16px rgba(0, 0, 0, 0.45); }

/* ── Embedded surfaces — SOMEONE ELSE owns the frame ─────────────────────
   Three hosts, one look: the CBUI Modal, the LocalModal host, and any host that
   mounts an editor LiveComponent directly in its own surface and drives save()
   from outside (`hideActions` → `.cbui-block-edit-embedded`, e.g. the Values
   detail's Metadaten tab, which puts its Abbrechen/Speichern in the detail
   footer). In all three the editor drops its own frame, padding and shadow.

   This lives HERE, not in the host bundle: it is generic block-editor optics, and
   keying it onto a host class (as values.css briefly did) both invites the next
   host to copy it and lets a rename in here break that host silently. */
.cbui-block-overlay-inner { min-width: 0; }
.cbui-modal .cbui-block-edit,
.cbui-local-modal-host .cbui-block-edit,
.cbui-block-edit-embedded {
    border: 0;
    box-shadow: none;
    padding: 0;
    background: transparent;
}
/* The map block's 16rem cap (§ Map field) exists so a long map cannot blow up the
   floating popover. An embedded editor has no popover — the HOST surface is the
   scroll container — so the cap is lifted rather than fought from the outside. */
.cbui-block-edit-embedded .cbui-block-map-scroll {
    max-height: none;
    overflow-y: visible;
}

/* ── Keyboard move (grabbed) — mirrors the Kanban model ───────────────── */

.cbui-block-row-grabbed > .cbui-block-card {
    border-color: var(--color-primary) !important;
    box-shadow: 0 8px 22px -8px rgba(0, 0, 0, 0.35), 0 0 0 2px color-mix(in srgb, var(--color-primary) 45%, transparent) !important;
}

/* ── Touch — actions + handle are fixed (not hover-gated) in edit mode ──── */

@media (pointer: coarse) {
    .cbui-block-doc:not(.cbui-block-doc-read) .cbui-block-actions,
    .cbui-block-doc:not(.cbui-block-doc-read) .cbui-block-handle { opacity: 1; }
}

/* ── Drag (SortableJS) ────────────────────────────────────────────────── */

.cbui-block-row-ghost { opacity: 0.4; }
.cbui-block-row-ghost .cbui-block-card { border-style: dashed; }
.cbui-block-row-chosen .cbui-block-card { border-color: var(--color-primary); }
.cbui-block-row-drag .cbui-block-card {
    box-shadow: 0 12px 30px -10px rgba(0, 0, 0, 0.4);
    border-color: var(--color-primary);
}

/* ══════════════════════════════════════════════════════════════════════════
   Surfaces (2026-10-29): in-place · popover · panel, and the
   opt-in modern controls.  Tokens only; type never below --font-size-md.
   ══════════════════════════════════════════════════════════════════════════ */

/* A bar must never cover a block: inside a block editor the scaffold toolbar keeps its own row
   (static, content starts below it) instead of floating over the blocks that scroll beneath it. */
.cbui-editor:has(.cbui-block-doc) > .cbui-bar-toolbar-sticky { position: static; }
.cbui-block-row { scroll-margin-top: 1rem; }

/* ── View Transitions: the block morphs into its editor and back ────────────
   Rows are named per transition by the engine (`view-transition-name`), so the rows
   BELOW slide instead of jumping. The root layer does not cross-fade (nothing
   else on the page changes). Reduced motion: the engine skips the transition. */
html.cbe-vt::view-transition-old(root),
html.cbe-vt::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
html.cbe-vt::view-transition-old(root) { display: none; }
html.cbe-vt::view-transition-group(*) {
    animation-duration: 0.3s;
    animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* The old/new snapshots keep their natural size and are anchored top-left inside the growing
   group (clipped), so the content cross-fades instead of being stretched. */
/* The margin keeps what overhangs a row (the position marker sits half outside its corner) from being clipped. */
html.cbe-vt::view-transition-group(*) { overflow: clip; overflow-clip-margin: 16px; }
html.cbe-vt::view-transition-old(*),
html.cbe-vt::view-transition-new(*) { height: 100%; object-fit: none; object-position: top left; }

/* ── Loading: the block is never swapped for a chip; it shows it is busy (aria-busy on the row) ──
   marker (default): the position marker spins; without a marker, the block's icon spins.
   border: a highlight travels around the block's own rounded border (`loading="border"`). */
.cbui-block-doc-loading-marker .cbui-block-row[aria-busy="true"] .cbui-block-number::after,
.cbui-block-doc-loading-marker:not(.cbui-block-doc-numbered) .cbui-block-row[aria-busy="true"] .cbui-block-icon::after {
    content: '';
    position: absolute;
    inset: -4px;
    border-radius: 50%;
    border: 2px solid color-mix(in srgb, var(--color-primary) 25%, transparent);
    border-top-color: var(--color-primary);
    animation: cbui-block-spin 0.6s linear infinite;
    pointer-events: none;
}
.cbui-block-doc-loading-marker:not(.cbui-block-doc-numbered) .cbui-block-row[aria-busy="true"] .cbui-block-icon { position: relative; }

@property --cbui-block-sweep { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
.cbui-block-doc-loading-border .cbui-block-row[aria-busy="true"] > .cbui-block-card::before {
    content: '';
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    padding: 2px;
    background: conic-gradient(from var(--cbui-block-sweep), transparent 0 62%, color-mix(in srgb, var(--color-primary) 30%, transparent) 80%, var(--color-primary) 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
    animation: cbui-block-sweep 1.1s linear infinite;
    pointer-events: none;
}
@keyframes cbui-block-sweep { to { --cbui-block-sweep: 360deg; } }
@keyframes cbui-block-pulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.9; } }
@media (prefers-reduced-motion: reduce) {
    .cbui-block-doc-loading-marker .cbui-block-row[aria-busy="true"] .cbui-block-number::after,
    .cbui-block-doc-loading-marker:not(.cbui-block-doc-numbered) .cbui-block-row[aria-busy="true"] .cbui-block-icon::after { animation: none; }
    .cbui-block-doc-loading-border .cbui-block-row[aria-busy="true"] > .cbui-block-card::before {
        background: var(--color-primary);
        animation: cbui-block-pulse 1.4s ease-in-out infinite;
    }
}

/* Saving: the primary button is disabled by the Live loading directive (no double submit) and carries a ring. */
.cbui-block-edit-foot .cbui-button.is-saving { cursor: progress; }
.cbui-block-edit-foot .cbui-button.is-saving::after {
    content: '';
    width: 1rem;
    height: 1rem;
    margin-inline-start: 0.5rem;
    border-radius: 50%;
    border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
    border-top-color: currentColor;
    animation: cbui-block-spin 0.6s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .cbui-block-edit-foot .cbui-button.is-saving::after { animation: none; } }

/* ── In-place · surface="in-place": the block grows into its form, same frame ──────── */
.cbui-block-row-editing > .cbui-block-editor-surface { animation: cbui-block-rise 0.2s ease-out; }
@keyframes cbui-block-rise { from { opacity: 0; } to { opacity: 1; } }
.cbui-block-doc-surface-in-place .cbui-block-edit { border-color: var(--color-primary); }
.cbui-block-step-edit-row { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 0.75rem; }
.cbui-block-step-expert { border-top: 1px solid var(--color-border); padding-top: 0.5rem; }
.cbui-block-step-expert > summary { cursor: pointer; font-weight: 600; color: var(--color-text-secondary); padding: 0.15rem 0; }
.cbui-block-step-expert[open] > summary { margin-bottom: 0.5rem; }
.cbui-block-step-tools-edit { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.cbui-block-step-tools-edit li { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.cbui-block-step-tool-name { font-weight: 500; color: var(--color-text); }
.cbui-block-edit-field [aria-invalid="true"] { border-color: var(--color-danger); }

/* Two-way choice as a segmented control (per-tool approval). */
.cbui-block-seg {
    display: inline-flex;
    padding: 0.125rem;
    gap: 0.125rem;
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    border: 1px solid var(--color-border);
}
.cbui-block-seg label { position: relative; display: inline-flex; }
.cbui-block-seg input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.cbui-block-seg span {
    padding: 0.2rem 0.65rem;
    border-radius: calc(var(--radius-md) - 2px);
    font-size: var(--font-size-md);
    color: var(--color-text-secondary);
    white-space: nowrap;
    transition: background 0.12s ease, color 0.12s ease;
}
.cbui-block-seg input:checked + span { background: var(--color-surface); color: var(--color-text); font-weight: 600; box-shadow: var(--shadow-sm); }
.cbui-block-seg input:focus-visible + span { outline: 2px solid var(--color-primary); outline-offset: 1px; }
.cbui-block-seg-set { border: 0; margin: 0; padding: 0; min-width: 0; }

/* ── Step card (demo-step) ────────────────────────────────────────────── */
.cbui-block-step { display: flex; flex-direction: column; gap: 0.3rem; }
.cbui-block-step-title { font-size: var(--font-size-lg); font-weight: 600; line-height: 1.35; color: var(--color-text); letter-spacing: -0.005em; }
.cbui-block-step-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.9rem; color: var(--color-text-secondary); font-size: var(--font-size-md); }
.cbui-block-step-assignee, .cbui-block-step-due, .cbui-block-step-tool { display: inline-flex; align-items: center; gap: 0.3rem; }
.cbui-block-step-tools { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.cbui-block-step-tool {
    padding: 0.05rem 0.5rem;
    border-radius: var(--radius-full, 999px);
    background: var(--color-bg-subtle);
    border: 1px solid var(--color-border);
    color: var(--color-text-secondary);
    font-size: var(--font-size-md);
}
.cbui-block-step-tool[data-approval="preapproved"] svg { color: var(--color-success); }
.cbui-block-step-tool[data-approval="ask"] svg { color: var(--color-warning); }

/* ── surface="popover": anchored below the block, with an arrow ───── */
.cbui-block-popover { max-width: calc(100vw - 1rem); filter: drop-shadow(0 18px 28px color-mix(in srgb, var(--color-text) 22%, transparent)); animation: cbui-block-pop 0.16s ease-out; transform-origin: 40px 0; }
.cbui-block-popover[data-side="above"] { transform-origin: 40px 100%; }
@keyframes cbui-block-pop { from { opacity: 0; transform: translateY(-6px) scale(0.98); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .cbui-block-popover { animation: none; } }
.cbui-block-popover .cbui-block-edit { box-shadow: none; border-radius: var(--radius-lg, var(--radius-md)); border-color: var(--color-border-strong, var(--color-border)); }
/* A popover with a long form (the format editor with preview, legend and switches) never grows past the viewport: the form scrolls
   inside, and Fertig / Abbrechen stay in view at its foot. A short popover is untouched (the cap is never reached). */
.cbui-block-popover .cbui-block-edit { max-height: calc(100dvh - 1rem); overflow-y: auto; overscroll-behavior: contain; }
.cbui-block-popover .cbui-block-edit-foot { position: sticky; bottom: -0.875rem; margin-bottom: -0.875rem; padding: 0.5rem 0 0.875rem; background: var(--color-surface); z-index: 1; }
.cbui-block-popover-arrow {
    position: absolute;
    left: 50%;
    top: -6px;
    width: 12px;
    height: 12px;
    margin-left: -6px;
    transform: rotate(45deg);
    background: var(--color-surface);
    border-top: 1px solid var(--color-border-strong, var(--color-border));
    border-left: 1px solid var(--color-border-strong, var(--color-border));
    border-top-left-radius: 3px;
}
.cbui-block-popover[data-side="above"] .cbui-block-popover-arrow { top: auto; bottom: -6px; transform: rotate(225deg); }
.cbui-block-row-anchor > .cbui-block-card { border-color: var(--color-primary); box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-primary) 30%, transparent); }

/* ── surface="panel": the editor lives in a panel; the row shows it is selected ─ */
.cbui-block-row-selected > .cbui-block-card { border-color: var(--color-primary); box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-primary) 30%, transparent); }
.cbui-block-panel-mount .cbui-block-edit { border: 0; box-shadow: none; background: transparent; padding: 0; }
.cbui-block-panel-mount .cbui-block-step-edit-row { grid-template-columns: minmax(0, 1fr); }
.cbui-block-panel-empty { padding: 1.5rem 0.5rem; text-align: center; color: var(--color-text-secondary); }
.cbui-block-panel-empty strong { display: block; color: var(--color-text); margin-bottom: 0.25rem; }
.cbui-block-panel-pad { padding: 0.75rem 1rem; }
.cbui-block-panel-pre {
    margin: 0.5rem 0 0;
    padding: 0.75rem;
    border-radius: var(--radius-md);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    font-family: var(--font-family-mono, ui-monospace, monospace);
    font-size: var(--font-size-md);
    overflow-x: auto;
}

/* ── controls="modern": quiet chrome, keyboard-first ──────────────── */
.cbui-block-doc-modern .cbui-block-handle-menu { position: absolute; inset: 0 auto 0 0; display: block; pointer-events: none; z-index: 6; }
.cbui-block-doc-modern .cbui-block-handle-menu .cbui-block-handle { pointer-events: auto; }
.cbui-block-doc-modern .cbui-block-handle { left: calc(var(--cbui-block-handle-x, 0.15rem) + var(--cbui-block-depth, 0) * var(--cbui-block-indent)); background: transparent; }
.cbui-block-doc-modern .cbui-block-handle:hover { background: var(--color-bg-subtle); }
.cbui-block-row[data-block-indent="1"] { margin-left: var(--cbui-block-indent); }
.cbui-block-row[data-block-indent="2"] { margin-left: calc(var(--cbui-block-indent) * 2); }
.cbui-block-row { transition: margin-left 0.18s ease; }
@media (prefers-reduced-motion: reduce) { .cbui-block-row { transition: none; } }
.cbui-block-plus {
    position: absolute;
    /* centred on the gap between two blocks (card centre), far from the position badges on the left */
    left: calc(var(--cbui-block-pl) + (100% - var(--cbui-block-pl)) / 2 - 0.75rem);
    bottom: -0.97rem;
    z-index: 8;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-text-secondary);
    cursor: pointer;
    opacity: 0;
    transform: scale(0.85);
    transition: opacity 0.12s ease, transform 0.12s ease, background 0.12s ease;
    box-shadow: var(--shadow-sm);
}
.cbui-block-row:hover > .cbui-block-plus,
.cbui-block-row:focus-within > .cbui-block-plus,
.cbui-block-row:focus > .cbui-block-plus { opacity: 1; transform: none; }
.cbui-block-plus:hover { background: var(--color-primary); color: var(--color-on-primary, #fff); border-color: var(--color-primary); }
.cbui-block-plus svg { width: 1rem; height: 1rem; }
.cbui-block-doc-modern .cbui-block-card { user-select: none; }
.cbui-block-row-multi > .cbui-block-card { background: color-mix(in srgb, var(--color-primary) 10%, var(--color-surface)); border-color: var(--color-primary); }
.cbui-block-row-flash > .cbui-block-card { animation: cbui-block-flash 0.9s ease-out; }
@keyframes cbui-block-flash { from { background: color-mix(in srgb, var(--color-success) 22%, transparent); } to { background: transparent; } }
.cbui-block-selbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    padding: 0.4rem 0.6rem;
    border-radius: var(--radius-md);
    background: var(--color-surface);
    border: 1px solid var(--color-primary);
    box-shadow: var(--shadow-md);
    font-weight: 600;
}
.cbui-block-selbar[hidden] { display: none; }

