:root {
    --bg-canvas: #f8fafc;
    --bg-surface: #ffffff;
    --bg-sidebar: #0f172a;
    --bg-sidebar-hover: rgba(255, 255, 255, 0.06);
    --bg-sidebar-active: rgba(99, 102, 241, 0.16);
    --border-subtle: #e2e8f0;
    --border-emphasis: #cbd5e1;
    --text-primary: #0f172a;
    --text-secondary: #475569;
    --text-muted: #94a3b8;
    --text-on-dark: #cbd5e1;
    --text-on-dark-active: #ffffff;
    --accent: #6366f1;
    --accent-soft: #eef2ff;
    --success: #10b981;
    --danger: #ef4444;
    --shadow-sm: 0 1px 2px 0 rgba(15, 23, 42, 0.04);
    --shadow-md: 0 4px 12px -2px rgba(15, 23, 42, 0.06), 0 2px 4px -2px rgba(15, 23, 42, 0.04);
    --radius-sm: 6px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --topbar-height: 56px;
    --sidebar-width: 216px;
}

* {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    padding: 0;
    height: 100vh;        /* fallback for older browsers */
    height: 100dvh;       /* iOS Safari: stay correct as the address bar resizes */
    overflow: hidden;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    background-color: var(--bg-canvas);
    color: var(--text-primary);
    font-size: 14px;
    line-height: 1.5;
}

.app {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
}

/* ─── Top bar ─────────────────────────────────────────────────── */

.topbar {
    display: flex;
    align-items: center;
    gap: 24px;
    height: var(--topbar-height);
    padding: 0 20px;
    background-color: var(--bg-surface);
    border-bottom: 1px solid var(--border-subtle);
    flex-shrink: 0;

    /* PWA / iOS standalone: when launched from the homescreen, the OS no
       longer reserves the status-bar strip, so the topbar would slide under
       the notch / camera island. env(safe-area-inset-top) is zero in a
       regular browser tab (requires viewport-fit=cover in the <meta
       viewport>, which App.razor now sets) and ~44–60px in standalone mode
       on iPhone X+. We add it to padding *and* grow the height by the same
       amount so the original 56px content area stays unchanged. */
    padding-top: env(safe-area-inset-top);
    height: calc(var(--topbar-height) + env(safe-area-inset-top));
}

.sidebar-toggle {
    /* Reset the topbar's 24px gap on the right side so the hamburger sits
       closer to the brand than the search box does to it. */
    margin-right: -16px;
    font-size: 18px;
}

@media (min-width: 768px) {
    /* Desktop semantics: the persisted preference hides the sidebar entirely.
       Below 768px the sidebar becomes an overlay drawer controlled by the
       transient `sidebar-open` class instead — see the mobile block below. */
    html.sidebar-collapsed .sidebar {
        display: none;
    }
}

html.sidebar-collapsed .brand {
    min-width: 0;
}

.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: calc(var(--sidebar-width) - 20px);
}

.brand-mark {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: conic-gradient(from 220deg at 50% 50%, #6366f1, #8b5cf6, #ec4899, #6366f1);
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.85), 0 0 0 1px rgba(15, 23, 42, 0.05);
}

.brand-name {
    font-weight: 600;
    font-size: 15px;
    letter-spacing: -0.01em;
    color: var(--text-primary);
}

.brand-env {
    font-size: 11px;
    font-weight: 500;
    color: var(--accent);
    background-color: var(--accent-soft);
    padding: 2px 8px;
    border-radius: 999px;
    text-transform: lowercase;
    letter-spacing: 0.02em;
}

.topbar-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
}

.icon-btn {
    position: relative;
    width: 34px;
    height: 34px;
    border: none;
    background: transparent;
    border-radius: var(--radius-md);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    font-size: 16px;
    transition: background-color 120ms ease, color 120ms ease;
}

.icon-btn:hover {
    background-color: var(--bg-canvas);
    color: var(--text-primary);
}

/* ─── Bell three-state palette (push notification status) ─────────────────
   Driven by push-notifications.js setBellState():
     .is-bell-red     — push not possible (server/browser/permission)
     .is-bell-outline — possible, not yet enabled
     .is-bell-green   — subscribed and live
   Color tokens map to Bootstrap's danger/success since the rest of Sphere
   uses BS variables everywhere. Hover keeps the state color so the user
   doesn't lose the visual signal on focus.
   ──────────────────────────────────────────────────────────────────────── */
.icon-btn.is-bell-red {
    color: var(--bs-danger, #dc3545);
}
.icon-btn.is-bell-red:hover {
    color: var(--bs-danger, #dc3545);
    background-color: var(--bg-canvas);
}
.icon-btn.is-bell-red[disabled],
.icon-btn.is-bell-red:disabled {
    /* override the global :disabled grey so the red signal stays loud */
    color: var(--bs-danger, #dc3545);
    opacity: 0.85;
    cursor: not-allowed;
}
.icon-btn.is-bell-green {
    color: var(--bs-success, #198754);
}
.icon-btn.is-bell-green:hover {
    color: var(--bs-success, #198754);
    background-color: var(--bg-canvas);
}
/* Orange "warn" — browser thinks it's subscribed but the server has no row
   (or pruned it as stale). Click re-runs subscribe() to re-POST the existing
   browser subscription to /api/push/subscribe. */
.icon-btn.is-bell-warn {
    color: var(--bs-warning, #f39c12);
}
.icon-btn.is-bell-warn:hover {
    color: var(--bs-warning, #f39c12);
    background-color: var(--bg-canvas);
}
.icon-btn.is-bell-outline {
    color: var(--text-secondary);
}
/* outline hover already covered by .icon-btn:hover above */

.badge-dot {
    position: absolute;
    top: 8px;
    right: 9px;
    width: 7px;
    height: 7px;
    background-color: var(--danger);
    border-radius: 50%;
    border: 2px solid var(--bg-surface);
}

.topbar-divider {
    width: 1px;
    height: 24px;
    background-color: var(--border-subtle);
    margin: 0 8px;
}

.user-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 10px 4px 4px;
    border: none;
    background: transparent;
    border-radius: var(--radius-md);
    cursor: pointer;
    color: var(--text-primary);
    font-family: inherit;
    font-size: 13.5px;
    transition: background-color 120ms ease;
}

.user-chip:hover {
    background-color: var(--bg-canvas);
}

.user-chip i {
    color: var(--text-muted);
    font-size: 11px;
}

.avatar {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
    color: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.user-name {
    font-weight: 500;
}

/* ─── Shell (sidebar + content) ───────────────────────────────── */

.shell {
    display: flex;
    flex: 1;
    overflow: hidden;
}

.sidebar {
    width: var(--sidebar-width);
    background-color: var(--bg-sidebar);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    overflow-y: auto;
}

.nav-menu {
    display: flex;
    flex-direction: column;
    padding: 12px 10px;
    gap: 1px;
    flex: 1;
}

.nav-item a {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    border-radius: var(--radius-md);
    color: var(--text-on-dark);
    text-decoration: none;
    font-size: 13.5px;
    font-weight: 500;
    transition: background-color 120ms ease, color 120ms ease;
}

.nav-item a i {
    font-size: 16px;
    width: 18px;
    text-align: center;
    color: var(--text-muted);
    transition: color 120ms ease;
}

.nav-item a:hover {
    background-color: var(--bg-sidebar-hover);
    color: var(--text-on-dark-active);
}

.nav-item a:hover i {
    color: var(--text-on-dark);
}

.nav-item a.active {
    background-color: var(--bg-sidebar-active);
    color: var(--text-on-dark-active);
    position: relative;
}

.nav-item a.active::before {
    content: '';
    position: absolute;
    left: -10px;
    top: 8px;
    bottom: 8px;
    width: 3px;
    background-color: var(--accent);
    border-radius: 0 2px 2px 0;
}

.nav-item a.active i {
    color: var(--accent);
}

.sidebar-badge {
    margin-left: auto;
    padding: 1px 8px;
    background-color: rgba(255, 255, 255, 0.10);
    color: var(--text-on-dark-active);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.6;
}

.nav-item a:hover .sidebar-badge {
    background-color: rgba(255, 255, 255, 0.16);
}

.nav-item a.active .sidebar-badge {
    background-color: rgba(99, 102, 241, 0.32);
    color: #ffffff;
}

.sidebar-footer {
    padding: 14px 22px;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.status {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 11.5px;
    color: var(--text-on-dark);
    font-weight: 500;
}

.status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background-color: var(--success);
    box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18);
}

.version {
    font-size: 11px;
    color: var(--text-muted);
    margin-left: 16px;
}

/* ─── Content ─────────────────────────────────────────────────── */

.content {
    flex: 1;
    overflow-y: auto;
    padding: 32px 40px;
    background-color: var(--bg-canvas);
}

.content h1 {
    margin: 0 0 8px 0;
    font-size: 24px;
    font-weight: 600;
    letter-spacing: -0.015em;
    color: var(--text-primary);
}

.content h1::after {
    content: '';
    display: block;
    width: 36px;
    height: 3px;
    background-color: var(--accent);
    border-radius: 2px;
    margin-top: 14px;
}

/* ─── Sidebar backdrop (mobile drawer dismiss target) ─────────── */

/* Plain <button> reset — only visible/active when html has `sidebar-open`,
   and only on narrow viewports (the rule below). The button gives us free
   accessible "tap outside to close" behaviour without an extra JS listener. */
.sidebar-backdrop {
    display: none;
    position: fixed;
    inset: var(--topbar-height) 0 0 0;
    background: rgba(15, 23, 42, 0.45);
    border: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
    z-index: 50;
}

/* ─── Mobile shell (≤768px) ───────────────────────────────────── */

@media (max-width: 767.98px) {
    /* Topbar slims down: drop the env pill, the user-name (avatar stays),
       and the visual divider between actions and identity. */
    .topbar {
        gap: 8px;
        padding: 0 12px;
    }

    .brand {
        min-width: 0;
        flex-shrink: 1;
    }

    .brand-env,
    .user-name,
    .topbar-divider {
        display: none;
    }

    /* Sidebar becomes an off-canvas drawer. Fixed-position so it floats over
       content rather than reflowing it, full-height under the topbar, and
       slid out of view by default. `sidebar-open` brings it on screen.
       The top offset matches .topbar's standalone-mode height (base + safe
       area) so the drawer hugs the topbar's bottom edge on iPhone X+ in
       installed-PWA mode. The bottom inset keeps the last nav item clear of
       the iOS home indicator. */
    .sidebar {
        position: fixed;
        top: calc(var(--topbar-height) + env(safe-area-inset-top));
        bottom: 0;
        left: 0;
        z-index: 60;
        transform: translateX(-100%);
        transition: transform 180ms ease;
        box-shadow: 4px 0 16px -4px rgba(15, 23, 42, 0.18);
        padding-bottom: env(safe-area-inset-bottom);
    }

    html.sidebar-open .sidebar {
        transform: translateX(0);
    }

    html.sidebar-open .sidebar-backdrop {
        display: block;
    }

    /* Content gets the full row since the sidebar is now an overlay. Tighter
       padding so 375px phones don't waste 80px of horizontal space on gutters.
       The safe-area-inset-bottom keeps content clear of the iOS home indicator
       when running as an installed PWA. */
    .content {
        padding: 20px 16px;
        padding-bottom: calc(20px + env(safe-area-inset-bottom));
    }

    /* Any wide table inside the content area scrolls horizontally on its own
       rather than expanding the viewport. Setting `display: block` on the
       table makes it a scroll container; the browser wraps the descendants
       in an anonymous table box so column alignment is preserved. */
    .data-table {
        display: block;
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
}

/* ─── Auth ────────────────────────────────────────────────────── */

.sign-in-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 14px;
    border-radius: var(--radius-md);
    background-color: var(--accent);
    color: #ffffff;
    text-decoration: none;
    font-size: 13px;
    font-weight: 500;
    transition: background-color 120ms ease, box-shadow 120ms ease;
}

.sign-in-btn:hover {
    background-color: #4f46e5;
    box-shadow: 0 1px 2px 0 rgba(79, 70, 229, 0.4);
}

.sign-in-btn i {
    font-size: 14px;
}

.user-chip {
    text-decoration: none;
}

.logout-form {
    display: contents;
}

/* ─── Bootstrap overrides (Sphere theme) ──────────────────────── */

.content hr {
    border: 0;
    border-top: 1px solid var(--border-subtle);
    margin: 18px 0;
}

/* Forms */

.form-control {
    width: 100%;
    padding: 9px 12px;
    background-color: var(--bg-surface);
    color: var(--text-primary);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    font-family: inherit;
    font-size: 14px;
    line-height: 1.5;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}

.form-control:focus {
    outline: none;
    border-color: var(--accent);
    background-color: var(--bg-surface);
    color: var(--text-primary);
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.14);
}

.form-control:disabled,
.form-control[readonly] {
    background-color: var(--bg-canvas);
    color: var(--text-muted);
    cursor: not-allowed;
}

.form-control::placeholder {
    color: var(--text-muted);
}

.form-label {
    display: block;
    margin-bottom: 6px;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-secondary);
}

/* Replace Bootstrap's floating-label pattern with a stacked label-then-input */
.form-floating {
    position: static;
    display: flex;
    flex-direction: column-reverse;
    margin-bottom: 14px;
}

.form-floating > .form-control,
.form-floating > .form-control:focus,
.form-floating > .form-control:not(:placeholder-shown) {
    height: auto;
    min-height: auto;
    padding: 9px 12px;
}

.form-floating > label,
.form-floating > .form-control:focus ~ label,
.form-floating > .form-control:not(:placeholder-shown) ~ label {
    position: static;
    transform: none;
    height: auto;
    padding: 0 0 6px 0;
    margin: 0;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-secondary);
    pointer-events: auto;
    background: transparent;
    opacity: 1;
    z-index: auto;
}

/* Checkboxes */

.form-check-input {
    width: 16px;
    height: 16px;
    margin: 0;
    border: 1px solid var(--border-emphasis);
    border-radius: 4px;
    background-color: var(--bg-surface);
    cursor: pointer;
    accent-color: var(--accent);
}

.form-check-input:focus {
    outline: none;
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.18);
    border-color: var(--accent);
}

.checkbox label {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    cursor: pointer;
    font-size: 13.5px;
    color: var(--text-secondary);
}

/* Buttons */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 9px 18px;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.4;
    cursor: pointer;
    text-decoration: none;
    /* Inherit body color so bare `.btn` (no -primary / -danger / -link
       variant) follows --text-primary in both themes. Without this the
       UA's `<button>` default colour wins and bare buttons (Edit/Share/
       Cancel in modals) go unreadable on the dark surface. Variants
       below set their own `color` explicitly and override `inherit`. */
    color: inherit;
    transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease, box-shadow 120ms ease;
}

.btn:focus,
.btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.22);
}

.btn-lg {
    padding: 11px 22px;
    font-size: 14px;
}

.btn-primary {
    background-color: var(--accent);
    color: #ffffff;
    border-color: var(--accent);
}

.btn-primary:hover {
    background-color: #4f46e5;
    border-color: #4f46e5;
    color: #ffffff;
}

.btn-danger {
    background-color: var(--danger);
    color: #ffffff;
    border-color: var(--danger);
}

.btn-danger:hover {
    background-color: #dc2626;
    border-color: #dc2626;
    color: #ffffff;
}

.btn-link {
    background-color: transparent;
    color: var(--accent);
    padding: 0;
    border: none;
}

.btn-link:hover {
    text-decoration: underline;
    color: #4f46e5;
}

/* Alerts */

.alert {
    padding: 12px 16px;
    margin: 0 0 16px 0;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-size: 13.5px;
    line-height: 1.55;
}

.alert-success {
    background-color: #f0fdf4;
    color: #14532d;
    border-color: #bbf7d0;
}

.alert-danger {
    background-color: #fef2f2;
    color: #7f1d1d;
    border-color: #fecaca;
}

.alert-warning {
    background-color: #fffbeb;
    color: #78350f;
    border-color: #fde68a;
}

.alert-info {
    background-color: var(--accent-soft);
    color: #312e81;
    border-color: #c7d2fe;
}

/* Nav pills (ManageNavMenu) */

.nav-pills {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.nav-pills .nav-item {
    margin: 0;
}

.nav-pills .nav-link {
    display: block;
    padding: 7px 12px;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 13.5px;
    font-weight: 500;
    transition: background-color 120ms ease, color 120ms ease;
}

.nav-pills .nav-link:hover {
    background-color: var(--bg-canvas);
    color: var(--text-primary);
}

.nav-pills .nav-link.active {
    background-color: var(--accent-soft);
    color: var(--accent);
}

/* Validation / status text */

.text-danger {
    display: inline-block;
    color: var(--danger);
    font-size: 12.5px;
    font-weight: 500;
}

.text-success {
    color: var(--success);
}

.text-info {
    color: var(--accent);
}

.font-weight-bold {
    font-weight: 600;
}

/* Anonymous auth page shell (no sidebar / no topbar) */

.auth-page {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 40px 24px;
    background:
        radial-gradient(circle at 20% 18%, rgba(99, 102, 241, 0.10), transparent 42%),
        radial-gradient(circle at 82% 84%, rgba(139, 92, 246, 0.07), transparent 42%),
        var(--bg-canvas);
}

.auth-page-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 28px;
}

.auth-page-brand .brand-mark {
    width: 28px;
    height: 28px;
}

.auth-page-brand .brand-name {
    font-size: 20px;
    font-weight: 600;
    color: var(--text-primary);
    letter-spacing: -0.015em;
}

.auth-page-body {
    width: 100%;
    display: flex;
    justify-content: center;
}

.auth-page-body .auth-card {
    margin: 0;
}

.auth-page-footer {
    margin-top: 28px;
    color: var(--text-muted);
    font-size: 12px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.auth-page-footer .status-dot {
    width: 6px;
    height: 6px;
    box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.18);
}

/* Auth card — Login and other anonymous auth pages */

.auth-card {
    max-width: 420px;
    margin: 8px auto 32px auto;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    padding: 32px 32px 28px 32px;
    box-shadow: var(--shadow-md);
}

.auth-card .auth-title {
    margin: 0 0 6px 0;
    font-size: 22px;
    font-weight: 600;
    color: var(--text-primary);
    letter-spacing: -0.015em;
}

.auth-card .auth-title::after {
    display: none;
}

.auth-card .auth-subtitle {
    margin: 0 0 22px 0;
    font-size: 13.5px;
    color: var(--text-secondary);
}

.auth-card hr {
    margin: 0 0 18px 0;
}

/* Manage shell — two-column nav + content */

.manage-shell {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 32px;
    margin-top: 22px;
}

.manage-shell .manage-sidebar {
    border-right: 1px solid var(--border-subtle);
    padding-right: 16px;
}

.manage-shell .manage-content {
    min-width: 0;
}

@media (max-width: 720px) {
    .manage-shell {
        grid-template-columns: 1fr;
    }

    .manage-shell .manage-sidebar {
        border-right: none;
        border-bottom: 1px solid var(--border-subtle);
        padding-right: 0;
        padding-bottom: 16px;
    }
}

/* ─── Tabs ────────────────────────────────────────────────────── */

.tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--border-subtle);
    margin: 18px 0 22px 0;
}

.tabs .tab {
    padding: 8px 14px;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 13.5px;
    font-weight: 500;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color 120ms ease, border-color 120ms ease;
}

.tabs .tab:hover {
    color: var(--text-primary);
}

.tabs .tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* ─── Data table ─────────────────────────────────────────────── */

.data-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    font-size: 13.5px;
}

.data-table th,
.data-table td {
    padding: 10px 14px;
    text-align: left;
    border-bottom: 1px solid var(--border-subtle);
    vertical-align: middle;
}

.data-table th {
    font-weight: 600;
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    background-color: var(--bg-canvas);
}

.data-table th.sortable {
    cursor: pointer;
    user-select: none;
    transition: color 100ms ease, background-color 100ms ease;
}

.data-table th.sortable:hover {
    color: var(--text-secondary);
    background-color: rgba(15, 23, 42, 0.03);
}

.data-table th.sortable.is-sorted {
    color: var(--text-primary);
}

.data-table th .sort-indicator {
    margin-left: 4px;
    color: var(--accent);
    font-size: 10.5px;
    vertical-align: middle;
}

.data-table tbody tr:last-child td {
    border-bottom: none;
}

.data-table tbody tr:hover {
    background-color: rgba(99, 102, 241, 0.04);
}

.data-table .cell-strong {
    font-weight: 600;
    color: var(--text-primary);
}

.data-table .cell-link {
    font-weight: 600;
    color: var(--text-primary);
    text-decoration: none;
}

.data-table .cell-link:hover {
    color: var(--accent);
    text-decoration: underline;
}

.data-table .cell-muted {
    color: var(--text-muted);
}

.data-table .project-input {
    padding: 5px 8px;
    font-size: 13px;
    background-color: transparent;
    border-color: transparent;
}

.data-table .project-input:hover {
    border-color: var(--border-subtle);
    background-color: var(--bg-surface);
}

.data-table .project-input:focus {
    border-color: var(--accent);
    background-color: var(--bg-surface);
}

.row-actions {
    display: flex;
    gap: 6px;
    justify-content: flex-end;
}

.icon-btn-danger:hover {
    color: var(--danger);
    background-color: #fef2f2;
}

/* Row action menu (three-dot popover) */

.row-menu-wrapper {
    position: relative;
    display: flex;
    justify-content: flex-end;
}

/* Name cells in row grids (Active, Endpoints, Policies, Prompts) are
   buttons styled as links — clicking opens the same drill-down view as
   the kebab menu's primary action. Gives mobile users a real tap target
   without forcing them to hit the small three-dots button. */
.row-name-link {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: var(--link, #1d4e5f);
    text-align: left;
    cursor: pointer;
    text-decoration: none;
}
.row-name-link:hover,
.row-name-link:focus-visible {
    text-decoration: underline;
    outline: none;
}
.row-name-link:focus-visible {
    outline: 2px solid var(--focus-ring, #2563eb);
    outline-offset: 2px;
    border-radius: 2px;
}

.row-menu-trigger {
    width: 30px;
    height: 30px;
    font-size: 15px;
}

.row-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 100;
    min-width: 180px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: 0 10px 20px -6px rgba(15, 23, 42, 0.18), 0 4px 8px -4px rgba(15, 23, 42, 0.10);
    padding: 4px;
}

.row-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 7px 10px;
    background-color: transparent;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    font-size: 13px;
    color: var(--text-primary);
    transition: background-color 80ms ease, color 80ms ease;
}

.row-menu-item:hover {
    background-color: var(--bg-canvas);
}

.row-menu-item i {
    width: 16px;
    text-align: center;
    color: var(--text-muted);
    font-size: 14px;
    transition: color 80ms ease;
}

.row-menu-item:hover i {
    color: var(--text-secondary);
}

.row-menu-item-danger {
    color: var(--danger);
}

.row-menu-item-danger i {
    color: var(--danger);
}

.row-menu-item-danger:hover {
    background-color: #fef2f2;
    color: var(--danger);
}

.row-menu-item-danger:hover i {
    color: var(--danger);
}

.row-menu-separator {
    height: 1px;
    background-color: var(--border-subtle);
    margin: 4px 2px;
}

.menu-backdrop {
    position: fixed;
    inset: 0;
    z-index: 90;
    background-color: transparent;
}

/* Entry-point chips */

.entry-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.entry-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 8px;
    border-radius: 999px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    font-size: 12px;
    line-height: 1.5;
}

.entry-chip .entry-name {
    font-weight: 600;
    color: var(--text-primary);
}

.entry-chip .entry-type {
    color: var(--text-secondary);
    font-style: italic;
}

.entry-chip .entry-type-json {
    color: var(--success);
    font-style: normal;
    font-weight: 500;
}

/* ─── Switch (toggle) ─────────────────────────────────────────── */

.switch {
    position: relative;
    display: inline-block;
    width: 34px;
    height: 20px;
    cursor: pointer;
    vertical-align: middle;
}

.switch input {
    opacity: 0;
    width: 0;
    height: 0;
    position: absolute;
}

.switch-slider {
    position: absolute;
    inset: 0;
    background-color: var(--border-emphasis);
    border-radius: 999px;
    transition: background-color 120ms ease;
}

.switch-slider::before {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    background-color: #ffffff;
    border-radius: 50%;
    transition: transform 120ms ease;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}

.switch input:checked + .switch-slider {
    background-color: var(--accent);
}

.switch input:checked + .switch-slider::before {
    transform: translateX(14px);
}

.switch input:focus-visible + .switch-slider {
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.22);
}

/* ─── Page toolbar / state (shared across data pages) ─────────── */

.page-toolbar {
    display: flex;
    gap: 12px;
    align-items: center;
    margin-bottom: 14px;
}

/* ─── Form rows used inside modal-body forms (Scheduler new-job dialog, etc.) ── */

.form-row {
    margin-bottom: 14px;
}

.form-row > label {
    display: block;
    margin-bottom: 6px;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-secondary);
}

.form-row--inline {
    display: grid;
    grid-template-columns: max-content 1fr 1fr;
    align-items: center;
    column-gap: 10px;
}

.form-row--inline > label {
    margin-bottom: 0;
}

.form-help {
    display: block;
    margin-top: 6px;
    font-size: 11.5px;
    color: var(--text-muted);
}

.form-help code {
    font-size: 11px;
    color: var(--text-secondary);
}

.form-control--mono {
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12.5px;
    line-height: 1.5;
}

/* Segmented control — two-or-three button toggle, used for Start/Message + Once/Cron in the
   scheduler new-job dialog. Sits on top of regular .btn-style buttons that get type="button". */
/* Flux Keys editor — form-tab rows. Each row is a Type pill + Name input
   (outputs only) + delete button, laid out on a single line. Skeleton-only;
   the per-type field forms are added in later slices. */
.fluxkeys-section-title {
    margin: 0.25rem 0 0.5rem;
    font-size: 0.95rem;
}

.fluxkeys-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0;
}

.fluxkeys-row--add {
    margin-top: 0.25rem;
}

.fluxkeys-kind {
    display: inline-flex;
    align-items: center;
    padding: 0.15rem 0.55rem;
    border-radius: var(--radius-sm);
    background: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    font-size: 0.8125rem;
    color: var(--text-secondary);
    min-width: 5.5em;
    justify-content: center;
}

/* Per-row "card" wrapping a Type + (optional) Name header and a body with
   type-specific fields. Each card represents one OutputClients entry or one
   Services slot in the FluxConfig JSON the dialog produces. */
.fluxkeys-card {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.5rem;
    background: var(--bg-surface);
}

.fluxkeys-card-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.fluxkeys-card-spacer {
    flex: 1 1 auto;
}

.fluxkeys-card-body {
    margin-top: 0.5rem;
    padding-left: 0.75rem;
    border-left: 2px solid var(--border-subtle);
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.fluxkeys-field-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.fluxkeys-field-label {
    flex: 0 0 8.5em;
    font-size: 0.85rem;
    color: var(--text-secondary);
    margin: 0;
}

.fluxkeys-field-row .form-control {
    flex: 1 1 auto;
}

.fluxkeys-cycle-amount {
    flex: 0 0 6em;
}

.fluxkeys-cycle-unit {
    flex: 0 0 8em;
}

.fluxkeys-stub {
    font-size: 0.85rem;
    font-style: italic;
}

.fluxkeys-inline-check {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    flex: 1 1 auto;
    font-size: 0.85rem;
}

/* Sub-block for HTTP input's PollChannels list — one channel per nested
   card. Keeps the inner cards visually distinct from the parent. */
.fluxkeys-subsection {
    margin-top: 0.25rem;
}

.fluxkeys-subsection-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 0.35rem;
}

.fluxkeys-channel {
    border: 1px dashed var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.6rem;
    margin-bottom: 0.4rem;
    background: var(--bg-canvas);
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

/* Slice 3 — validation indicators. Cards/channels with at least one error get
   a tinted border + background; per-field errors render as a small red
   line below the input. The dialog also shows a roll-up summary at the top. */
.fluxkeys-card--error {
    border-color: var(--danger, #dc3545);
    background: rgba(220, 53, 69, 0.04);
}

.fluxkeys-channel--error {
    border-color: var(--danger, #dc3545);
    background: rgba(220, 53, 69, 0.04);
}

.fluxkeys-field-error {
    display: block;
    color: var(--danger, #dc3545);
    font-size: 0.8rem;
    margin-top: -0.15rem;
}

.fluxkeys-field-error--indent {
    margin-left: calc(8.5em + 0.5rem);
}

.fluxkeys-name-wrap {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.fluxkeys-name-wrap .form-control {
    width: 100%;
}

.fluxkeys-issues {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.fluxkeys-issues-header {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.9rem;
}

.fluxkeys-issues-list {
    margin: 0;
    padding-left: 1.6rem;
    font-size: 0.85rem;
}

.seg-group {
    display: inline-flex;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background-color: var(--bg-canvas);
    padding: 2px;
    gap: 2px;
}

.seg-btn {
    appearance: none;
    background: transparent;
    border: none;
    padding: 6px 14px;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-secondary);
    border-radius: calc(var(--radius-md) - 2px);
    cursor: pointer;
    transition: background-color 100ms ease, color 100ms ease;
    font-family: inherit;
}

.seg-btn:hover {
    color: var(--text-primary);
}

.seg-btn.is-active {
    background-color: var(--bg-surface);
    color: var(--text-primary);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}

/* Modal footer used by Scheduler dialogs — right-aligned button row. */
.modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 14px 22px;
    border-top: 1px solid var(--border-subtle);
    flex-shrink: 0;
}

/* ─── Policy editor (Development → Policies card) ────────────────────────── */

.policy-editor-shell {
    display: grid;
    grid-template-columns: 280px 1fr;
    gap: 14px;
    height: calc(100vh - var(--topbar-height) - 220px);
    min-height: 480px;
}

.policy-editor-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px;
    overflow: hidden;
}

.policy-editor-list .form-control {
    flex-shrink: 0;
}

.policy-list {
    list-style: none;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    flex: 1;
    min-height: 0;
}

.policy-list-item {
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color 100ms ease;
}

.policy-list-item:hover {
    background-color: var(--bg-canvas);
}

.policy-list-item.is-selected {
    background-color: var(--accent-soft);
}

.policy-list-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.policy-list-item.is-selected .policy-list-name {
    color: var(--accent);
}

.policy-list-meta {
    font-size: 11.5px;
    color: var(--text-muted);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.policy-editor-main {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px;
    overflow: hidden;
    min-width: 0;
}

.policy-editor-header {
    display: flex;
    align-items: flex-end;
    gap: 14px;
    flex-wrap: wrap;
}

.policy-editor-fields {
    display: grid;
    grid-template-columns: 1fr 1fr 100px;
    gap: 10px;
    flex: 1 1 460px;
    min-width: 0;
}

.policy-editor-fields .form-row {
    margin-bottom: 0;
}

.policy-editor-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.policy-editor-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.policy-editor-dirty-indicator {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--accent);
    font-weight: 500;
}

.policy-editor-dirty-indicator i {
    font-size: 18px;
    line-height: 0;
}

.policy-editor-mount {
    flex: 1;
    min-height: 320px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

@media (max-width: 960px) {
    .policy-editor-shell {
        grid-template-columns: 1fr;
        height: auto;
    }

    .policy-editor-list {
        max-height: 240px;
    }
}

/* ─── Connector Maker page ────────────────────────────────────────────────── */

/* Anchored to the visible viewport. No `min-height` floor — on short viewports
   the shell compresses with the editor rather than overflowing `.content`'s
   scroll area (which would clip Monaco's bottom row + scrollbar handle). The
   260px subtraction covers .content's 32px top + 32px bottom padding, the
   page <h1> + .page-intro stack (~120px), and a small breathing-room margin. */
.connector-maker-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
    height: calc(100vh - var(--topbar-height) - 260px);
    min-height: 360px;
}

.connector-maker-main {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px;
    overflow: hidden;
    /* Flex children default to `min-height: auto` which refuses to shrink past
       intrinsic content size — that's what was letting `min-height: 320px` on
       .connector-maker-mount escape this container. Force shrinkability. */
    min-height: 0;
    min-width: 0;
    flex: 1;
}

.connector-maker-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
}

.connector-maker-meta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-muted);
    min-width: 0;
    flex-wrap: wrap;
}

.connector-maker-runtime-label {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-secondary);
    margin: 0;
}

.connector-maker-runtime-select {
    width: auto;
    min-width: 180px;
    padding-top: 4px;
    padding-bottom: 4px;
    font-size: 13px;
}

.connector-maker-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bg-canvas);
    color: var(--text-muted);
    text-align: center;
    padding: 24px;
}

.connector-maker-placeholder i {
    font-size: 32px;
    display: block;
    margin-bottom: 8px;
    color: var(--border-strong);
}

.connector-maker-placeholder p {
    margin: 0;
    font-size: 14px;
}

.connector-maker-placeholder-hint {
    margin-top: 6px !important;
    font-size: 12.5px;
    max-width: 420px;
}

.form-required {
    color: var(--danger, #d62a2a);
    font-weight: 600;
}

.connector-maker-filename {
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.connector-maker-meta-sep {
    color: var(--border-subtle);
}

.connector-maker-meta-hint {
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12px;
}

.connector-maker-dirty {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    color: var(--accent);
    font-weight: 500;
}

.connector-maker-dirty i {
    font-size: 18px;
    line-height: 0;
}

.connector-maker-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.connector-maker-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.connector-maker-hint {
    margin: 0;
    padding: 8px 10px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    font-size: 12.5px;
    color: var(--text-muted);
    line-height: 1.5;
}

.connector-maker-hint code {
    font-size: 12px;
    background-color: var(--bg-surface);
    padding: 1px 4px;
    border-radius: 3px;
}

.connector-maker-mount {
    flex: 1;
    /* Smaller usability floor than the previous 320px so the mount actually
       fits inside the shell on viewports where min-height kicks in. */
    min-height: 220px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

/* ─── KPI page ─────────────────────────────────────────────────────────────── */

.kpi-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.kpi-config-card {
    display: flex;
    flex-direction: column;
    gap: 18px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 18px 20px;
}

.kpi-section {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Pair Aggregation + Chart on a single row to save vertical space. Wraps on narrow screens. */
.kpi-section-row {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
    align-items: flex-start;
}

.kpi-section-row > .kpi-section {
    flex: 1 1 auto;
    min-width: 0;
}

.kpi-section-subrow {
    margin-top: 8px;
}

.kpi-section h3 {
    margin: 0 0 2px 0;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.kpi-chip-tray {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    min-height: 36px;
    padding: 6px 8px;
    border: 1px dashed var(--border-subtle);
    border-radius: var(--radius-sm);
    background-color: var(--bg-canvas);
}

.kpi-chip-tray.is-empty {
    border-style: dashed;
}

.kpi-chip-empty {
    color: var(--text-muted);
    font-size: 12.5px;
    align-self: center;
}

.kpi-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 4px 3px 10px;
    background-color: var(--accent-soft);
    color: var(--accent);
    border-radius: 999px;
    font-size: 12px;
    font-weight: 500;
    line-height: 1;
}

.kpi-chip-text {
    padding-block: 4px;
}

.kpi-chip-remove {
    appearance: none;
    border: none;
    background: transparent;
    color: inherit;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0.7;
    transition: background-color 100ms ease, opacity 100ms ease;
}

.kpi-chip-remove:hover {
    background-color: rgba(99, 102, 241, 0.16);
    opacity: 1;
}

.kpi-chip-picker {
    position: relative;
}

.kpi-chip-search-icon {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    font-size: 13px;
    pointer-events: none;
}

.kpi-chip-input {
    width: 100%;
    height: 34px;
    padding: 0 12px 0 32px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background-color: var(--bg-canvas);
    font-family: inherit;
    font-size: 13px;
    color: var(--text-primary);
    transition: border-color 120ms ease, background-color 120ms ease;
}

.kpi-chip-input:focus {
    outline: none;
    border-color: var(--accent);
    background-color: var(--bg-surface);
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.12);
}

.kpi-chip-suggestions {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 100;
    list-style: none;
    margin: 0;
    padding: 4px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px -6px rgba(15, 23, 42, 0.15);
    max-height: 240px;
    overflow-y: auto;
}

.kpi-chip-suggestions li {
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 13px;
    color: var(--text-primary);
}

.kpi-chip-suggestions li:hover {
    background-color: var(--bg-canvas);
}

.kpi-chip-suggestion-empty {
    color: var(--text-muted);
    cursor: default !important;
    font-style: italic;
}

.kpi-chip-suggestion-empty:hover {
    background-color: transparent !important;
}

.kpi-presets {
    flex-wrap: wrap;
}

.kpi-window-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 10px;
}

.kpi-window-grid .form-row {
    margin-bottom: 0;
}

.kpi-config-actions {
    display: flex;
    align-items: center;
    gap: 12px;
}

.kpi-config-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.kpi-last-gen {
    font-size: 12px;
    color: var(--text-muted);
    font-feature-settings: 'tnum';
}

.kpi-chart-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 16px 20px;
}

.kpi-chart-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.kpi-chart-header h3 {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.kpi-chart-meta {
    font-size: 12px;
    color: var(--text-muted);
    font-feature-settings: 'tnum';
}

@media (max-width: 720px) {
    .kpi-window-grid {
        grid-template-columns: 1fr;
    }
}

/* ─── Tabs (Realms page) ─────────────────────────────────────────────────── */

.tab-nav {
    display: flex;
    gap: 2px;
    border-bottom: 1px solid var(--border-subtle);
    margin: 18px 0 18px 0;
    overflow-x: auto;
}

.tab-button {
    appearance: none;
    background: transparent;
    border: none;
    padding: 10px 16px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--text-secondary);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color 100ms ease, border-color 100ms ease, background-color 100ms ease;
    font-family: inherit;
    white-space: nowrap;
}

.tab-button:hover {
    color: var(--text-primary);
    background-color: var(--bg-surface);
}

.tab-button.is-active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

.tab-button i {
    font-size: 14px;
}

.tab-content {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* ─── Realms tab — small extras on top of the existing data-table styling ─ */

.cell-inline-icon {
    color: var(--text-muted);
    margin-right: 6px;
}

.cell-meta {
    margin-left: 10px;
    font-size: 12px;
    color: var(--accent);
    font-weight: 500;
}

/* ─── Realm detail page (/realms/{name}) ─────────────────────────────────── */

.page-breadcrumb {
    margin-bottom: 6px;
}

.breadcrumb-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text-muted);
    font-size: 13px;
    text-decoration: none;
}

.breadcrumb-link:hover {
    color: var(--accent);
}

.page-title-icon {
    margin-right: 10px;
    color: var(--text-muted);
    font-size: 0.85em;
}

.realm-detail-shell {
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.realm-detail-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.realm-detail-picker {
    flex: 1 1 320px;
    min-width: 240px;
    max-width: 480px;
}

.realm-detail-section {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.realm-detail-section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: 14.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.realm-detail-section-title > i {
    color: var(--text-muted);
}

.realm-detail-section-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    padding: 0 7px;
    height: 18px;
    border-radius: 9px;
    background-color: var(--bg-canvas);
    color: var(--text-muted);
    font-size: 11.5px;
    font-weight: 600;
}

.realm-detail-section-hint {
    margin: 0;
    font-size: 12.5px;
    color: var(--text-muted);
    line-height: 1.5;
}

.realm-detail-meta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--text-muted);
}

.realm-detail-meta code {
    color: var(--text-primary);
    background-color: var(--bg-canvas);
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 12px;
}

.realm-detail-meta-sep {
    color: var(--border-subtle);
}

.realm-detail-meta-hint {
    color: var(--text-muted);
}

.realm-detail-redis-pre {
    margin: 0;
    padding: 14px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    overflow-x: auto;
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--text-primary);
    white-space: pre;
}

/* ─── Types tab (master-detail) ──────────────────────────────────────────── */

.types-shell {
    display: grid;
    grid-template-columns: 320px 1fr;
    gap: 14px;
    min-height: 560px;
}

.types-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px;
    min-height: 0;
}

.types-list-toolbar {
    display: flex;
    gap: 6px;
    align-items: center;
}

.types-list-toolbar .form-control {
    flex: 1;
}

.types-add-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.types-list-items {
    list-style: none;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    flex: 1;
    max-height: 640px;
}

.types-list-item {
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    border: 1px solid transparent;
    transition: background-color 100ms ease, border-color 100ms ease;
    margin-bottom: 4px;
}

.types-list-item:hover {
    background-color: var(--bg-canvas);
}

.types-list-item.is-selected {
    background-color: var(--accent-soft);
    border-color: rgba(99, 102, 241, 0.25);
}

.types-list-name {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.types-list-item.is-selected .types-list-name {
    color: var(--accent);
}

.types-list-meta {
    font-size: 11.5px;
    color: var(--text-muted);
    margin-top: 2px;
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
}

.types-list-meta .dot {
    margin: 0 4px;
    opacity: 0.6;
}

/* Lambdas / Registry list items get a row of small pills for input/output type. */
.types-list-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 6px;
}

.types-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
}

.types-list-version {
    margin-left: 6px;
    font-size: 11.5px;
    font-weight: 500;
    color: var(--text-muted);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
}

.types-detail-version {
    margin-left: 8px;
    font-size: 13px;
    color: var(--text-muted);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
}

/* Readout grids for Lambdas / Registry detail panes. */
.types-readout-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px 18px;
}

.types-readout-grid .types-readout-full {
    grid-column: 1 / -1;
}

.types-readout-label {
    font-size: 11.5px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 4px;
}

.types-readout-value {
    font-size: 13px;
    color: var(--text-primary);
    word-break: break-word;
}

.types-readout-value--mono {
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12px;
    color: var(--text-secondary);
}

/* Package upload row in the Lambdas detail view. */
.types-package-controls {
    margin-top: 12px;
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}

.types-package-controls .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* ─── Schema Validator page ──────────────────────────────────────────────── */

.schema-validator-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.schema-validator-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 14px;
    padding: 14px 16px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}

.schema-validator-picker {
    flex: 1 1 320px;
    min-width: 280px;
    margin-bottom: 0;
}

.schema-validator-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin-left: auto;
}

.schema-validator-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.schema-validator-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
}

.schema-validator-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    min-width: 0;
}

.schema-validator-panel-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.schema-validator-panel-header h3 {
    margin: 0;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.schema-validator-panel-meta {
    font-size: 11.5px;
    color: var(--text-muted);
}

.schema-validator-mount {
    height: 460px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.schema-validator-results {
    display: flex;
    flex-direction: column;
    gap: 10px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
}

.schema-validator-results-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.schema-validator-results-header h3 {
    margin: 0;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.schema-validator-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.5;
    letter-spacing: 0.01em;
    border: 1px solid transparent;
}

.schema-validator-status--neutral {
    background-color: var(--accent-soft);
    color: var(--accent);
    border-color: rgba(99, 102, 241, 0.25);
}

.schema-validator-status--success {
    background-color: rgba(16, 185, 129, 0.12);
    color: #047857;
    border-color: rgba(16, 185, 129, 0.30);
}

.schema-validator-status--error {
    background-color: rgba(239, 68, 68, 0.12);
    color: #b91c1c;
    border-color: rgba(239, 68, 68, 0.30);
}

.schema-validator-empty {
    margin: 0;
    color: var(--text-muted);
    font-size: 13px;
    font-style: italic;
}

.schema-validator-result-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 320px;
    overflow-y: auto;
}

.schema-validator-result-line {
    padding: 6px 10px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12px;
    color: var(--text-primary);
    word-break: break-word;
    white-space: pre-wrap;
    line-height: 1.5;
}

/* Dark mode tweaks: keep the status pill colours readable. */
html.dark .schema-validator-status--success {
    color: #6ee7b7;
}

html.dark .schema-validator-status--error {
    color: #fca5a5;
}

@media (max-width: 1024px) {
    .schema-validator-grid {
        grid-template-columns: 1fr;
    }

    .schema-validator-actions {
        margin-left: 0;
    }
}

.types-detail {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 18px 22px;
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-width: 0;
}

.types-empty-pane {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 6px;
    padding: 48px 16px;
}

.types-empty-pane i {
    font-size: 36px;
    color: var(--text-muted);
    margin-bottom: 6px;
}

.types-empty-pane h3 {
    margin: 0;
    font-size: 15px;
    color: var(--text-primary);
}

.types-empty-pane p {
    margin: 0;
    color: var(--text-muted);
    font-size: 13px;
}

.types-detail-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.types-detail-title h3 {
    margin: 0 0 4px 0;
    font-size: 17px;
    font-weight: 600;
    color: var(--text-primary);
}

.types-detail-meta {
    font-size: 12px;
    color: var(--text-muted);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
}

.types-detail-meta .dot {
    margin: 0 4px;
    opacity: 0.6;
}

.types-detail-actions {
    display: flex;
    gap: 6px;
    align-items: center;
}

.types-detail-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.types-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 14px;
}

.types-form-grid .form-row--full {
    grid-column: 1 / -1;
}

.form-help-inline {
    margin-left: 8px;
    font-size: 11.5px;
    color: var(--text-muted);
    font-weight: 400;
}

.types-readout {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.types-readout-card {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    background-color: var(--bg-canvas);
}

.types-readout-card h4 {
    margin: 0 0 8px 0;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.types-readout-card-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 8px;
}

.types-readout-card-header h4 {
    margin: 0;
}

.types-readout-meta {
    font-size: 11.5px;
    color: var(--text-muted);
    font-feature-settings: 'tnum';
}

.types-description {
    margin: 0;
    color: var(--text-primary);
    white-space: pre-wrap;
    line-height: 1.55;
    font-size: 13px;
}

.types-empty-inline {
    margin: 0;
    color: var(--text-muted);
    font-style: italic;
    font-size: 13px;
}

/* Placeholder card used by the Lambdas / Registry tabs */
.placeholder-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 8px;
    padding: 48px 32px;
    background-color: var(--bg-surface);
    border: 1px dashed var(--border-subtle);
    border-radius: var(--radius-md);
    color: var(--text-secondary);
}

.placeholder-card .placeholder-icon {
    font-size: 36px;
    color: var(--text-muted);
    margin-bottom: 4px;
}

.placeholder-card h3 {
    margin: 0;
    font-size: 15px;
    color: var(--text-primary);
    font-weight: 600;
}

.placeholder-card p {
    margin: 0;
    font-size: 13px;
    line-height: 1.55;
    max-width: 540px;
}

.placeholder-card .placeholder-status {
    margin-top: 8px;
    color: var(--text-muted);
    font-size: 12px;
    font-style: italic;
}

@media (max-width: 960px) {
    .types-shell {
        grid-template-columns: 1fr;
    }

    .types-list-items {
        max-height: 280px;
    }

    .types-form-grid {
        grid-template-columns: 1fr;
    }
}

.page-toolbar .form-control {
    max-width: 340px;
}

.page-toolbar .btn-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.active-toplevel-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 4px;
    color: var(--text-secondary);
    font-size: 13px;
    user-select: none;
    cursor: pointer;
}
.active-toplevel-toggle .form-check-input {
    margin: 0;
}

.page-loading,
.page-count {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 12.5px;
    font-feature-settings: 'tnum';
}

.page-empty {
    padding: 28px 16px;
    text-align: center;
    color: var(--text-muted);
    background-color: var(--bg-surface);
    border: 1px dashed var(--border-subtle);
    border-radius: var(--radius-md);
    font-size: 13.5px;
}

/* ─── Data table — id / center / indicator-dot ────────────────── */

.data-table .cell-id {
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12px;
    color: var(--text-secondary);
    max-width: 18em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.data-table .cell-center {
    text-align: center;
}

.indicator-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    vertical-align: middle;
}

.indicator-dot-active {
    background-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.18);
}

.indicator-dot-idle {
    background-color: var(--border-emphasis);
}

/* Green "online" dot — used in RealmsTab's Details dialog to flag the
   Hades nodes that answered the ping. Same 8px shape as the other
   indicator-dot variants; pulls from --success so light/dark themes
   carry the right green automatically. */
.indicator-dot-online {
    background-color: var(--success);
    box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18);
}

/* Active-Hades node list inside the Details modal — strip the default
   bullet so the indicator-dot is the only marker, and lay rows out as
   "dot + identity" with a comfortable gap. */
.details-node-list {
    list-style: none;
    padding: 0;
    margin: 8px 0 0;
}

.details-node-list li {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 0;
}

/* Flux node realm — small label sat next to the IP code so an operator
   can see which realm each pod belongs to at a glance. Subdued type so
   the IP (the operationally relevant field) keeps visual priority. */
.flux-node-realm {
    display: inline-block;
    min-width: 90px;
    padding: 1px 8px;
    border-radius: var(--radius-sm, 4px);
    background-color: var(--bg-surface, rgba(99, 102, 241, 0.08));
    border: 1px solid var(--border-subtle, rgba(0, 0, 0, 0.08));
    font-size: 12px;
    font-weight: 500;
    color: var(--text-secondary, inherit);
    line-height: 1.4;
}

.flux-node-realm-empty {
    color: var(--text-muted, #888);
    font-style: italic;
}

/* ─── Bulk delete toolbar / search banner ─────────────────────── */

.bulk-toolbar {
    display: flex;
    gap: 10px;
    align-items: center;
    margin-bottom: 14px;
    padding: 8px 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}

.bulk-toolbar .bulk-label {
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-right: 2px;
}

.bulk-toolbar .form-control {
    max-width: none;
}

.bulk-toolbar .bulk-flow-select {
    flex: 1 1 220px;
    min-width: 180px;
}

.bulk-toolbar .bulk-date-range {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
}

.bulk-toolbar .bulk-date {
    flex: 0 0 auto;
    width: 175px;
    font-feature-settings: 'tnum';
}

.bulk-toolbar .bulk-sep {
    color: var(--text-muted);
    font-size: 13px;
}

.bulk-toolbar .bulk-delete-btn {
    flex: 0 0 auto;
}

.bulk-error {
    margin: -6px 0 14px 0;
    padding: 6px 12px;
    color: var(--danger);
    font-size: 12.5px;
    background-color: #fef2f2;
    border: 1px solid #fecaca;
    border-radius: var(--radius-md);
}

.search-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 14px;
    margin-bottom: 12px;
    background-color: var(--accent-soft);
    border: 1px solid #c7d2fe;
    border-radius: var(--radius-md);
    font-size: 13px;
    color: #312e81;
}

.search-banner i {
    color: var(--accent);
    font-size: 14px;
}

/* ─── Generic modal dialog (Show, Dependencies, …) ────────────── */

.modal-overlay {
    position: fixed;
    inset: 0;
    background-color: rgba(15, 23, 42, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 24px;
}

.modal-dialog {
    background-color: var(--bg-surface);
    border-radius: var(--radius-lg);
    box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.25);
    width: 100%;
    max-width: 680px;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    /* Bootstrap 5.3's .modal-dialog sets pointer-events: none (its design assumes a
       .modal-content child re-enables them). Sphere uses .modal-dialog directly, so
       re-enable here or the overlay swallows every click and closes the dialog. */
    pointer-events: auto;
    position: relative;
}

.modal-dialog--wide {
    max-width: 960px;
}

.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 22px;
    border-bottom: 1px solid var(--border-subtle);
    flex-shrink: 0;
}

.modal-header h3 {
    margin: 0;
    font-size: 14.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.modal-header .modal-subtitle {
    font-weight: 500;
    color: var(--text-secondary);
    margin-left: 4px;
}

.modal-close {
    background: transparent;
    border: none;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    transition: background-color 100ms ease, color 100ms ease;
}

.modal-close:hover {
    background-color: var(--bg-canvas);
    color: var(--text-primary);
}

.modal-body {
    padding: 18px 22px;
    overflow-y: auto;
}

.modal-hint {
    margin: 0 0 14px 0;
    font-size: 12.5px;
    color: var(--text-muted);
}

.policy-code {
    margin: 0;
    padding: 16px 18px;
    background-color: #0f172a;
    color: #e2e8f0;
    border-radius: var(--radius-md);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12.5px;
    line-height: 1.55;
    overflow: auto;
    max-height: calc(85vh - 140px);
    white-space: pre;
    tab-size: 4;
}

.policy-code code {
    color: inherit;
    background: transparent;
    font: inherit;
}

/* ─── Inline page banner (e.g. cache refresh result) ──────────── */

.page-banner {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.alert-close {
    background: transparent;
    border: none;
    color: inherit;
    opacity: 0.55;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    font-size: 12px;
    transition: opacity 100ms ease;
}

.alert-close:hover {
    opacity: 1;
}

/* ─── Confirm dialog ──────────────────────────────────────────── */

.confirm-overlay {
    position: fixed;
    inset: 0;
    background-color: rgba(15, 23, 42, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 24px;
}

.confirm-dialog {
    background-color: var(--bg-surface);
    border-radius: var(--radius-lg);
    box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.25);
    padding: 24px 28px 20px 28px;
    max-width: 440px;
    width: 100%;
    /* See note on .modal-dialog — same Bootstrap pointer-events conflict applies here. */
    pointer-events: auto;
    position: relative;
}

.confirm-dialog h3 {
    margin: 0 0 8px 0;
    font-size: 17px;
    font-weight: 600;
    color: var(--text-primary);
}

.confirm-dialog p {
    margin: 0 0 18px 0;
    color: var(--text-secondary);
    font-size: 13.5px;
    line-height: 1.55;
}

.confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 4px;
}

/* Extra detail paragraphs + lists used by the Endpoints "delete flow"
   confirm dialog when there are orphan/kept dataweaves to display. The
   default .confirm-dialog max-width (440px) is too tight when these are
   present; widen it via the modifier below. */
.confirm-dialog--wide {
    max-width: 580px;
}

.confirm-detail {
    margin: 0 0 6px 0 !important;
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.5;
}

.confirm-detail--muted {
    color: var(--text-muted);
    font-size: 12.5px;
}

.confirm-list {
    margin: 0 0 14px 0;
    padding-left: 20px;
    font-size: 12.5px;
    color: var(--text-primary);
    line-height: 1.7;
    max-height: 160px;
    overflow: auto;
}

.confirm-list--muted {
    color: var(--text-muted);
}

.confirm-list code {
    font-size: 11.5px;
}

/* ─── Page intro paragraph ────────────────────────────────────── */

.page-intro {
    margin: -2px 0 20px 0;
    color: var(--text-secondary);
    font-size: 13.5px;
    line-height: 1.55;
    max-width: 720px;
}

/* ─── App-card dashboard tiles (Architecture, future Apps page) ─ */

.app-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 16px;
    margin-top: 22px;
}

.app-card {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 20px 22px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    color: inherit;
    position: relative;
    transition:
        border-color 160ms ease,
        transform 160ms ease,
        box-shadow 160ms ease,
        background-color 160ms ease;
}

.app-card:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.app-card-icon {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    border-radius: var(--radius-md);
    background-color: var(--accent-soft);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    transition: background-color 160ms ease, color 160ms ease;
}

.app-card:hover .app-card-icon {
    background-color: var(--accent);
    color: #ffffff;
}

.app-card-content {
    flex: 1 1 auto;
    min-width: 0;
}

.app-card-category {
    display: block;
    margin-bottom: 4px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}

.app-card-title {
    margin: 0 0 6px 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
    letter-spacing: -0.01em;
}

.app-card-title::after {
    display: none;
}

.app-card-description {
    margin: 0;
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.5;
}

.app-card-action {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: 16px;
    transition: color 160ms ease, transform 160ms ease;
}

.app-card:hover .app-card-action {
    color: var(--accent);
    transform: translateX(3px);
}

/* ─── Inbox (Tasks) ───────────────────────────────────────────── */

.unread-pill {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 8px;
    background-color: var(--accent-soft);
    color: var(--accent);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
}

.inbox-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 6px;
}

.inbox-row {
    display: flex;
    gap: 10px;
    align-items: stretch;
    width: 100%;
    padding: 12px 14px;
    /* Recessed at rest — sits visibly inside the list surface (which is
       --bg-surface). The border defines each card so adjacent rows don't
       blur into a single column. */
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    text-align: left;
    cursor: pointer;
    font: inherit;
    color: inherit;
    transition: background-color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
}

.inbox-row:hover {
    /* Hover lifts the row: matches the list surface + subtle elevation +
       slightly stronger border, so the hovered row reads as raised rather
       than just tinted. */
    background-color: var(--bg-surface);
    border-color: var(--text-muted);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}

.inbox-row:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--accent);
}

.inbox-row-indicator {
    flex: 0 0 auto;
    width: 4px;
    border-radius: 2px;
    background-color: transparent;
    align-self: stretch;
}

.inbox-row--unread .inbox-row-indicator {
    background-color: var(--accent);
}

/* Unread row treatment composes three layers — already-present indicator
   strip, already-present bold subject (below), plus a subtle accent wash
   on the row background + an accent-tinted border. Keeps the design
   language quiet: no badges, no animations, no overlapping dots — the
   row reads as one deliberately-highlighted object. Uses the existing
   --accent-soft variable so both themes (light + dark) get a tonally
   correct wash without hardcoded rgba. */
.inbox-row--unread {
    background-color: var(--accent-soft);
    border-color: var(--accent);
}

/* Hovering an unread row would otherwise inherit the base
   .inbox-row:hover background (var(--bg-surface)) and wash out the
   accent tint. Re-assert the wash + accent border, and keep the same
   neutral lift-shadow the base hover uses so the elevation cue is
   consistent across read / unread states. */
.inbox-row--unread:hover {
    background-color: var(--accent-soft);
    border-color: var(--accent);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}

.inbox-row-main {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.inbox-row-top {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    font-size: 12.5px;
    color: var(--text-secondary);
}

.inbox-row-from {
    font-weight: 500;
    color: var(--text-primary);
}

.inbox-row-time {
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    flex-shrink: 0;
}

.inbox-row-subject {
    font-size: 14px;
    color: var(--text-primary);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.inbox-row--unread .inbox-row-subject {
    font-weight: 700;
}

.inbox-row-bottom {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    font-size: 12.5px;
    color: var(--text-muted);
}

.inbox-row-preview {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.inbox-row-flow {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text-secondary);
}

.inbox-row-flow i {
    font-size: 12px;
    color: var(--accent);
}

/* Detail view */

.inbox-detail {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.inbox-detail-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 16px;
    border-bottom: 1px solid var(--border-subtle);
    background-color: var(--bg-canvas);
}

.inbox-detail-source {
    font-size: 12px;
    color: var(--text-muted);
}

.inbox-detail-body {
    padding: 24px 28px;
}

.inbox-detail-subject {
    margin: 0 0 12px 0;
    font-size: 20px;
    font-weight: 600;
    color: var(--text-primary);
    letter-spacing: -0.01em;
}

.inbox-detail-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
    margin-bottom: 18px;
    font-size: 13px;
    color: var(--text-secondary);
}

.inbox-detail-meta-label {
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-right: 4px;
}

.inbox-detail-text {
    margin-bottom: 24px;
    padding: 16px 18px;
    background-color: var(--bg-canvas);
    border-left: 3px solid var(--border-emphasis);
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    color: var(--text-primary);
    white-space: pre-wrap;
    line-height: 1.55;
}

/* Schema form */

.schema-form {
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 20px 22px;
}

.schema-form-title {
    margin: 0 0 16px 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.schema-form-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 14px;
}

.schema-form-field label {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-secondary);
}

.schema-form-required {
    color: var(--danger);
    margin-left: 3px;
    font-weight: 600;
}

.schema-form-switch {
    align-self: flex-start;
}

.schema-form-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--border-subtle);
}

/* ─── Tabs as <button> elements (Home tabs are state-based, not URL-based) ── */

.tabs button.tab {
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--text-secondary);
    padding: 8px 14px;
    margin-bottom: -1px;
    transition: color 120ms ease, border-color 120ms ease;
}

.tabs button.tab:hover {
    color: var(--text-primary);
}

.tabs button.tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* ─── Home: friendly empty-state placeholders for stub tabs ──── */

.home-tab-empty {
    text-align: center;
    padding: 56px 24px;
    max-width: 560px;
    margin: 24px auto;
}

.home-tab-empty .home-tab-empty-icon {
    width: 56px;
    height: 56px;
    margin: 0 auto 18px auto;
    border-radius: 50%;
    background-color: var(--accent-soft);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
}

.home-tab-empty h3 {
    margin: 0 0 8px 0;
    font-size: 17px;
    font-weight: 600;
    color: var(--text-primary);
}

.home-tab-empty p {
    margin: 0;
    color: var(--text-secondary);
    font-size: 13.5px;
    line-height: 1.55;
}

/* ─── Home: User Info tab ─────────────────────────────────────── */

.user-info-tab {
    margin-top: 18px;
}

.user-info-card {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 20px 24px;
}

.user-info-header {
    display: flex;
    align-items: center;
    gap: 14px;
    padding-bottom: 16px;
    margin-bottom: 18px;
    border-bottom: 1px solid var(--border-subtle);
}

.user-info-avatar {
    width: 44px;
    height: 44px;
    font-size: 16px;
}

.user-info-identity h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
}

.user-info-meta {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--text-muted);
}

.user-info-claims h4 {
    margin: 0 0 12px 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* ─── Home: Console tab ───────────────────────────────────────── */

.console-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    margin: 18px 0 12px 0;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}

.console-toolbar-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    margin-right: 2px;
}

.console-toolbar .console-select {
    flex: 0 0 auto;
    max-width: none;
    width: 12em;
}

.console-channel {
    flex: 0 0 auto;
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12px;
    padding: 4px 10px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: 999px;
    color: var(--text-secondary);
}

.console-toolbar-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 6px;
}

.console-mode-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    color: var(--text-secondary);
    cursor: pointer;
    margin: 0 6px;
}

.console-mode-toggle input {
    margin: 0;
}

.console-status {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--text-secondary);
    margin-bottom: 8px;
}

.console-status code {
    font-size: 11.5px;
    color: var(--accent);
    background: transparent;
}

.console-output {
    background-color: #0b1220;
    color: #34d399;
    border: 1px solid #1e293b;
    border-radius: var(--radius-md);
    padding: 14px 16px;
    /* Grow the console until it nearly reaches the bottom of the viewport. The
       subtracted value approximates the chrome above (app header + page intro +
       tab strip + console toolbar + status row + alert banner) plus a small
       trailing gutter so the page doesn't scroll. */
    height: calc(100vh - 320px);
    min-height: 320px;
    overflow-y: auto;
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12px;
    line-height: 1.55;
}

.console-output--amber {
    color: #f59e0b;
}

/* Standalone pop-out at /webconsole — uses BlankLayout, so there's no Sphere
   chrome above to reserve viewport height for. Reclaim that height for the
   live output and add a comfortable gutter. */
.webconsole-shell {
    padding: 16px 20px;
    height: 100vh;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.webconsole-shell .console-output {
    /* Fit-to-viewport minus toolbar (~50px) + status row (~28px) + paddings. */
    height: calc(100vh - 140px);
    min-height: 240px;
    flex: 1 1 auto;
}

.console-output-empty {
    color: #64748b;
    font-style: italic;
}

.console-line {
    display: flex;
    gap: 10px;
    padding: 2px 0;
    border-bottom: 1px solid rgba(148, 163, 184, 0.06);
    white-space: pre-wrap;
    word-break: break-word;
}

.console-line:last-child {
    border-bottom: none;
}

.console-line-time {
    flex: 0 0 auto;
    color: rgba(148, 163, 184, 0.65);
    font-variant-numeric: tabular-nums;
}

.console-line-channel {
    flex: 0 0 auto;
    color: #818cf8;
    font-weight: 500;
}

.console-line-text {
    flex: 1 1 auto;
    color: inherit;
}

/* ─── Dark mode ────────────────────────────────────────────────────────────
   Activated when <html class="dark"> is set (App.razor head script handles this
   before first paint, gated by localStorage and a path check for /workflow-editor).
   Most components route through the variables below, so flipping them does the
   heavy lifting. A few hardcoded selectors (alerts, native form controls) need
   explicit overrides at the bottom of this block.
   ─────────────────────────────────────────────────────────────────────────── */

html.dark {
    --bg-canvas: #0b1220;
    --bg-surface: #131c2f;
    --bg-sidebar: #0a0f1c;
    --bg-sidebar-hover: rgba(255, 255, 255, 0.05);
    --bg-sidebar-active: rgba(99, 102, 241, 0.22);
    --border-subtle: #1e293b;
    --border-emphasis: #334155;
    --text-primary: #e2e8f0;
    --text-secondary: #94a3b8;
    --text-muted: #64748b;
    --text-on-dark: #cbd5e1;
    --text-on-dark-active: #ffffff;
    --accent: #818cf8;
    --accent-soft: rgba(129, 140, 248, 0.18);
    --success: #34d399;
    --danger: #f87171;
    --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.35);
    --shadow-md: 0 4px 12px -2px rgba(0, 0, 0, 0.45), 0 2px 4px -2px rgba(0, 0, 0, 0.30);
}

html.dark body,
html.dark .home-shell,
html.dark .auth-shell,
html.dark .manage-shell {
    color-scheme: dark;
}

/* Alerts — Sphere's light versions are pastel; dark versions are tinted-on-dark. */
html.dark .alert-success {
    background-color: rgba(16, 185, 129, 0.12);
    color: #6ee7b7;
    border-color: rgba(16, 185, 129, 0.30);
}
html.dark .alert-danger {
    background-color: rgba(239, 68, 68, 0.12);
    color: #fca5a5;
    border-color: rgba(239, 68, 68, 0.30);
}
html.dark .alert-warning {
    background-color: rgba(245, 158, 11, 0.12);
    color: #fcd34d;
    border-color: rgba(245, 158, 11, 0.30);
}
html.dark .alert-info {
    background-color: rgba(129, 140, 248, 0.12);
    color: #a5b4fc;
    border-color: rgba(129, 140, 248, 0.30);
}

/* Native form controls — make the browser pick dark chrome (scrollbars, datetime pickers). */
html.dark .form-control,
html.dark select.form-control,
html.dark input.form-control,
html.dark textarea.form-control {
    color-scheme: dark;
    background-color: var(--bg-canvas);
    color: var(--text-primary);
    border-color: var(--border-subtle);
}
html.dark .form-control:focus {
    background-color: var(--bg-surface);
}
html.dark .form-control::placeholder {
    color: var(--text-muted);
}

/* Data table row hover: the light-mode value sits on a near-white surface and
   reads gray; on dark it'd disappear into the surface, so use a pale highlight. */
html.dark .data-table tbody tr:hover {
    background-color: rgba(255, 255, 255, 0.03);
}

/* KPI chip suggestion list — its boxshadow needs more contrast on dark. */
html.dark .kpi-chip-suggestions {
    box-shadow: 0 8px 24px -6px rgba(0, 0, 0, 0.6);
}

/* Data-table cell text — `--text-muted` resolves to #64748b in dark which
   sits too close to the surface. Bump muted cells to --text-secondary so
   row meta (versions, timestamps, IDs in lists like Endpoints / Policies /
   Prompts / Active) is comfortably readable. */
html.dark .data-table .cell-muted {
    color: var(--text-secondary);
}

/* Notes header danger-soft (trash) — hardcoded #c0392b is too dark to
   read against --bg-surface. Use the dark-palette danger token, which is
   already calibrated for light-on-dark. */
html.dark .notes-header-actions .btn-danger-soft {
    color: var(--danger);
}
html.dark .notes-header-actions .btn-danger-soft:hover {
    background-color: rgba(248, 113, 113, 0.12);
    border-color: var(--danger);
}

/* Room chat — light-mode rules use raw #fff / var(--bs-body-bg,#fff) /
   var(--bs-tertiary-bg,#f8f9fa). None of those survive dark mode, which
   was the "entire chat interface remains white" report. Route every chat
   surface through Sphere's own --bg-* tokens here so the chat inherits
   the same hierarchy as the rest of the app. */
html.dark .room-chat-header {
    background: var(--bg-surface);
    border-bottom-color: var(--border-subtle);
}
html.dark .room-chat-messages {
    background: var(--bg-canvas);
}
html.dark .room-chat-bubble-inner {
    background: var(--bg-surface);
    color: var(--text-primary);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
html.dark .room-chat-bubble-inner a {
    color: var(--accent);
}
html.dark .room-chat-meta strong {
    color: var(--text-primary);
}
/* "is-mine" bubbles use --bs-primary (#0d6efd) on light; keep the accent
   shape but use Sphere's dark accent so it sits coherently with the rest
   of the UI rather than a flat blue patch. */
html.dark .room-chat-bubble.is-mine .room-chat-bubble-inner {
    background: var(--accent);
    color: #0b1220;
}
html.dark .room-chat-bubble.is-mine .room-chat-bubble-inner a {
    color: #0b1220;
}
html.dark .room-chat-daysep::before,
html.dark .room-chat-daysep::after {
    border-top-color: var(--border-subtle);
}
html.dark .room-chat-pending-image {
    background: var(--bg-surface);
    border-top-color: var(--border-subtle);
}
html.dark .room-chat-reply-chip {
    background: var(--bg-surface);
    border-top-color: var(--border-subtle);
    border-left-color: var(--accent);
}
html.dark .room-chat-reply-chip-icon,
html.dark .room-chat-quote-sender {
    color: var(--accent);
}
html.dark .room-chat-quote {
    border-left-color: var(--accent);
}
html.dark .room-chat-quote:hover {
    background: rgba(129, 140, 248, 0.10);
}
html.dark .room-chat-composer textarea,
html.dark .room-chat-composer input {
    background-color: var(--bg-canvas);
    color: var(--text-primary);
    border-color: var(--border-subtle);
}

/* ApexCharts SVG text — the library writes inline `fill="..."` on every
   axis tick / legend label / data label. The default fill is dark-on-light
   and there's no theme-aware re-render hook on Sphere's side. CSS wins
   over presentation attributes when we use a specific selector +
   !important, and the rule re-evaluates on theme toggle so the user
   doesn't have to reload the page. */
html.dark .apexcharts-text,
html.dark .apexcharts-xaxis-label,
html.dark .apexcharts-yaxis-label,
html.dark .apexcharts-xaxis-title,
html.dark .apexcharts-yaxis-title {
    fill: var(--text-secondary) !important;
}
html.dark .apexcharts-legend-text {
    color: var(--text-primary) !important;
}
html.dark .apexcharts-datalabel,
html.dark .apexcharts-pie-label {
    fill: var(--text-primary) !important;
}
html.dark .apexcharts-tooltip {
    background: var(--bg-surface) !important;
    color: var(--text-primary) !important;
    border-color: var(--border-subtle) !important;
}
html.dark .apexcharts-tooltip-title {
    background: var(--bg-canvas) !important;
    border-bottom-color: var(--border-subtle) !important;
}
html.dark .apexcharts-gridline {
    stroke: var(--border-subtle) !important;
}

/* Status pills (success / warning / error / neutral) reused across components:
   `.schema-validator-status--*`, `.flow-launcher-status--*`, `.dataweave-status--*`,
   etc. All variants share the same translucent tint backgrounds + a hard-coded
   dark text color that disappears in dark mode. Attribute selector targets every
   `-status--success / -status--warning / -status--error` class without enumerating
   each prefix. Mirrors the established dark-alert palette above. */
html.dark [class*="-status--success"] {
    color: #6ee7b7;
}
html.dark [class*="-status--warning"] {
    color: #fcd34d;
}
html.dark [class*="-status--error"] {
    color: #fca5a5;
}

/* BluePrinter activity icon colours — soft "Soph parity" hardcoded greens
   and grays that read OK on light but disappear on dark. Lift to the dark
   palette analogues. */
html.dark .bp-icon-human { color: #6ee7b7; }
html.dark .bp-icon-aeon  { color: var(--text-secondary); }

/* Theme toggle icon swap. Only one of the two <i> elements inside .theme-toggle
   shows at a time, driven by html.dark. */
.theme-icon-dark { display: none; }
html.dark .theme-icon-light { display: none; }
html.dark .theme-icon-dark { display: inline; }

/* ─── Flow Launcher page ─────────────────────────────────────────────────── */

.flow-launcher-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.flow-launcher-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 14px;
    padding: 14px 16px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}

.flow-launcher-toolbar-field {
    flex: 1 1 280px;
    min-width: 240px;
    margin-bottom: 0;
}

.flow-launcher-grid {
    display: grid;
    grid-template-columns: 1fr 1.2fr;
    gap: 14px;
}

.flow-launcher-panel {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    min-width: 0;
}

.flow-launcher-panel-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.flow-launcher-panel-header h3 {
    margin: 0;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.flow-launcher-panel-meta {
    font-size: 11.5px;
    color: var(--text-muted);
}

.flow-launcher-panel-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.flow-launcher-panel-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.flow-launcher-mount {
    height: 460px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.flow-launcher-launch-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 14px 18px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    flex-wrap: wrap;
}

.flow-launcher-launch-copy {
    color: var(--text-secondary);
    font-size: 13px;
    flex: 1;
    min-width: 240px;
}

.flow-launcher-url {
    background-color: var(--bg-canvas);
    padding: 2px 6px;
    border-radius: var(--radius-sm);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 11.5px;
    color: var(--text-primary);
    word-break: break-all;
}

.flow-launcher-launch-card .btn-lg {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.flow-launcher-output {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
}

.flow-launcher-output-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.flow-launcher-output-header h3 {
    margin: 0;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.flow-launcher-output-controls {
    display: flex;
    align-items: center;
    gap: 10px;
}

.flow-launcher-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    color: var(--text-secondary);
    cursor: pointer;
}

.flow-launcher-toggle input[type="checkbox"] {
    margin: 0;
    cursor: pointer;
}

.flow-launcher-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.5;
    letter-spacing: 0.01em;
    border: 1px solid transparent;
    white-space: nowrap;
}

.flow-launcher-status--neutral {
    background-color: var(--accent-soft);
    color: var(--accent);
    border-color: rgba(99, 102, 241, 0.25);
}

.flow-launcher-status--success {
    background-color: rgba(16, 185, 129, 0.12);
    color: #047857;
    border-color: rgba(16, 185, 129, 0.30);
}

.flow-launcher-status--warning {
    background-color: rgba(245, 158, 11, 0.12);
    color: #92400e;
    border-color: rgba(245, 158, 11, 0.30);
}

.flow-launcher-status--error {
    background-color: rgba(239, 68, 68, 0.12);
    color: #b91c1c;
    border-color: rgba(239, 68, 68, 0.30);
}

html.dark .flow-launcher-status--success { color: #6ee7b7; }
html.dark .flow-launcher-status--warning { color: #fcd34d; }
html.dark .flow-launcher-status--error   { color: #fca5a5; }

@media (max-width: 1024px) {
    .flow-launcher-grid {
        grid-template-columns: 1fr;
    }
}

/* ─── Dataweave Editor ───────────────────────────────────────────────────── */

/* Same master-detail shell as the Policy Editor, but the main pane fits both
   the Monaco editor and the test-runner panel below it — so the shell goes
   auto-height (vs the Policy Editor's locked 100vh-ish) and the page scrolls. */
.dataweave-editor-shell {
    height: auto;
    min-height: 0;
}

.dataweave-editor-shell .policy-editor-list {
    /* Sticky list rail so it stays visible while the right pane scrolls. */
    position: sticky;
    top: 0;
    max-height: calc(100vh - var(--topbar-height) - 64px);
    align-self: flex-start;
}

.dataweave-editor-shell .policy-editor-main {
    overflow: visible;
}

.dataweave-editor-fields {
    grid-template-columns: 1fr;
}

.dataweave-editor-mount {
    /* Override the Policy Editor's flex:1 so the editor + test panel coexist. */
    flex: none;
    height: 460px;
    min-height: 320px;
}

.dataweave-test-panel {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px 16px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}

.dataweave-test-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.dataweave-test-header h3 {
    margin: 0;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.dataweave-test-subtitle {
    display: block;
    font-size: 11.5px;
    color: var(--text-muted);
    margin-top: 2px;
}

.dataweave-test-subtitle code {
    background-color: var(--bg-surface);
    padding: 1px 6px;
    border-radius: var(--radius-sm);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    color: var(--text-primary);
}

.dataweave-test-controls {
    display: flex;
    align-items: center;
    gap: 10px;
}

.dataweave-test-controls .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.dataweave-test-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
}

.dataweave-test-grid .form-row {
    margin-bottom: 0;
    min-width: 0;
}

.dataweave-test-textarea {
    resize: vertical;
    min-height: 180px;
}

.dataweave-test-actions {
    display: flex;
    justify-content: flex-start;
}

.dataweave-test-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.dataweave-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.5;
    letter-spacing: 0.01em;
    border: 1px solid transparent;
    white-space: nowrap;
}

.dataweave-status--neutral {
    background-color: var(--accent-soft);
    color: var(--accent);
    border-color: rgba(99, 102, 241, 0.25);
}

.dataweave-status--success {
    background-color: rgba(16, 185, 129, 0.12);
    color: #047857;
    border-color: rgba(16, 185, 129, 0.30);
}

.dataweave-status--warning {
    background-color: rgba(245, 158, 11, 0.12);
    color: #92400e;
    border-color: rgba(245, 158, 11, 0.30);
}

.dataweave-status--error {
    background-color: rgba(239, 68, 68, 0.12);
    color: #b91c1c;
    border-color: rgba(239, 68, 68, 0.30);
}

html.dark .dataweave-status--success { color: #6ee7b7; }
html.dark .dataweave-status--warning { color: #fcd34d; }
html.dark .dataweave-status--error   { color: #fca5a5; }

@media (max-width: 1024px) {
    .dataweave-test-grid {
        grid-template-columns: 1fr;
    }
}

/* ─── Types reference dialog (Dataweave editor) ──────────────────────────── */

/* Two-pane layout: scrollable name list on the left, definition preview on
   the right. Sized to fill the .modal-dialog--wide max-width minus padding,
   capped against the dialog's max-height so the inner Monaco mount always
   has a deterministic height instead of stretching the modal off-screen. */
.types-dialog-grid {
    display: grid;
    grid-template-columns: 280px 1fr;
    gap: 14px;
    min-height: 460px;
    height: 60vh;
}

.types-dialog-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: 10px;
    overflow: hidden;
    min-height: 0;
}

.types-dialog-list .policy-list {
    overflow-y: auto;
    flex: 1;
    min-height: 0;
    margin: 0;
}

.types-dialog-detail {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-width: 0;
    min-height: 0;
}

.types-dialog-detail-header h4 {
    margin: 0 0 4px 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.types-dialog-desc {
    margin: 0 0 6px 0;
    font-size: 12.5px;
    color: var(--text-secondary);
}

.types-dialog-labels {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.types-dialog-label {
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 9999px;
    background-color: var(--accent-soft);
    color: var(--accent);
}

.types-dialog-preview {
    flex: 1;
    min-height: 240px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.types-dialog-empty {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    color: var(--text-muted);
    text-align: center;
}

.types-dialog-empty i {
    font-size: 28px;
}

@media (max-width: 860px) {
    .types-dialog-grid {
        grid-template-columns: 1fr;
        height: auto;
    }
    .types-dialog-list {
        max-height: 240px;
    }
}

/* ─── Forms Designer ─────────────────────────────────────────────────────── */

.forms-designer-fields {
    grid-template-columns: 2fr 1fr 1fr;
}

.forms-designer-tabs {
    margin: 0 0 12px 0;
}

/* Schema / Form editors are always in the DOM so Monaco doesn't remount on tab
   switches; we just hide the non-active pane via display:none. */
.forms-designer-pane { display: none; }
.forms-designer-pane.is-visible { display: block; }

.forms-designer-preview {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
}

.forms-designer-preview-form,
.forms-designer-preview-model {
    display: flex;
    flex-direction: column;
    gap: 10px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    min-width: 0;
}

.forms-designer-preview-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}
.forms-designer-preview-header h3 {
    margin: 0;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}
.forms-designer-preview-meta {
    font-size: 11.5px;
    color: var(--text-muted);
}
.forms-designer-preview-mount { min-height: 320px; }

.forms-designer-preview-model-controls {
    display: flex;
    align-items: center;
    gap: 10px;
}
.forms-designer-preview-model-toggle {
    padding: 2px 6px;
    font-size: 12px;
    line-height: 1;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

/* Editable variant — same monospace dark slab as .policy-code so toggling
   between view/edit doesn't shift the layout, but rendered as a textarea so
   the user can actually type into the model. */
textarea.forms-designer-preview-model-edit {
    width: 100%;
    min-height: 320px;
    max-height: calc(85vh - 140px);
    border: none;
    resize: vertical;
    white-space: pre;
    overflow: auto;
}
textarea.forms-designer-preview-model-edit:focus {
    outline: 2px solid var(--accent, #3b82f6);
    outline-offset: -2px;
}

/* Vanilla preview renderer classes (emitted by js/forms-preview.js). */
.forms-preview-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.forms-preview-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.forms-preview-label {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-secondary);
}
.forms-preview-label--inline {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.forms-preview-checkbox { margin-right: 4px; }
.forms-preview-input { font-family: inherit; font-size: 13px; }
.forms-preview-help { font-size: 11.5px; color: var(--text-muted); }
.forms-preview-fieldset {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: 10px 14px;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.forms-preview-legend {
    padding: 0 6px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Author-supplied `style` on a uischema entry becomes one or more CSS classes
   on the field's wrapper. The set below covers the common "layout" cases the
   Forms Designer is likely to emit; bespoke values pass through verbatim so
   app-level CSS (or Bootstrap utilities) can extend it. */
.forms-preview-fieldset.row,
.forms-preview-fieldset.forms-preview-row {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-end;
}
.forms-preview-fieldset.row > .forms-preview-field,
.forms-preview-fieldset.forms-preview-row > .forms-preview-field {
    flex: 1 1 200px;
}
.forms-preview-field.inline {
    flex-direction: row;
    align-items: center;
    gap: 10px;
}
.forms-preview-field.inline > .forms-preview-label { flex: 0 0 auto; }
.forms-preview-field.inline > .forms-preview-input { flex: 1 1 auto; }
.forms-preview-field.half  { flex-basis: calc(50% - 12px); }
.forms-preview-field.third { flex-basis: calc(33.333% - 12px); }
.forms-preview-field.quarter { flex-basis: calc(25% - 12px); }

/* Read-only field treatment. Applied by the renderer when the uischema entry
   carries `readonly: true`. Controls inside are also `disabled` (set by
   lockdownReadonly in forms-preview.js); the CSS adds a visual hint so the
   author can spot read-only fields at a glance even when the input itself
   doesn't change shape much (e.g. a select). */
.forms-preview-field.is-readonly > .forms-preview-label {
    color: var(--text-muted);
}
.forms-preview-field.is-readonly > .forms-preview-label::after {
    content: ' · read-only';
    font-style: italic;
    font-size: 0.85em;
    opacity: 0.7;
    font-weight: normal;
}

/* Inline validation marker — applied by SphereFormsPreview.validate when a
   field fails its schema constraint. Red ring on whatever input element
   the field hosts (text/select/textarea), plus a small error message
   `<p class="forms-preview-field-error">` appended to the wrapper. */
.forms-preview-field.is-invalid > .forms-preview-input,
.forms-preview-field.is-invalid > select.forms-preview-input,
.forms-preview-field.is-invalid > textarea.forms-preview-input,
.forms-preview-field.is-invalid input.forms-preview-input,
.forms-preview-field.is-invalid select.forms-preview-input,
.forms-preview-field.is-invalid textarea.forms-preview-input {
    border-color: var(--danger, #ef4444);
    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.12);
}
.forms-preview-field.is-invalid > .forms-preview-label {
    color: var(--danger, #ef4444);
}
.forms-preview-field-error {
    color: var(--danger, #ef4444);
    font-size: 0.85em;
    margin: 4px 0 0 0;
}

.forms-preview-field--missing {
    border: 1px dashed var(--accent-warn, #d98c2b);
    border-radius: var(--radius-sm);
    padding: 6px 10px;
    background: rgba(217, 140, 43, 0.06);
}
.forms-preview-field--missing .forms-preview-help { color: var(--accent-warn, #d98c2b); }

/* Widget-specific styling: each block targets the structures emitted by the
   builders in forms-preview.js. Selectors are scoped under .forms-preview-*
   classes so this can't bleed into the rest of the app. */

/* Image widget (string with type: "image" on the form-layout entry).
   Display-only; the entry's `style` classes land directly on the <img>, so
   `style: "w-50"` etc. size it. Default keeps it contained. */
.forms-preview-image {
    display: block;
    max-width: 100%;
    height: auto;
    border-radius: var(--radius-sm);
}

/* Template widget (string with type: "template" on the form-layout entry).
   Author-supplied HTML rendered via innerHTML — wrapper exists only so the
   entry's `style` classes have somewhere to land. No intrinsic styling. */
.forms-preview-template {
    display: block;
}

/* Array-of-images widgets (string[] arrays with type: "images_list" or
   "images_table" on the form-layout entry). Both render display-only with
   uniform square tiles so mixed-resolution / sparse models still present
   cleanly. Authors size the outer wrapper via the entry's `style` classes. */
.forms-preview-images-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.forms-preview-images-table {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 0.5rem;
}

.forms-preview-images-item {
    object-fit: cover;
    border-radius: var(--radius-sm);
    background: var(--surface-muted, #f3f3f3);
}

.forms-preview-images-list > .forms-preview-images-item {
    width: 200px;
    aspect-ratio: 1 / 1;
}

.forms-preview-images-table > .forms-preview-images-item {
    width: 100%;
    aspect-ratio: 1 / 1;
}

/* Object-array iteration via `subForms`. The outer wrapper stacks each item
   block below the previous; each per-item <fieldset> groups the array
   element's rendered child entries. Authors size/space the outer wrapper via
   the entry's `style` classes. */
.forms-preview-items {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.forms-preview-items-title {
    font-size: 1rem;
    margin: 0;
}

.forms-preview-item {
    border: 1px solid var(--border-muted, #e5e7eb);
    border-radius: var(--radius-sm);
    padding: 0.75rem 1rem;
}

.forms-preview-item-legend {
    padding: 0 0.4rem;
    font-size: 0.875rem;
    color: var(--text-muted, #6b7280);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
}

/* Container that holds the item cards for a subForms array. Sits between the
   title/description block and the + Add button; gets rebuilt in place on add/
   remove without re-mounting the surrounding wrap. */
.forms-preview-items-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* + Add button at the bottom of a subForms list. Always visible — even when
   the array is empty, this is how the user adds the first item. */
.forms-preview-add-item {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.75rem;
    font-size: 0.875rem;
    border: 1px dashed var(--border-muted, #e5e7eb);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text, #1a1d24);
    text-decoration: none;
}
.forms-preview-add-item:hover {
    background: var(--bg-muted, #f3f4f6);
    border-style: solid;
}

/* Per-item × remove button — sits in the legend row, right-aligned. */
.forms-preview-remove-item {
    padding: 0.1rem 0.35rem;
    font-size: 0.75rem;
    line-height: 1;
    border: none;
    background: transparent;
    color: var(--text-muted, #6b7280);
    cursor: pointer;
}
.forms-preview-remove-item:hover {
    color: var(--danger, #dc2626);
}

/* Toggleable wrapper. The entry-level `toggleable: true` flag (any property
   type) wraps the field in a switch row + a content area that's hidden when
   the switch is off. Soph parity: model value is preserved across toggling. */
.forms-preview-toggleable {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.forms-preview-toggleable-row {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    font-size: 0.875rem;
    color: var(--text-secondary, var(--text-primary));
}

.forms-preview-toggleable-text {
    user-select: none;
}

.forms-preview-toggleable-content {
    display: block;
}

/* Monospace text inputs — markdown / code / html / json textareas. */
.forms-preview-input--mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    line-height: 1.45;
}

/* Radio list (string enum, type: "radio"). */
.forms-preview-radio-group {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.forms-preview-radio-row {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    cursor: pointer;
}

/* Range slider (number, type: "range"). */
.forms-preview-range {
    display: flex;
    align-items: center;
    gap: 10px;
}
.forms-preview-range-input { flex: 1 1 auto; }
.forms-preview-range-value {
    min-width: 3em;
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary);
    font-size: 12.5px;
}

/* Toggle switch (boolean, type: "toggle"). Pure CSS — the underlying control
   is still a checkbox so form-handling stays uniform. */
.forms-preview-switch {
    position: relative;
    display: inline-block;
    width: 36px;
    height: 20px;
    flex: 0 0 auto;
}
.forms-preview-switch input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
    z-index: 1;
}
.forms-preview-switch-track {
    position: absolute;
    inset: 0;
    background: var(--border-strong, #cbd5e1);
    border-radius: 999px;
    transition: background 0.15s ease;
}
.forms-preview-switch-thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    background: #ffffff;
    border-radius: 999px;
    transition: transform 0.15s ease;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.forms-preview-switch input[type="checkbox"]:checked ~ .forms-preview-switch-track {
    background: var(--accent, #3b82f6);
}
.forms-preview-switch input[type="checkbox"]:checked ~ .forms-preview-switch-track .forms-preview-switch-thumb {
    transform: translateX(16px);
}

/* Tags input (array, type: "tags"). */
.forms-preview-tags {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.forms-preview-tag-list {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    min-height: 0;
}
.forms-preview-tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 4px 2px 8px;
    background: var(--bg-canvas, #f1f5f9);
    border: 1px solid var(--border-subtle);
    border-radius: 999px;
    font-size: 12px;
}
.forms-preview-tag-remove {
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-size: 14px;
    line-height: 1;
    padding: 0 4px;
    cursor: pointer;
}
.forms-preview-tag-remove:hover { color: var(--accent-danger, #dc2626); }
.forms-preview-tag-input { font-size: 13px; }

/* Primitive-item array widget (default for string[] / integer[] / number[]
   / boolean[] / enum[]). Stacked list of values, each with a remove
   button; a single input + "Add" button below. Replaces the legacy
   "raw JSON textarea" default — the textarea is now reached only for
   array shapes whose items aren't a primitive (or have no `items` at
   all). */
.forms-preview-primarr {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.forms-preview-primarr-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.forms-preview-primarr-empty {
    color: var(--text-muted);
    font-size: 12px;
    font-style: italic;
    padding: 4px 0;
}
.forms-preview-primarr-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 6px 4px 10px;
    background: var(--bg-canvas, #f1f5f9);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    font-size: 13px;
}
.forms-preview-primarr-value {
    flex: 1 1 auto;
    word-break: break-word;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.forms-preview-primarr-remove {
    flex: 0 0 auto;
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-size: 16px;
    line-height: 1;
    padding: 0 4px;
    cursor: pointer;
}
.forms-preview-primarr-remove:hover { color: var(--accent-danger, #dc2626); }
.forms-preview-primarr-add-row {
    display: flex;
    gap: 6px;
    align-items: stretch;
}
.forms-preview-primarr-add-row > .forms-preview-input {
    flex: 1 1 auto;
}
.forms-preview-primarr-add {
    flex: 0 0 auto;
    padding: 4px 12px;
    border: 1px solid var(--accent);
    background: var(--accent-soft, #eef2ff);
    color: var(--accent);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 13px;
    font-weight: 500;
}
.forms-preview-primarr-add:hover {
    background: var(--accent);
    color: white;
}

/* Google Maps widget placeholder (string, type: "googlemaps"). */
.forms-preview-googlemaps {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* Map widget wrap — positions the refresh button as a top-right overlay
   on the map area. Used by the OSM/Leaflet `map` widget in forms-preview.js
   so the user can re-run geocoding after editing the paired text input
   (typical shape in BluePrinter Startup + task inbox forms). */
.forms-preview-map-wrap {
    position: relative;
    width: 100%;
}
.forms-preview-map-refresh {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 401;            /* above Leaflet panes (which top out at 400) */
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--border-emphasis);
    background: white;
    color: var(--text-secondary);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.forms-preview-map-refresh:hover {
    color: var(--accent);
    border-color: var(--accent);
}
.forms-preview-map-refresh:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* ============================================================
   SearchableSelect (Sphere/Components/Shared/SearchableSelect.razor)
   Generic single-select with substring filter. Reuses .form-control
   tokens for the trigger so callers can drop it in where a <select>
   sat without changing surrounding layout.
   ============================================================ */
.sphere-searchable-select {
    position: relative;
    display: block;
    width: 100%;
}
.sphere-searchable-select-trigger {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 34px;
    padding: 4px 8px 4px 10px;
    background: var(--bg-elevated, #ffffff);
    border: 1px solid var(--border-strong, #cbd5e1);
    border-radius: var(--radius-sm);
    color: var(--text-primary, inherit);
    font-size: 13px;
    text-align: left;
    cursor: pointer;
    gap: 8px;
}
.sphere-searchable-select.is-open .sphere-searchable-select-trigger {
    border-color: var(--accent, #3b82f6);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}
.sphere-searchable-select.is-disabled .sphere-searchable-select-trigger {
    opacity: 0.55;
    cursor: not-allowed;
}
.sphere-searchable-select-value {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.sphere-searchable-select-value-main { overflow: hidden; text-overflow: ellipsis; }
.sphere-searchable-select-value-sub {
    font-size: 11px;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
}
.sphere-searchable-select-placeholder {
    flex: 1 1 auto;
    color: var(--text-muted, #94a3b8);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sphere-searchable-select-actions {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;
}
.sphere-searchable-select-clear {
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 0 4px;
    font-size: 14px;
    line-height: 1;
    border-radius: 4px;
}
.sphere-searchable-select-clear:hover {
    background: rgba(0, 0, 0, 0.06);
    color: var(--text-primary);
}
.sphere-searchable-select-chevron {
    color: var(--text-muted);
    font-size: 13px;
    transition: transform 0.15s ease;
}
.sphere-searchable-select.is-open .sphere-searchable-select-chevron {
    transform: rotate(180deg);
}

.sphere-searchable-select-backdrop {
    position: fixed;
    inset: 0;
    background: transparent;
    z-index: 1000;
}
.sphere-searchable-select-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 1001;
    background: var(--bg-elevated, #ffffff);
    border: 1px solid var(--border-strong, #cbd5e1);
    border-radius: var(--radius-sm);
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
    display: flex;
    flex-direction: column;
    max-height: 320px;
    overflow: hidden;
}
.sphere-searchable-select-search {
    margin: 6px 6px 4px 6px;
    width: calc(100% - 12px);
    font-size: 13px;
}
.sphere-searchable-select-list {
    list-style: none;
    margin: 0;
    padding: 2px 0 4px 0;
    overflow-y: auto;
    flex: 1 1 auto;
}
.sphere-searchable-select-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    cursor: pointer;
    font-size: 13px;
    color: var(--text-primary, inherit);
}
.sphere-searchable-select-item:hover {
    background: var(--bg-canvas, #f1f5f9);
}
.sphere-searchable-select-item.is-selected {
    background: rgba(59, 130, 246, 0.10);
    color: var(--accent, #3b82f6);
}
.sphere-searchable-select-item-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sphere-searchable-select-item-sub {
    flex: 0 0 auto;
    font-size: 11px;
    color: var(--text-muted);
}
.sphere-searchable-select-item-tick {
    flex: 0 0 auto;
    color: var(--accent, #3b82f6);
}
.sphere-searchable-select-empty {
    padding: 10px 12px;
    color: var(--text-muted);
    font-size: 12.5px;
    text-align: center;
}
.forms-preview-empty {
    padding: 28px 16px;
    text-align: center;
    color: var(--text-muted);
    background-color: var(--bg-canvas);
    border: 1px dashed var(--border-subtle);
    border-radius: var(--radius-sm);
    font-size: 13px;
    line-height: 1.5;
}

.forms-designer-deploy {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 18px 22px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.forms-designer-deploy-header h3 {
    margin: 0 0 4px 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}
.forms-designer-deploy-meta {
    display: block;
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 2px;
}
.forms-designer-deploy-meta code {
    background-color: var(--bg-canvas);
    padding: 1px 6px;
    border-radius: var(--radius-sm);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 11.5px;
    color: var(--text-primary);
}
.forms-designer-deploy-actions {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}
.forms-designer-deploy-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.forms-designer-deploy-hint {
    color: var(--text-muted);
    font-size: 12.5px;
    font-style: italic;
}

@media (max-width: 1024px) {
    .forms-designer-fields { grid-template-columns: 1fr; }
    .forms-designer-preview { grid-template-columns: 1fr; }
}

/* Schema-tab toolbar above the Monaco mount — hosts the "Add property" trigger. */
.forms-designer-schema-toolbar {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 10px;
    flex-wrap: wrap;
}

.forms-designer-schema-toolbar .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* Schema-tab "Load from type" row — label + SearchableSelect + Load button +
   inline hint. The SearchableSelect needs a bounded width or it consumes the
   whole row; flex on the .typesel wrapper lets it size proportionally. */
.forms-designer-schema-toolbar-label {
    font-size: 0.875rem;
    color: var(--text-muted, #6b7280);
    white-space: nowrap;
}
.forms-designer-schema-toolbar-typesel {
    flex: 0 1 280px;
    min-width: 200px;
}

.forms-designer-schema-toolbar-hint {
    color: var(--text-muted);
    font-size: 12px;
    line-height: 1.4;
    flex: 1;
    min-width: 240px;
}

.forms-designer-schema-toolbar-hint code {
    background-color: var(--bg-canvas);
    padding: 1px 6px;
    border-radius: var(--radius-sm);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 11.5px;
    color: var(--text-primary);
}

/* Help dialog (Widget types reference). Compact two-column tables, grouped by
   the underlying JSON Schema type. */
.forms-designer-help-btn {
    flex: 0 0 auto;
}
.forms-designer-help-group {
    margin: 16px 0 6px;
    font-size: 11.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-secondary);
}
.forms-designer-help-group:first-of-type { margin-top: 8px; }
.forms-designer-help-group code {
    background-color: var(--bg-canvas);
    padding: 0 4px;
    border-radius: var(--radius-sm);
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 11px;
    text-transform: none;
    letter-spacing: 0;
}
.forms-designer-help-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12.5px;
    line-height: 1.45;
}
.forms-designer-help-table th,
.forms-designer-help-table td {
    text-align: left;
    vertical-align: top;
    padding: 4px 8px;
    border-bottom: 1px solid var(--border-subtle);
}
.forms-designer-help-table th {
    font-weight: 500;
    width: 14em;
    color: var(--text-secondary);
}
.forms-designer-help-table th code {
    font-family: ui-monospace, 'SF Mono', 'Menlo', 'Consolas', monospace;
    font-size: 12px;
    color: var(--text-primary);
}
.forms-designer-help-table tr:last-child th,
.forms-designer-help-table tr:last-child td {
    border-bottom: none;
}
.forms-designer-help-foot {
    margin-top: 16px;
    padding-top: 10px;
    border-top: 1px solid var(--border-subtle);
}

/* Property type picker grid inside the Add Property modal. Three columns on the
   wide modal; collapses to two on narrow screens. */
.property-type-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
}

.property-type-btn {
    appearance: none;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: 12px 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    color: var(--text-secondary);
    font-size: 12px;
    font-weight: 500;
    font-family: inherit;
    transition: background-color 100ms ease, border-color 100ms ease, color 100ms ease;
}

.property-type-btn:hover {
    background-color: var(--bg-surface);
    color: var(--text-primary);
}

.property-type-btn.is-active {
    background-color: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

.property-type-btn i {
    font-size: 18px;
}

.forms-builder-inline-check {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-primary);
    cursor: pointer;
    user-select: none;
}

.forms-builder-inline-check input[type="checkbox"] {
    margin: 0;
}

@media (max-width: 720px) {
    .property-type-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ─── BluePrinter ─────────────────────────────────────────────────────────── */

.blueprinter-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* Page-level blueprint switcher. Lives above .blueprinter-header so the
   user can hop between saved blueprints from any tab. Capped at a sane
   width so it doesn't span the whole monitor on a wide screen — feels
   like an inline switcher rather than a full toolbar. */
.blueprinter-loadbar {
    display: flex;
    align-items: stretch;
    gap: 8px;
    max-width: 560px;
}
.blueprinter-loadbar > .sphere-searchable-select {
    flex: 1 1 auto;
    min-width: 0;
}
.blueprinter-loadbar > .btn {
    flex: 0 0 auto;
}

.blueprinter-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
}

.blueprinter-name-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 280px;
}

.blueprinter-name-label {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.blueprinter-name-input {
    max-width: 460px;
    font-weight: 500;
}

.blueprinter-dirty {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    font-size: 12px;
    color: var(--accent);
    font-weight: 500;
}

.blueprinter-dirty i {
    font-size: 22px;
    line-height: 0;
}

.blueprinter-header-actions {
    display: flex;
    gap: 8px;
}

/* Tab count badges */
.tab-button .tab-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 600;
}

.tab-button.is-active .tab-count {
    background-color: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

/* Generic icon-only button used by row actions in the BluePrinter view. */
.btn-icon {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background-color 100ms ease, color 100ms ease, border-color 100ms ease;
}

.btn-icon:hover:not(:disabled) {
    background-color: var(--bg-surface);
    color: var(--text-primary);
    border-color: var(--border-subtle);
}

.btn-icon:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.btn-icon-danger:hover:not(:disabled) {
    color: var(--danger);
}

/* Common section block — light card, padded, used across most tabs. */
.bp-section {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.bp-section-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.bp-section-hint {
    color: var(--text-muted);
    font-size: 13px;
    margin: 0;
}

/* Inline add row: input(s) on the left, primary button on the right. */
.bp-inline-add {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.bp-inline-add .form-control {
    min-width: 200px;
    flex: 0 1 auto;
}

/* Activity list */
.bp-activity-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.bp-activity-card {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    transition: border-color 100ms ease, box-shadow 100ms ease;
}

.bp-activity-card.is-expanded {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.bp-activity-summary {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
}

.bp-activity-seq {
    font-size: 12px;
    color: var(--text-muted);
    font-weight: 600;
    min-width: 28px;
    text-align: right;
    font-feature-settings: 'tnum';
}

.bp-activity-toggle {
    appearance: none;
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
    flex: 1 1 auto;
    padding: 4px 6px;
    border-radius: var(--radius-sm);
    display: flex;
    flex-direction: column;
    gap: 4px;
    color: var(--text-primary);
    font-family: inherit;
}

.bp-activity-toggle:hover {
    background-color: rgba(99, 102, 241, 0.05);
}

.bp-activity-name {
    font-weight: 500;
    font-size: 14px;
}

/* Enum values editor — used in two places:
   1. Below the Variables-tab inline-add row when newVarType == "Enum"
      (collecting values before the new variable is added).
   2. Expanded inside the variables <table> as a colspan=3 row when an
      existing Enum variable's pencil-edit button is toggled.
   The shared shell keeps the visual identical in both contexts. */
.bp-enum-editor {
    margin: 6px 0 14px;
    padding: 12px 14px;
    background: var(--bs-tertiary-bg, #f6f7f9);
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 8px;
}
.bp-enum-editor-label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    margin-bottom: 8px;
    color: var(--bs-secondary-color, #6c757d);
    text-transform: uppercase;
    letter-spacing: 0.4px;
}
.bp-enum-editor .bp-token-list {
    margin-bottom: 8px;
}
.bp-enum-edit-row > td {
    background: var(--bs-tertiary-bg, #f6f7f9);
    padding: 6px 8px 12px;
}

/* Activity-level Variables sub-section inside an expanded activity card.
   Sits below the Description editor; renders the currently-attached
   variables as removable pill-tokens, and a picker + Attach button
   under that. Disabled state covers "no blueprint-level variables
   defined yet" — the user is steered to the Variables tab. */
.bp-activity-vars {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px dashed var(--bs-border-color, #dee2e6);
}
.bp-activity-vars-header {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.95rem;
    font-weight: 600;
    margin: 0 0 10px;
    color: var(--bs-body-color, #1d1d1f);
}
.bp-section-hint--inline {
    margin: 4px 0 10px;
    font-size: 0.85rem;
    opacity: 0.7;
}

/* Form preview that lives under the variable-tags row inside an expanded
   Activity card. Renders what the recipient will see at run-time, using
   the same BluePrintFormBuilder + SphereFormsPreview path as the Startup
   modal. Detach a tag above → ReconcileActivityFormsAsync re-mounts this
   without the removed field. */
.bp-activity-form-preview {
    margin-top: 18px;
    padding: 12px 14px 14px;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: var(--radius-md, 8px);
    background: var(--bs-tertiary-bg, #f6f7f9);
}
.bp-activity-form-preview-header {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.85rem;
    font-weight: 600;
    margin: 0 0 10px;
    color: var(--text-secondary, #475569);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}
.bp-activity-form-preview-header > i {
    font-size: 0.95rem;
    opacity: 0.7;
}
.bp-activity-form-host {
    min-height: 60px;
}

/* Activity status icons — Soph parity. Each conditional flag (human/aeon,
   notify, review, has variables) shows as a small Bootstrap-icon glyph
   in front of the activity's pill row. Colour palette ports Soph's
   Radzen/Material icon colours one-to-one for visual continuity. */
.bp-activity-icons {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 8px;
    font-size: 0.95rem;
    flex-shrink: 0;
}
.bp-icon { line-height: 1; }
.bp-icon-human  { color: #1f8a3a; }   /* darkgreen — Soph parity */
.bp-icon-aeon   { color: #6b7280; }   /* gray — Soph parity */
.bp-icon-review { color: #cc6600; }   /* darkorange — Soph parity */
.bp-icon-notify { color: #f59e0b; }   /* orange — Soph parity */
.bp-icon-vars   { color: #663399; }   /* rebeccapurple — Soph parity */

.bp-activity-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.bp-pill {
    display: inline-flex;
    align-items: center;
    padding: 1px 8px;
    border-radius: 9999px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 500;
    text-transform: lowercase;
    font-feature-settings: 'tnum';
}
/* Variant for pills that carry long content (URLs, paths) — caps the
   visible width and ellipsizes. Hover title still shows the full string. */
.bp-pill.bp-pill--truncate {
    max-width: 28ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-transform: none;
}

.bp-pill-human {
    background-color: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

.bp-pill-system {
    background-color: #ecfeff;
    border-color: #06b6d4;
    color: #0e7490;
}

/* Startup activity row marker — paired with the gentle green tint on the
   card itself. Positional only (Activities[0]); nothing else depends on it. */
.bp-pill-startup {
    background-color: #ecfdf5;
    border-color: #10b981;
    color: #047857;
    text-transform: none;
}

.bp-activity-card--startup {
    background-color: #f0fdf4;
    border-left: 3px solid #10b981;
}

.bp-activity-card--startup.is-expanded {
    background-color: #ecfdf5;
}

/* Startup-form modal — slightly wider than the default modal-dialog so the
   inline form has room to breathe. */
.bp-startup-modal {
    max-width: 720px;
    width: 90%;
}

.bp-startup-description {
    border-left: 3px solid var(--border-subtle);
    padding: 0.25rem 0.75rem;
    margin-bottom: 0.75rem;
    color: var(--text-secondary);
}

.bp-pill-risk-low {
    background-color: #f0fdf4;
    border-color: #22c55e;
    color: #15803d;
}

.bp-pill-risk-medium {
    background-color: #fffbeb;
    border-color: #f59e0b;
    color: #b45309;
}

.bp-pill-risk-high {
    background-color: #fef2f2;
    border-color: #ef4444;
    color: #b91c1c;
}

html.dark .bp-pill {
    background-color: rgba(255, 255, 255, 0.04);
}

html.dark .bp-pill-system {
    background-color: rgba(6, 182, 212, 0.15);
    color: #67e8f9;
}

html.dark .bp-pill-risk-low {
    background-color: rgba(34, 197, 94, 0.15);
    color: #86efac;
}

html.dark .bp-pill-risk-medium {
    background-color: rgba(245, 158, 11, 0.15);
    color: #fcd34d;
}

html.dark .bp-pill-risk-high {
    background-color: rgba(239, 68, 68, 0.15);
    color: #fca5a5;
}

.bp-activity-rowactions {
    display: flex;
    gap: 4px;
    align-items: center;
}

.bp-activity-body {
    border-top: 1px solid var(--border-subtle);
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.bp-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 10px;
}

.bp-grid .form-row--inline {
    align-items: center;
    gap: 14px;
}

.bp-quill-wrap {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.bp-quill-label {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.bp-quill-mount {
    min-height: 200px;
    background-color: var(--bg-canvas);
    border-radius: var(--radius-md);
}

.bp-quill-mount .ql-toolbar {
    border-top-left-radius: var(--radius-md);
    border-top-right-radius: var(--radius-md);
    border-color: var(--border-subtle);
}

.bp-quill-mount .ql-container {
    border-bottom-left-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
    border-color: var(--border-subtle);
    font-family: inherit;
    font-size: 14px;
    min-height: 160px;
}

html.dark .bp-quill-mount .ql-toolbar,
html.dark .bp-quill-mount .ql-container {
    background-color: var(--bg-surface);
    color: var(--text-primary);
}

html.dark .bp-quill-mount .ql-stroke {
    stroke: var(--text-secondary);
}

html.dark .bp-quill-mount .ql-fill {
    fill: var(--text-secondary);
}

html.dark .bp-quill-mount .ql-picker {
    color: var(--text-secondary);
}

/* Token list (Participants & Components) */
.bp-token-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.bp-token {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 4px 4px 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: 9999px;
    font-size: 13px;
    color: var(--text-primary);
}

.bp-token-protected {
    background-color: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
    padding-right: 12px;
}

.bp-token-icon {
    color: var(--text-muted);
    font-size: 13px;
}

.bp-token-remove {
    appearance: none;
    background: transparent;
    border: none;
    width: 22px;
    height: 22px;
    border-radius: 9999px;
    color: var(--text-muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.bp-token-remove:hover {
    background-color: var(--bg-canvas);
    color: var(--danger);
}

/* Per-activity-variable flag buttons (read-only, toggleable). Inside a
   `.bp-token` chip — same dimensions as .bp-token-remove so the chip stays
   compact. Default ("off") is muted; "is-on" flips to the accent colour so
   the author can see at a glance which variables in an activity are
   read-only or toggleable. */
.bp-token-flag {
    appearance: none;
    background: transparent;
    border: none;
    width: 22px;
    height: 22px;
    border-radius: 9999px;
    color: var(--text-muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
}
.bp-token-flag:hover {
    background-color: var(--bg-canvas);
    color: var(--text-secondary);
}
.bp-token-flag.is-on {
    color: var(--accent);
    background-color: var(--accent-soft);
}
.bp-token-flag.is-on:hover {
    color: var(--accent);
    background-color: var(--accent-soft);
    filter: brightness(0.95);
}

/* Manage tab (labelled "Share" in the UI; enum kept as Manage to avoid
   churn — see BluePrinterView TabLabel mapping). */
.bp-raw summary {
    cursor: pointer;
    font-size: 13px;
    color: var(--text-secondary);
    padding: 6px 0;
}

/* Share tab — recipient picker above the read-only JSON view. */
.bp-share-recipient {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 18px;
    max-width: 600px;
}
.bp-share-recipient > .btn {
    flex: 0 0 auto;
}
.bp-share-recipient-label {
    font-weight: 600;
    font-size: 0.9rem;
    flex: 0 0 auto;
    color: var(--text-primary);
}
.bp-share-recipient > .sphere-searchable-select {
    flex: 1 1 auto;
    min-width: 0;
}

.bp-share-json {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.bp-share-json-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.bp-share-json-header > label {
    font-weight: 600;
    font-size: 0.9rem;
    margin: 0;
    color: var(--text-primary);
}
.bp-share-copy {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border: 1px solid var(--border-subtle);
    background: var(--bg-surface);
    color: var(--text-secondary);
    border-radius: var(--radius-sm);
    font-size: 0.85em;
    cursor: pointer;
}
.bp-share-copy:hover {
    color: var(--accent);
    border-color: var(--accent);
}
.bp-share-copy > i {
    font-size: 0.95em;
}

.bp-raw-text {
    width: 100%;
    margin-top: 6px;
}

/* Run tab */
.bp-run-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
}

.bp-run-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

/* Subheading for the per-`$xxx` participant-assignments block on the Run
   tab. Mirrors .bp-activity-vars-header's visual weight. */
.bp-section-subheader {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.95rem;
    font-weight: 600;
    margin: 16px 0 10px;
    color: var(--bs-body-color, #1d1d1f);
}

/* Per-`$xxx` placeholder → username dropdown column on the Run tab.
   Single column so the placeholder names line up vertically; SearchableSelect
   handles its own width. */
.bp-run-bindings {
    display: grid;
    grid-template-columns: 1fr;
    gap: 8px;
    margin-bottom: 12px;
}

.bp-run-log {
    border-top: 1px solid var(--border-subtle);
    padding-top: 12px;
}

.bp-run-log-header {
    font-size: 13px;
    color: var(--text-muted);
    margin: 0 0 8px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.bp-run-log ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.bp-run-log li {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-primary);
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    background-color: var(--bg-surface);
}

.bp-run-log li.is-ok i {
    color: var(--success, #22c55e);
}

.bp-run-log li.is-error i {
    color: var(--danger);
}

/* Instances tab */
.bp-progression-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: 12px;
}

/* /tasks → Activity tab. Forces the same `.bp-progression` cards into a
   single vertical column ordered latest-first (BluePrinter's Instances
   tab uses the grid above instead — multi-column on wide screens). */
.tasks-activity-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.bp-progression {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.bp-progression-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.bp-progression-name {
    font-weight: 600;
    color: var(--text-primary);
}

.bp-progression-id {
    font-size: 11px;
    color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
}

.bp-progression-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    font-size: 12px;
    color: var(--text-secondary);
}

.bp-progression-meta span i {
    margin-right: 4px;
    color: var(--text-muted);
}

.bp-progression-bar {
    height: 6px;
    background-color: var(--bg-canvas);
    border-radius: 3px;
    overflow: hidden;
}

.bp-progression-fill {
    height: 100%;
    background-color: var(--accent);
    transition: width 200ms ease;
}

.bp-progression-pct {
    font-size: 12px;
    color: var(--text-muted);
    text-align: right;
    font-feature-settings: 'tnum';
    font-weight: 500;
}

/* Risk colour. Applies to the span wrapper so both the icon and the word
   pick up the same colour via inheritance — the .bp-progression-meta rule
   above would otherwise force the icon to var(--text-muted). */
.bp-risk             { font-weight: 500; text-transform: capitalize; }
.bp-risk i           { color: inherit; }
.bp-risk-controlled  { color: #0ea5e9; }  /* sky-500 — mitigated */
.bp-risk-low         { color: #16a34a; }  /* green-600 — safe */
.bp-risk-medium      { color: #d97706; }  /* amber-600 — caution */
.bp-risk-high        { color: #dc2626; }  /* red-600 — danger */

/* ─── Statistics tab ──────────────────────────────────────────────────────── */

.stats-tab {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.stats-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 16px;
}

.stats-toolbar-banner {
    display: flex;
    align-items: baseline;
    gap: 12px;
    flex-wrap: wrap;
}

.stats-banner-label {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.stats-banner-value {
    font-size: 28px;
    font-weight: 700;
    color: var(--text-primary);
    font-feature-settings: 'tnum';
    line-height: 1;
}

.stats-banner-sub {
    display: inline-flex;
    gap: 6px;
}

.stats-banner-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    border-radius: 9999px;
    font-size: 12px;
    font-weight: 600;
    font-feature-settings: 'tnum';
    border: 1px solid var(--border-subtle);
}

.stats-banner-pill-ok {
    background-color: #f0fdf4;
    border-color: #22c55e;
    color: #15803d;
}

.stats-banner-pill-err {
    background-color: #fef2f2;
    border-color: #ef4444;
    color: #b91c1c;
}

html.dark .stats-banner-pill-ok {
    background-color: rgba(34, 197, 94, 0.12);
    color: #86efac;
}

html.dark .stats-banner-pill-err {
    background-color: rgba(239, 68, 68, 0.12);
    color: #fca5a5;
}

.stats-toolbar-controls {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.stats-name-filter {
    width: 200px;
}

.stats-topn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-secondary);
}

.stats-topn-select {
    width: 5.5em;
}

.stats-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-secondary);
    user-select: none;
    cursor: pointer;
}

.stats-toggle input {
    margin: 0;
}

.stats-spin {
    animation: stats-spin-anim 0.9s linear infinite;
}

@keyframes stats-spin-anim {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

.stats-chart-card {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.stats-chart-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.stats-chart-header h3 {
    margin: 0;
    font-size: 14px;
    color: var(--text-primary);
    font-weight: 600;
}

.stats-chart-meta {
    font-size: 12px;
    color: var(--text-muted);
    font-feature-settings: 'tnum';
    flex: 1 1 auto;
}

.stats-view-toggle {
    margin-left: auto;
}

.stats-threshold {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}

.stats-threshold-mode {
    width: 7em;
}

.stats-threshold-value {
    width: 7em;
    font-feature-settings: 'tnum';
}

.stats-chart-empty {
    padding: 36px 12px;
    text-align: center;
}

/* OpenRouter key-status card — table + limit-remaining progress bar. */
.stats-keys-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}
.stats-keys-table thead th {
    text-align: left;
    padding: 8px 10px;
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    border-bottom: 1px solid var(--border-subtle);
    font-weight: 600;
}
.stats-keys-table tbody td {
    padding: 8px 10px;
    border-bottom: 1px solid var(--border-subtle);
    color: var(--text-primary);
    vertical-align: middle;
}
.stats-keys-table tbody tr:last-child td { border-bottom: none; }
.stats-keys-num {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.stats-keys-label { font-weight: 600; color: var(--text-primary); }
.stats-keys-config { font-size: 11px; color: var(--text-muted); }
.stats-keys-error {
    color: var(--danger, #ef4444);
    font-size: 12px;
}
.stats-keys-bar-col { width: 34%; min-width: 160px; }
.stats-keys-bar {
    height: 10px;
    border-radius: 5px;
    background-color: var(--bg-canvas, rgba(0, 0, 0, 0.06));
    border: 1px solid var(--border-subtle);
    overflow: hidden;
}
.stats-keys-bar > span {
    display: block;
    height: 100%;
    border-radius: 5px;
    transition: width 200ms ease;
}
.stats-keys-bar-green { background-color: #22c55e; }
.stats-keys-bar-orange { background-color: #f59e0b; }
.stats-keys-bar-red { background-color: #ef4444; }
.stats-keys-bar-unlimited > span { background-color: #22c55e; opacity: 0.45; }

.stats-donut-pair {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 12px;
}

.stats-donut h4 {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    margin: 0 0 6px;
    text-align: center;
}

.stats-empty {
    text-align: center;
    padding: 48px 16px;
    color: var(--text-secondary);
}

.stats-empty i {
    font-size: 32px;
    color: var(--text-muted);
    margin-bottom: 8px;
    display: block;
}

.stats-empty-hint {
    font-size: 12px;
    color: var(--text-muted);
}

.stats-foot {
    font-size: 12px;
    color: var(--text-muted);
    text-align: right;
}

/* ─── Tags tab ────────────────────────────────────────────────────────────── */

.tags-tab {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.tags-toolbar {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.tags-filter-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.tags-filter-input {
    width: 260px;
}

.tags-mode-toggle {
    margin-left: 6px;
}

.tags-active-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.tags-result-toolbar {
    display: flex;
    justify-content: flex-end;
    color: var(--text-muted);
    font-size: 12px;
    font-feature-settings: 'tnum';
}

.tags-table {
    width: 100%;
}

.tags-table td {
    vertical-align: middle;
}

.tags-empty {
    padding: 36px 12px;
    text-align: center;
}

.tags-empty i {
    font-size: 28px;
    color: var(--text-muted);
    margin-bottom: 6px;
    display: block;
}

/* Context badges */
.tags-ctx {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    text-transform: lowercase;
    padding: 2px 8px;
    border-radius: 9999px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    letter-spacing: 0.02em;
}

.tags-ctx-petrinet,
.tags-ctx-flow {
    background-color: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

.tags-ctx-policy {
    background-color: #fef3c7;
    border-color: #f59e0b;
    color: #b45309;
}

.tags-ctx-dataweave {
    background-color: #ecfeff;
    border-color: #06b6d4;
    color: #0e7490;
}

.tags-ctx-type {
    background-color: #f3e8ff;
    border-color: #a855f7;
    color: #7e22ce;
}

.tags-ctx-form {
    background-color: #f0fdf4;
    border-color: #22c55e;
    color: #15803d;
}

.tags-ctx-scheduler {
    background-color: #fdf2f8;
    border-color: #ec4899;
    color: #be185d;
}

.tags-ctx-secret {
    background-color: #fef2f2;
    border-color: #ef4444;
    color: #b91c1c;
}

.tags-ctx-lambda {
    background-color: #fff7ed;
    border-color: #f97316;
    color: #c2410c;
}

html.dark .tags-ctx { background-color: rgba(255, 255, 255, 0.05); }
html.dark .tags-ctx-policy    { background-color: rgba(245, 158, 11, 0.18); color: #fcd34d; }
html.dark .tags-ctx-dataweave { background-color: rgba(6, 182, 212, 0.18); color: #67e8f9; }
html.dark .tags-ctx-type      { background-color: rgba(168, 85, 247, 0.18); color: #d8b4fe; }
html.dark .tags-ctx-form      { background-color: rgba(34, 197, 94, 0.18); color: #86efac; }
html.dark .tags-ctx-scheduler { background-color: rgba(236, 72, 153, 0.18); color: #f9a8d4; }
html.dark .tags-ctx-secret    { background-color: rgba(239, 68, 68, 0.18); color: #fca5a5; }
html.dark .tags-ctx-lambda    { background-color: rgba(249, 115, 22, 0.18); color: #fdba74; }

.tags-name-link {
    color: var(--accent);
    text-decoration: none;
    font-weight: 500;
    /* Reset native <button> chrome so the same class can style both <a> (route
       fallback) and <button> (dialog trigger) consistently. */
    background: transparent;
    border: 0;
    padding: 0;
    font: inherit;
    cursor: pointer;
    text-align: left;
}

.tags-name-link:hover {
    text-decoration: underline;
}

.tags-name {
    color: var(--text-primary);
    font-weight: 500;
}

/* Tag chips inside a row */
.tags-pill-list {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.tags-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 4px 2px 10px;
    border-radius: 9999px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-primary);
    font-size: 12px;
}

.tags-pill-remove {
    appearance: none;
    background: transparent;
    border: none;
    width: 18px;
    height: 18px;
    border-radius: 9999px;
    color: var(--text-muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
}

.tags-pill-remove:hover {
    background-color: var(--bg-surface);
    color: var(--danger);
}

/* Per-row add-tag input */
.tags-add-row {
    display: flex;
    gap: 6px;
}

.tags-add-input {
    flex: 1 1 auto;
    min-width: 0;
}

/* ─── Replay ──────────────────────────────────────────────────────────────── */

.replay-shell {
    display: grid;
    grid-template-columns: 360px 1fr;
    gap: 14px;
    min-height: calc(100vh - 280px);
}

.replay-list {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
}

.replay-list-toolbar {
    display: flex;
    gap: 6px;
    align-items: center;
}

.replay-list-toolbar .form-control {
    flex: 1 1 auto;
}

.replay-breadcrumb {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-muted);
    padding: 4px 4px 8px;
    border-bottom: 1px solid var(--border-subtle);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
}

.replay-breadcrumb-name {
    color: var(--text-primary);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 500;
}

.replay-back {
    appearance: none;
    background: transparent;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 0 4px;
    line-height: 0;
}

.replay-back:hover {
    color: var(--accent);
}

.replay-flow-list,
.replay-instance-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    overflow-y: auto;
    min-height: 0;
}

.replay-flow-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-primary);
    transition: background-color 100ms ease, color 100ms ease;
}

.replay-flow-item:hover {
    background-color: var(--accent-soft);
    color: var(--accent);
}

.replay-flow-item i {
    color: var(--text-muted);
    font-size: 14px;
}

.replay-flow-item:hover i {
    color: var(--accent);
}

.replay-flow-name {
    font-size: 14px;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.replay-instance-item {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    border: 1px solid transparent;
    transition: background-color 100ms ease, border-color 100ms ease;
}

.replay-instance-item:hover {
    background-color: var(--bg-canvas);
}

.replay-instance-item.is-selected {
    background-color: var(--accent-soft);
    border-color: var(--accent);
}

.replay-instance-id {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-primary);
}

.replay-instance-item.is-selected .replay-instance-id {
    color: var(--accent);
}

.replay-instance-times {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    font-size: 11px;
    color: var(--text-muted);
    font-feature-settings: 'tnum';
}

.replay-instance-times i {
    margin-right: 3px;
}

.replay-instance-duration {
    color: var(--accent);
    font-weight: 500;
}

/* Right pane */
.replay-main {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-height: 0;
    min-width: 0;
}

.replay-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
}

.replay-header h3 {
    margin: 0 0 4px;
    font-size: 16px;
    color: var(--text-primary);
}

.replay-instance-code {
    display: block;
    font-size: 11px;
    color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    margin-bottom: 6px;
    word-break: break-all;
}

.replay-header-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    font-size: 12px;
    color: var(--text-secondary);
    font-feature-settings: 'tnum';
}

.replay-header-meta i {
    color: var(--text-muted);
    margin-right: 4px;
}

.replay-header-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

/* Monaco mount */
.replay-mount {
    flex: 1 1 auto;
    min-height: 520px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.replay-mount-empty {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    text-align: center;
    color: var(--text-muted);
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 60px 24px;
    min-height: 520px;
}

.replay-mount-empty i {
    font-size: 36px;
    color: var(--text-muted);
}

/* Responsive: stack the master/detail at narrow viewports */
@media (max-width: 960px) {
    .replay-shell {
        grid-template-columns: 1fr;
    }
    .replay-list {
        max-height: 320px;
    }
}

/* ─── Active instance detail (/active/{id}) ───────────────────────────────── */

.instance-detail-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.instance-detail-back {
    align-self: flex-start;
}

.instance-detail-header {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 16px 18px;
    display: flex;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.instance-detail-flow {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
}

.instance-detail-flow i {
    color: var(--accent);
}

.instance-detail-version {
    font-size: 12px;
    color: var(--text-muted);
    font-weight: 500;
    margin-left: 2px;
}

.instance-detail-id {
    display: block;
    margin: 6px 0 8px;
    color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 12px;
    word-break: break-all;
}

.instance-detail-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    font-size: 12px;
    color: var(--text-secondary);
    font-feature-settings: 'tnum';
}

.instance-detail-meta i {
    color: var(--text-muted);
    margin-right: 4px;
}

.instance-detail-sub-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    border-radius: 9999px;
    background-color: #fffbeb;
    border: 1px solid #f59e0b;
    color: #b45309;
    font-weight: 600;
}

html.dark .instance-detail-sub-pill {
    background-color: rgba(245, 158, 11, 0.15);
    color: #fcd34d;
}

.instance-detail-sub-pill i {
    color: inherit;
    margin-right: 4px;
}

.instance-detail-header-actions {
    display: flex;
    gap: 8px;
    align-items: flex-start;
}

/* Tab content */
.instance-tab-pane {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.instance-tab-pane.is-hidden {
    display: none;
}

.instance-tab-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.instance-monaco-mount {
    min-height: 520px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
}

/* Markings — petrinet outline host */
.instance-petri-host {
    background: #ffffff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 8px;
    min-height: 300px;
    overflow: auto;
}

.instance-petri-host svg {
    background-color: #ffffff !important;
    max-width: 100%;
    height: auto;
}

/* gojs-based read-only workflow canvas (WorkflowViewer.razor). Sized via inline
   `Style="height: …px"`; this rule just supplies the chrome. */
.petrinet-viewer-host,
.configurator-petri-host {
    background: #ffffff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
    width: 100%;
}

.petrinet-viewer-empty {
    margin-top: 8px;
    padding: 12px;
    border: 1px dashed var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--surface-subtle, #f9fafb);
    color: var(--text-muted, #6b7280);
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
}

.petrinet-viewer-empty i {
    font-size: 18px;
    opacity: 0.7;
}

.petrinet-viewer-empty p { margin: 0; }

/* Markings — side-by-side row: tokens on the left, read-only WorkflowViewer on
   the right. Below ~1100px we collapse to a stack (tokens above the viewer) —
   see the @media block at the bottom of this block. */
.instance-markings-row {
    display: grid;
    grid-template-columns: minmax(260px, 360px) minmax(0, 1fr);
    gap: 14px;
    align-items: start;
}

/* The left column caps at the viewer height (520px) so a long token list
   scrolls inside its own pane instead of stretching the whole row. */
.instance-markings-row__tokens {
    max-height: 520px;
    overflow-y: auto;
}

@media (max-width: 1100px) {
    .instance-markings-row {
        grid-template-columns: 1fr;
    }
    .instance-markings-row__tokens {
        max-height: none;
        overflow-y: visible;
    }
}

/* Markings — token grid */
.instance-tokens {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.instance-tokens-header {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.instance-tokens-count {
    color: var(--accent);
    font-weight: 700;
}

.instance-tokens-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 10px;
}

.instance-token-card {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.instance-token-card header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.instance-token-place {
    font-weight: 600;
    color: var(--accent);
}

.instance-token-id {
    font-size: 11px;
    color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    word-break: break-all;
}

.instance-token-payload {
    margin: 0;
    padding: 8px 10px;
    background-color: var(--bg-canvas);
    border-radius: var(--radius-sm);
    font-size: 11px;
    color: var(--text-primary);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    max-height: 220px;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
}

.instance-tokens-empty {
    text-align: center;
    padding: 24px 12px;
}

/* Pending (staged via Tokens tab, not yet persisted via Nudge) — orange tint */
.instance-token-card.is-pending {
    border-color: #e67e22;
    background-color: rgba(230, 126, 34, 0.08);
}

.instance-token-card.is-pending .instance-token-place {
    color: #e67e22;
}

.instance-token-pending-badge {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: #e67e22;
    background-color: rgba(230, 126, 34, 0.15);
    padding: 1px 6px;
    border-radius: 8px;
}

/* Historical (token from the AeonExecutable's `prev` snapshot while a
   sub-flow is awaited) — gray tint mirroring the pending badge pattern.
   Same shape/size so the two badges look like sibling states. */
.instance-token-card.is-historical {
    border-color: #95a5a6;
    background-color: rgba(127, 140, 141, 0.08);
}

.instance-token-card.is-historical .instance-token-place {
    color: #7f8c8d;
}

.instance-token-historical-badge {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: #7f8c8d;
    background-color: rgba(127, 140, 141, 0.15);
    padding: 1px 6px;
    border-radius: 8px;
}

/* × control on every token card. Tiny, ghosted by default, reddens on hover. */
.instance-token-delete {
    margin-left: auto;
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    width: 22px;
    height: 22px;
    border-radius: var(--radius-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 12px;
    line-height: 1;
    transition: background-color 80ms ease, color 80ms ease;
}

.instance-token-delete:hover,
.instance-token-delete:focus-visible {
    color: #c0392b;
    background-color: rgba(192, 57, 43, 0.12);
    outline: none;
}

.instance-token-card.is-pending .instance-token-delete:hover,
.instance-token-card.is-pending .instance-token-delete:focus-visible {
    color: #d35400;
    background-color: rgba(230, 126, 34, 0.18);
}

/* Pending count chip on the Tokens tab button — orange instead of accent */
.instance-tab-count-pending {
    background-color: rgba(230, 126, 34, 0.2);
    color: #e67e22;
    border: 1px solid #e67e22;
}

/* Toolbar above the Markings row — hosts the Nudge button + hint */
.instance-markings-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
    flex-wrap: wrap;
}

.instance-markings-hint {
    font-size: 12px;
    color: var(--text-muted);
}

.instance-send-label-hint {
    text-transform: none;
    letter-spacing: 0;
    color: var(--text-muted);
    font-weight: 400;
}

.instance-send-pending-summary {
    margin-left: auto;
    font-size: 12px;
    color: var(--text-muted);
}

/* Capacity-error modal — small, centered, just an OK button */
.instance-capacity-modal {
    max-width: 480px;
}

/* Send tab */
.instance-send-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 12px;
    align-items: flex-end;
}

.instance-send-target {
    display: inline-block;
    padding: 8px 12px;
    background-color: var(--bg-canvas);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 12px;
    word-break: break-all;
}

.instance-send-label {
    margin: 8px 0 -4px;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.instance-send-payload {
    min-height: 180px;
}

.instance-send-actions {
    display: flex;
    justify-content: flex-end;
}

.instance-send-response {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px;
}

.instance-send-response header {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    margin-bottom: 6px;
}

.instance-send-response pre {
    margin: 0;
    font-size: 12px;
    color: var(--text-primary);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    max-height: 320px;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
}

/* ─── Endpoint Documentation (/endpoint-docs/{process}/{version}) ─────────── */

.endpoint-doc-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.endpoint-doc-back {
    align-self: flex-start;
}

.endpoint-doc-header {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 16px 18px;
}

.endpoint-doc-flow {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
}

.endpoint-doc-flow i {
    color: var(--accent);
}

.endpoint-doc-version {
    font-size: 12px;
    color: var(--text-muted);
    font-weight: 500;
    margin-left: 4px;
}

.endpoint-doc-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin-top: 6px;
    font-size: 12px;
    color: var(--text-secondary);
    font-feature-settings: 'tnum';
}

.endpoint-doc-meta i {
    color: var(--text-muted);
    margin-right: 4px;
}

.endpoint-doc-description {
    margin: 10px 0 0;
    color: var(--text-primary);
    line-height: 1.5;
}

/* Master-detail layout for the Transitions/Places tabs */
.doc-master-detail {
    display: grid;
    grid-template-columns: 320px 1fr;
    gap: 14px;
    min-height: 520px;
}

.doc-list {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-height: 0;
}

.doc-list-items {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    overflow-y: auto;
    min-height: 0;
}

.doc-list-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    border: 1px solid transparent;
    transition: background-color 100ms ease, border-color 100ms ease;
}

.doc-list-item:hover {
    background-color: var(--bg-canvas);
}

.doc-list-item.is-selected {
    background-color: var(--accent-soft);
    border-color: var(--accent);
}

.doc-list-item-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.doc-list-item.is-selected .doc-list-item-name {
    color: var(--accent);
}

.doc-list-item-meta {
    font-size: 11px;
    color: var(--text-muted);
}

.doc-detail {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-width: 0;
}

.doc-detail-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    flex-wrap: wrap;
}

.doc-detail-header h3 {
    margin: 0;
    font-size: 16px;
    color: var(--text-primary);
}

.doc-detail-sub {
    font-size: 12px;
    color: var(--text-muted);
}

.doc-fieldset {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.doc-fieldset h4 {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.doc-fieldset-empty {
    text-align: left;
    padding: 12px 0;
    color: var(--text-muted);
    font-size: 13px;
}

/* Key/value tables in the detail pane */
.doc-kv-table {
    width: 100%;
    border-collapse: collapse;
}

.doc-kv-table th {
    text-align: left;
    font-weight: 600;
    color: var(--text-secondary);
    width: 9em;
    padding: 6px 10px 6px 0;
    vertical-align: top;
    font-size: 13px;
}

.doc-kv-table td {
    padding: 6px 0;
    color: var(--text-primary);
    font-size: 13px;
    word-break: break-word;
    vertical-align: top;
}

.doc-kv-table tr + tr th,
.doc-kv-table tr + tr td {
    border-top: 1px solid var(--border-subtle);
}

.doc-kv-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 12px;
    color: var(--text-primary);
    background-color: var(--bg-canvas);
    padding: 2px 6px;
    border-radius: var(--radius-sm);
}

.doc-kv-hint {
    font-size: 11px;
    color: var(--text-muted);
    margin-left: 6px;
}

.doc-kv-empty {
    color: var(--text-muted);
}

.doc-uri {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 12px;
    color: var(--text-primary);
    background-color: var(--bg-canvas);
    padding: 4px 8px;
    border-radius: var(--radius-sm);
    word-break: break-all;
    display: inline-block;
}

.doc-pill {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 9999px;
    font-size: 11px;
    font-weight: 600;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
}

.doc-pill-override {
    background-color: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

.doc-pill-audit {
    background-color: #f0fdf4;
    border-color: #22c55e;
    color: #15803d;
}

html.dark .doc-pill-audit {
    background-color: rgba(34, 197, 94, 0.18);
    color: #86efac;
}

.doc-pill-audit i {
    margin-right: 4px;
}

.doc-policy-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.doc-policy-list .btn {
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
}

.doc-policy-code {
    margin: 0;
    padding: 12px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 12px;
    color: var(--text-primary);
    max-height: 60vh;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
}

.doc-kpi-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}

.doc-kpi-table th {
    text-align: left;
    padding: 6px 10px;
    background-color: var(--bg-canvas);
    color: var(--text-secondary);
    font-weight: 600;
    border-bottom: 1px solid var(--border-subtle);
}

.doc-kpi-table td {
    padding: 6px 10px;
    border-bottom: 1px solid var(--border-subtle);
    vertical-align: top;
}

.doc-kpi-table code {
    display: block;
    white-space: pre-wrap;
    word-break: break-word;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    color: var(--text-primary);
}

/* Responsive: stack master/detail at narrow viewports */
@media (max-width: 960px) {
    .doc-master-detail {
        grid-template-columns: 1fr;
    }
    .doc-list {
        max-height: 280px;
    }
}

/* ─── Shared compact toggle (originally Assistant; reused by DebuggerView) ── */

.assistant-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-secondary);
    user-select: none;
    cursor: pointer;
}

.assistant-toggle input {
    margin: 0;
}

/* ─── Simulator (/simulator) ──────────────────────────────────────────────── */

.simulator-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.simulator-model-picker {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}

.simulator-model-picker select {
    width: 18em;
}

.simulator-scenario {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) minmax(0, 1fr);
    gap: 14px;
    min-height: 480px;
}

.simulator-list-card,
.simulator-editor-card {
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
}

.simulator-list-card > header,
.simulator-editor-card > header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.simulator-list-card h4,
.simulator-editor-card h4 {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.simulator-list-card > header span,
.simulator-editor-card > header span {
    font-size: 12px;
    color: var(--text-secondary);
}

.simulator-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    overflow-y: auto;
    min-height: 0;
}

.simulator-list-item {
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    cursor: pointer;
    color: var(--text-primary);
    font-size: 13px;
    transition: background-color 100ms ease, border-color 100ms ease;
}

.simulator-list-item:hover {
    background-color: var(--bg-surface);
}

.simulator-list-item.is-selected {
    background-color: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
    font-weight: 500;
}

.simulator-list-item-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 6px 6px 10px;
}

.simulator-list-name {
    appearance: none;
    background: transparent;
    border: none;
    text-align: left;
    flex: 1 1 auto;
    cursor: pointer;
    color: inherit;
    display: flex;
    align-items: center;
    gap: 6px;
    font-family: inherit;
    font-size: 13px;
}

.simulator-list-name:hover {
    color: var(--accent);
}

.simulator-list-empty {
    padding: 12px;
    color: var(--text-muted);
    font-size: 12px;
    text-align: center;
}

/* Risk pills inside the configured list */
.simulator-risk {
    margin-left: auto;
    display: inline-block;
    padding: 1px 8px;
    border-radius: 9999px;
    font-size: 10px;
    font-weight: 600;
    text-transform: lowercase;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
}

.simulator-risk-controlled { background-color: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.simulator-risk-low        { background-color: #f0fdf4; border-color: #22c55e; color: #15803d; }
.simulator-risk-medium     { background-color: #fffbeb; border-color: #f59e0b; color: #b45309; }
.simulator-risk-high       { background-color: #fef2f2; border-color: #ef4444; color: #b91c1c; }
html.dark .simulator-risk-low      { background-color: rgba(34, 197, 94, 0.18); color: #86efac; }
html.dark .simulator-risk-medium   { background-color: rgba(245, 158, 11, 0.18); color: #fcd34d; }
html.dark .simulator-risk-high     { background-color: rgba(239, 68, 68, 0.18); color: #fca5a5; }

.simulator-pair {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.simulator-pair-label {
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.simulator-pair-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
}

.simulator-editor-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

/* Run controls + warning text */
.simulator-warn {
    color: var(--danger);
}

.simulator-run-controls {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-left: auto;
}

.simulator-runs {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-secondary);
}

.simulator-runs-input {
    width: 6em;
}

/* Report summary */
.simulator-report-summary {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.simulator-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 10px;
}

.simulator-stat {
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.simulator-stat-label {
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.simulator-stat-value {
    font-size: 22px;
    font-weight: 700;
    color: var(--text-primary);
    font-feature-settings: 'tnum';
    line-height: 1.1;
}

.simulator-stat-sub {
    font-size: 11px;
    color: var(--text-secondary);
    font-feature-settings: 'tnum';
}

.simulator-report-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 12px;
}

.simulator-report-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.simulator-report-card h4 {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

@media (max-width: 1100px) {
    .simulator-scenario {
        grid-template-columns: 1fr;
    }
    .simulator-list-card {
        max-height: 280px;
    }
}

/* ─── Debugger (/debugger + /debugger/{ref}) ──────────────────────────────── */

.debugger-list-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.debugger-list-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 16px;
    flex-wrap: wrap;
}

.debugger-list-filter {
    flex: 1 1 320px;
    max-width: 440px;
}

.debugger-list-table {
    width: 100%;
}

/* Per-story view */
.debugger-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.debugger-back {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.debugger-orb-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    color: #b45309;
    background-color: #fffbeb;
    border: 1px solid #f59e0b;
    padding: 4px 10px;
    border-radius: 9999px;
}

html.dark .debugger-orb-pill {
    background-color: rgba(245, 158, 11, 0.15);
    color: #fcd34d;
}

.debugger-tab-pane {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.debugger-tab-pane.is-hidden { display: none; }

.debugger-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
}

.debugger-story-grid {
    display: grid;
    grid-template-columns: minmax(0, 360px) minmax(0, 1fr);
    gap: 14px;
    min-height: 480px;
}

.debugger-actions-list,
.debugger-action-editor {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
}

.debugger-actions-list > header,
.debugger-action-editor > header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.debugger-actions-list h4,
.debugger-action-editor h4 {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 6px;
}

.debugger-action-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.debugger-action-seq {
    font-size: 11px;
    color: var(--text-muted);
    font-weight: 600;
    font-feature-settings: 'tnum';
    min-width: 1.8em;
}

.debugger-action-label {
    color: var(--text-secondary);
    font-size: 13px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.debugger-action-type {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 9999px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    letter-spacing: 0.04em;
}

.debugger-action-type-start     { background-color: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.debugger-action-type-session   { background-color: #ecfeff; border-color: #06b6d4; color: #0e7490; }
.debugger-action-type-token     { background-color: #f0fdf4; border-color: #22c55e; color: #15803d; }
.debugger-action-type-fire      { background-color: #fff7ed; border-color: #f97316; color: #c2410c; }
.debugger-action-type-mock      { background-color: #f3e8ff; border-color: #a855f7; color: #7e22ce; }
.debugger-action-type-break     { background-color: #fffbeb; border-color: #f59e0b; color: #b45309; }
.debugger-action-type-delete    { background-color: #fef2f2; border-color: #ef4444; color: #b91c1c; }

html.dark .debugger-action-type-session  { background-color: rgba(6, 182, 212, 0.18); color: #67e8f9; }
html.dark .debugger-action-type-token    { background-color: rgba(34, 197, 94, 0.18); color: #86efac; }
html.dark .debugger-action-type-fire     { background-color: rgba(249, 115, 22, 0.18); color: #fdba74; }
html.dark .debugger-action-type-mock     { background-color: rgba(168, 85, 247, 0.18); color: #d8b4fe; }
html.dark .debugger-action-type-break    { background-color: rgba(245, 158, 11, 0.18); color: #fcd34d; }
html.dark .debugger-action-type-delete   { background-color: rgba(239, 68, 68, 0.18); color: #fca5a5; }

.debugger-action-buttons {
    display: flex;
    gap: 4px;
    padding-top: 6px;
    border-top: 1px solid var(--border-subtle);
}

.debugger-add-row {
    display: flex;
    gap: 6px;
}

.debugger-action-data {
    min-height: 200px;
    font-size: 12px;
}

/* Debugger tab */
.debugger-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

.debugger-output-tabs {
    margin-top: 4px;
}

.debugger-output-pane {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    min-height: 200px;
}

.debugger-output-pane.is-hidden { display: none; }

.debugger-output-pre {
    margin: 0;
    background-color: var(--bg-canvas);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 12px;
    color: var(--text-primary);
    max-height: 480px;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
}

/* Manage tab */
.debugger-manage-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
    gap: 14px;
}

.debugger-redis-key {
    display: inline-block;
    margin-top: 6px;
    padding: 4px 10px;
    background-color: var(--bg-canvas);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-size: 11px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    word-break: break-all;
}

@media (max-width: 1100px) {
    .debugger-story-grid {
        grid-template-columns: 1fr;
    }
    .debugger-actions-list {
        max-height: 320px;
    }
}

/* ─── Apps (/apps) ────────────────────────────────────────────────────────── */

.apps-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.apps-tab-pane {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.apps-tab-pane.is-hidden { display: none; }

.apps-orb-pill {
    display: inline-flex;
    align-self: flex-start;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    color: #b45309;
    background-color: #fffbeb;
    border: 1px solid #f59e0b;
    padding: 4px 10px;
    border-radius: 9999px;
}

html.dark .apps-orb-pill {
    background-color: rgba(245, 158, 11, 0.15);
    color: #fcd34d;
}

.apps-name-input {
    max-width: 32em;
}

.apps-edit-grid {
    display: grid;
    grid-template-columns: minmax(0, 360px) minmax(0, 1fr);
    gap: 14px;
    min-height: 480px;
}

.apps-list-card,
.apps-slide-editor {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
}

.apps-list-card > header,
.apps-slide-editor > header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.apps-list-card h4,
.apps-slide-editor h4 {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.apps-add-btn {
    margin-left: auto;
}

/* Submit-type pills */
.apps-submit-pill {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 9999px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    letter-spacing: 0.04em;
}

.apps-submit-pill-local   { background-color: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.apps-submit-pill-start   { background-color: #f0fdf4; border-color: #22c55e; color: #15803d; }
.apps-submit-pill-message { background-color: #fff7ed; border-color: #f97316; color: #c2410c; }

html.dark .apps-submit-pill-start   { background-color: rgba(34, 197, 94, 0.18); color: #86efac; }
html.dark .apps-submit-pill-message { background-color: rgba(249, 115, 22, 0.18); color: #fdba74; }

.apps-raw-actions {
    display: flex;
    gap: 8px;
    margin-top: 6px;
}

/* Run tab */
.apps-run-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    flex-wrap: wrap;
}

.apps-run-header h3 {
    margin: 0;
    font-size: 16px;
    color: var(--text-primary);
}

.apps-run-step {
    font-size: 14px;
    color: var(--text-secondary);
    margin-left: 6px;
}

.apps-run-progress {
    font-size: 12px;
    color: var(--text-muted);
    font-feature-settings: 'tnum';
}

.apps-form-host {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
}

.apps-run-actions {
    display: flex;
    justify-content: flex-end;
}

.apps-run-summary {
    background-color: var(--bg-surface);
    border: 1px solid var(--accent);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.apps-run-summary h4 {
    margin: 0;
    font-size: 14px;
    color: var(--accent);
    font-weight: 600;
}

.apps-result-card {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.apps-result-card header {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
}

.apps-result-title {
    font-weight: 600;
    color: var(--text-primary);
}

.apps-result-uri {
    font-size: 11px;
    color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    word-break: break-all;
}

.apps-result-pre {
    margin: 0;
    padding: 8px 10px;
    background-color: var(--bg-canvas);
    border-radius: var(--radius-sm);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 12px;
    color: var(--text-primary);
    max-height: 320px;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
}

/* Block that hosts the SphereResponseViewer Monaco editor inside the Orb
   response card. Fixed height keeps the result card from inflating when
   Monaco hands back a long value; Monaco's own scroll takes over. */
.apps-response-value-section {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 4px;
}

.apps-response-value-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}

.apps-response-value-mount {
    height: 280px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

@media (max-width: 1100px) {
    .apps-edit-grid {
        grid-template-columns: 1fr;
    }
    .apps-list-card {
        max-height: 320px;
    }
}

/* Split layout for the App Builder Run tab while the scene is still in
   progress. Form on the left, accumulating compact result cards on the right.
   The aside is only rendered once runResults.Count > 0, so the very first
   slide gets the full width until something has been captured. After
   runEnded the page reverts to the existing single-column finished view
   without these styles applying. */
.apps-run-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 18px;
    align-items: start;
}

.apps-run-main {
    /* min-width:0 lets the form/host shrink inside the grid track instead of
       pushing the side column off-screen on long URLs / wide previews. */
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.apps-run-side {
    display: flex;
    flex-direction: column;
    gap: 8px;
    /* Cap the side column's height so it scrolls independently of the form
       host. Roughly matches the visible viewport minus the page header/tab
       chrome — operators see the latest captures without the form
       disappearing off-screen. */
    max-height: 70vh;
    overflow-y: auto;
    padding-right: 4px;
}

.apps-run-side-header {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 6px 8px;
    background-color: var(--bg-canvas);
    border-bottom: 1px solid var(--border-subtle);
}

.apps-run-side-title {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}

.apps-run-side-count {
    font-size: 12px;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.apps-result-card--compact {
    padding: 8px 10px;
    gap: 4px;
}

.apps-result-card--compact header {
    gap: 6px;
}

.apps-result-card--compact .apps-result-title {
    font-size: 12px;
}

.apps-result-card--compact .apps-result-uri {
    font-size: 10px;
}

.apps-result-card--compact .apps-result-pre {
    max-height: 120px;
    font-size: 11px;
    padding: 6px 8px;
}

@media (max-width: 1100px) {
    .apps-run-grid {
        grid-template-columns: 1fr;
    }
    .apps-run-side {
        max-height: 360px;
    }
}

/* ─── Apps launchpad (/apps) ──────────────────────────────────────────────── */

.apps-launch-toolbar {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    flex-wrap: wrap;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 16px;
    margin-bottom: 14px;
}

.apps-launch-namespace {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 14em;
}

.apps-launch-label {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.apps-launch-search {
    flex: 1 1 240px;
    max-width: 30em;
}

.apps-launch-builder {
    margin-left: auto;
}

/* The launchpad cards re-use `.app-card-grid` / `.app-card`. We render them as <button>
   so the click triggers an in-page state change; these overrides make the button behave
   visually identical to the <a>-based AppCard used elsewhere. */
.apps-launch-card {
    appearance: none;
    background-color: var(--bg-surface);
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    color: inherit;
    width: 100%;
}

.apps-launch-card-mock {
    opacity: 0.78;
    border-style: dashed;
}

.apps-launch-card-mock-pill {
    display: inline-block;
    margin-left: 6px;
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 1px 6px;
    border-radius: 9999px;
    background-color: var(--accent-soft);
    border: 1px solid var(--accent);
    color: var(--accent);
    vertical-align: middle;
}

.apps-launch-card-flow {
    font-feature-settings: 'tnum';
}

.apps-launch-card-version {
    color: var(--text-muted);
    font-size: 11px;
}

/* Trash button overlaid in the top-right corner of an apps-launch-card.
   AuthorizeView around the rendered element gates visibility to Admin /
   SuperAdmin, and the outer card is now a <div role="button"> so this real
   <button> can sit inside without HTML5 nesting violations. */
.apps-launch-card-delete {
    position: absolute;
    top: 8px;
    right: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    background-color: transparent;
    color: var(--text-muted);
    font-size: 14px;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}

.apps-launch-card-delete:hover {
    background-color: var(--danger-soft, rgba(220, 53, 69, 0.1));
    color: var(--danger, #dc3545);
    border-color: var(--danger, #dc3545);
}

.apps-launch-card-delete:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Edit button — sits to the left of the trash, same square footprint,
   primary-accent hover instead of danger so the two affordances are
   visually distinct. Gated to SuperAdmin via AuthorizeView; if the trash
   is hidden (non-Admin), the edit slot is too so the corner stays clean. */
.apps-launch-card-edit {
    position: absolute;
    top: 8px;
    right: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    background-color: transparent;
    color: var(--text-muted);
    font-size: 14px;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}

.apps-launch-card-edit:hover {
    background-color: var(--accent-soft, rgba(59, 130, 246, 0.1));
    color: var(--accent, #3b82f6);
    border-color: var(--accent, #3b82f6);
}

.apps-launch-card-edit:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Inline launch panel — shown in place of the grid once a card is clicked. */
.apps-launch-panel {
    display: flex;
    flex-direction: column;
    gap: 14px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 16px 18px;
}

.apps-launch-panel-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.apps-launch-panel-title {
    margin: 4px 0 6px;
    font-size: 18px;
    color: var(--text-primary);
}

.apps-launch-panel-description {
    margin: 0 0 6px;
    color: var(--text-secondary);
}

.apps-launch-panel-route {
    display: inline-block;
    padding: 2px 10px;
    background-color: var(--bg-canvas);
    border-radius: var(--radius-sm);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 11px;
    color: var(--text-muted);
    word-break: break-all;
}

.apps-launch-actions {
    display: flex;
    justify-content: flex-end;
}

/* ─── AI Admin (/ai) ──────────────────────────────────────────────────────── */

.ai-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.ai-tab {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.ai-tab-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.ai-tab-toolbar h3 {
    margin: 0;
    font-size: 16px;
    color: var(--text-primary);
}

.ai-tab-meta {
    font-size: 12px;
    color: var(--text-muted);
    font-feature-settings: 'tnum';
    flex: 1 1 auto;
}

/* Service-type pills mirror the action-type pill palette pattern. */
.ai-service-pill {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 9999px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    letter-spacing: 0.04em;
}

.ai-service-pill-openai     { background-color: #ecfdf5; border-color: #10b981; color: #047857; }
.ai-service-pill-gemini     { background-color: #eff6ff; border-color: #3b82f6; color: #1d4ed8; }
.ai-service-pill-claude     { background-color: #fef3c7; border-color: #f59e0b; color: #b45309; }
.ai-service-pill-openrouter { background-color: #f5f3ff; border-color: #8b5cf6; color: #6d28d9; }

html.dark .ai-service-pill-openai     { background-color: rgba(16, 185, 129, 0.18); color: #6ee7b7; }
html.dark .ai-service-pill-gemini     { background-color: rgba(59, 130, 246, 0.18); color: #93c5fd; }
html.dark .ai-service-pill-claude     { background-color: rgba(245, 158, 11, 0.18); color: #fcd34d; }
html.dark .ai-service-pill-openrouter { background-color: rgba(139, 92, 246, 0.18); color: #c4b5fd; }

.ai-secret {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 11.5px;
    color: var(--text-secondary);
}

.ai-reveal {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    user-select: none;
}

.ai-reveal input {
    margin: 0;
}

/* System prompts sections */
.ai-prompts-section {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.ai-prompts-section header {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
}

.ai-prompts-section h4 {
    margin: 0;
    font-size: 14px;
    color: var(--text-primary);
}

.ai-prompts-actions {
    display: flex;
    gap: 8px;
    margin-left: auto;
}

.ai-prompts-textarea {
    min-height: 200px;
    font-size: 12.5px;
    line-height: 1.5;
}

/* ─── Configurator (/configurator) ────────────────────────────────────────── */

.configurator-shell {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.configurator-tab-pane {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.configurator-tab-pane.is-hidden { display: none; }

.configurator-picker {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 16px;
}

.configurator-picker-label {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.configurator-picker-select {
    width: 22em;
}

.configurator-loaded {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--accent);
}

/* Inline anchor-style button used inside data-table cells (e.g. the
   Operations type pair). Reads like a hyperlink but is a real <button> so
   the click handler can pop a Blazor dialog without leaving the page. */
.link-button {
    appearance: none;
    background: transparent;
    border: none;
    padding: 0;
    margin: 0;
    color: var(--accent);
    cursor: pointer;
    font: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.link-button:hover {
    text-decoration: none;
    color: var(--accent-strong, var(--accent));
}

.overview-type-arrow {
    margin: 0 6px;
    color: var(--text-muted);
}

/* Compact 22×22 icon button placed next to a dataweave name in the
   Transitions card. Slightly tighter than .icon-btn to keep the row scan-able. */
.overview-dw-icon {
    margin-left: 4px;
    padding: 2px 4px;
    font-size: 12px;
}

/* ─── Weave dialog ─────────────────────────────────────────────────────────
   Compact visual-mapping dialog. Layout from top to bottom:
     header  → fixed 44px
     toolbar → type pickers row (~52px)
     body    → 3-column grid (from-tree | Monaco | to-tree), fills remainder
     toolbar → weave/constant/undo row (~40px)
     summary → wrap of mapping chips (auto, capped)
     footer  → save/close
   Sized at 1040×640 so the editor has breathing room without overwhelming
   the underlying Configurator. Min/max keep it usable on narrower viewports. */

.weave-dialog {
    max-width: min(1040px, 95vw);
    width: 95vw;
    max-height: 90vh;
    height: 640px;
}

.weave-dialog-body {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0; /* let the workspace claim leftover height */
}

.weave-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.weave-type-row {
    gap: 12px;
}

.weave-type-cell {
    flex: 1 1 220px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.weave-label {
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.weave-arrow {
    align-self: flex-end;
    margin-bottom: 8px;
    color: var(--text-muted);
    font-size: 16px;
}

.weave-workspace {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr) 220px;
    gap: 10px;
    flex: 1 1 auto;
    min-height: 0;
}

.weave-tree-pane,
.weave-editor-pane {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

.weave-tree-pane > header,
.weave-editor-pane > header {
    padding: 6px 10px;
    border-bottom: 1px solid var(--border-subtle);
    background-color: var(--bg-canvas);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    min-height: 24px;
}

.weave-selected {
    color: var(--accent);
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    font-size: 10.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 60%;
}

.weave-dirty {
    color: var(--accent);
    font-size: 10.5px;
    text-transform: none;
    letter-spacing: 0;
}

.weave-tree {
    list-style: none;
    margin: 0;
    padding: 4px 0;
    overflow: auto;
    flex: 1 1 auto;
    font-size: 12.5px;
}

.weave-tree-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 8px 3px 6px;
    cursor: pointer;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.weave-tree-item:hover {
    background-color: var(--bg-canvas);
}

.weave-tree-item.is-selected {
    background-color: var(--accent);
    color: white;
}

.weave-tree-item i {
    opacity: 0.7;
    font-size: 11px;
}

.weave-tree-item.is-selected i {
    opacity: 0.95;
}

.weave-empty {
    padding: 12px;
    text-align: center;
    color: var(--text-muted);
    font-size: 12px;
}

.weave-editor-mount {
    flex: 1 1 auto;
    min-height: 0;
}

.weave-actions-row {
    padding: 4px 0;
    border-top: 1px dashed var(--border-subtle);
    border-bottom: 1px dashed var(--border-subtle);
}

.weave-divider {
    width: 1px;
    background-color: var(--border-subtle);
    align-self: stretch;
}

.weave-spacer {
    flex: 1 1 auto;
}

.weave-constant-input {
    width: 160px;
}

.weave-mappings {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    max-height: 64px;
    overflow: auto;
}

.weave-mapping-chip {
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: 999px;
    padding: 2px 8px;
    font-size: 11.5px;
    color: var(--text-primary);
}

.weave-error {
    margin: 0;
}

.weave-footer {
    justify-content: flex-end;
}

@media (max-width: 900px) {
    .weave-workspace {
        grid-template-columns: 1fr;
        grid-auto-rows: minmax(160px, auto);
    }
    .weave-dialog {
        height: auto;
        max-height: 95vh;
    }
}

/* Inline call-to-action inside the deploy banner (e.g. "Open dataweave
   editor"). Sits between the multi-line message and the close button —
   page-banner uses `align-items: flex-start`, so nudge it down a hair so
   it aligns with the first message line. */
.configurator-banner-link {
    align-self: flex-start;
    white-space: nowrap;
    padding: 0;
    font-size: 13px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    text-decoration: underline;
}

.configurator-banner-link:hover {
    text-decoration: none;
}

.configurator-card {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.configurator-card > header h3 {
    margin: 0;
    font-size: 14px;
    color: var(--text-primary);
}

.configurator-card > header p {
    margin: 2px 0 0;
    font-size: 12px;
    color: var(--text-muted);
}

.configurator-general {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 14px;
}

.configurator-card-actions,
.configurator-card-wide {
    grid-column: 1 / -1;
}

.configurator-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.configurator-version-row {
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
}

.configurator-version-row .form-control {
    flex: 1 1 8em;
    min-width: 8em;
}

.configurator-overview,
.configurator-workflow,
.configurator-operations,
.configurator-exceptions {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.configurator-add-actions {
    display: flex;
    justify-content: flex-end;
}

.configurator-exceptions-grid {
    display: grid;
    grid-template-columns: minmax(0, 360px) minmax(0, 1fr);
    gap: 14px;
    min-height: 480px;
}

.configurator-rules-list,
.configurator-rule-editor {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
}

.configurator-rules-list > header,
.configurator-rule-editor > header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.configurator-rules-list h4,
.configurator-rule-editor h4 {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

.configurator-exception-data {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
}

.configurator-exception-data legend {
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    padding: 0 4px;
}

.configurator-exception-data label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-secondary);
}

.configurator-strategy-saved {
    font-size: 11px;
    align-self: flex-end;
}

.configurator-panel-subhead {
    margin: 14px 0 8px;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}

/* TransitionDetailPanel: Properties + Secrets sit side by side inside the
   modal, each in its own lightly-bordered card to make the boundary obvious
   without the heavyweight `.configurator-card` chrome (which is meant for
   page-level sections, not inside a dialog). */
.transition-detail-pair {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 14px;
    align-items: start;
}

.transition-detail-card {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background-color: var(--bg-surface);
    padding: 12px 14px 14px;
}

/* The h4 inside each card carries its own top margin from
   `.configurator-panel-subhead`; zero it out so the card padding sets the
   spacing instead. */
.transition-detail-card > .configurator-panel-subhead:first-child {
    margin-top: 0;
}

@media (max-width: 1100px) {
    .configurator-general {
        grid-template-columns: 1fr;
    }
    .configurator-exceptions-grid {
        grid-template-columns: 1fr;
    }
    .configurator-rules-list {
        max-height: 320px;
    }
    /* Same call inside the Transition modal: side-by-side Properties / Secrets
       cards become too tight, so stack them. */
    .transition-detail-pair {
        grid-template-columns: 1fr;
    }
}

/* ─── Maintenance page ────────────────────────────────────────────────────
   Admin → Maintenance: five cleanup tabs (Types, Models, Policies, Forms,
   KPI). The first three are per-row deletion lists with a trash button;
   Forms and KPI are bulk-delete cards centred on a count + a confirm. */

.maintenance-tab {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding-top: 12px;
}

.maintenance-toolbar {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
}

.maintenance-toolbar-info { flex: 1; min-width: 0; }

.maintenance-toolbar-info h3 {
    margin: 0 0 4px 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.maintenance-toolbar-info p {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-muted);
}

/* Per-row table — used by Types, Models, Policies. */
.maintenance-table {
    width: 100%;
    border-collapse: collapse;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.maintenance-table thead th {
    text-align: left;
    padding: 10px 14px;
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    background-color: var(--bg-elevated, var(--bg-surface));
    border-bottom: 1px solid var(--border-subtle);
    font-weight: 600;
}

.maintenance-table tbody td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-subtle);
    font-size: 13px;
    color: var(--text-primary);
    vertical-align: middle;
}

.maintenance-table tbody tr:last-child td { border-bottom: none; }

.maintenance-table tbody tr:hover {
    background-color: var(--bg-hover, rgba(0, 0, 0, 0.02));
}

.maintenance-col-action {
    width: 56px;
    text-align: right;
}

.maintenance-col-desc {
    color: var(--text-muted);
    max-width: 480px;
    overflow: hidden;
    text-overflow: ellipsis;
}

.maintenance-col-num {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Small trash button used inline in the maintenance tables. */
.btn-sm {
    padding: 4px 8px;
    font-size: 12px;
    line-height: 1;
    border-radius: var(--radius-sm);
}

.btn-danger-outline {
    background-color: transparent;
    color: var(--text-muted);
    border: 1px solid var(--border-subtle);
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.btn-danger-outline:hover {
    background-color: var(--danger-soft, rgba(220, 53, 69, 0.1));
    color: var(--danger, #dc3545);
    border-color: var(--danger, #dc3545);
}

.btn-danger-outline:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Bulk-delete card — Forms and KPI share this shape. */
.maintenance-bulk-card {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 18px 20px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.maintenance-kpi-controls {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
}

.maintenance-kpi-date {
    max-width: 200px;
}

.maintenance-bulk-stats {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: 24px;
    row-gap: 6px;
    margin: 0;
    font-size: 13px;
}

.maintenance-bulk-stats dt {
    color: var(--text-muted);
    font-weight: 500;
}

.maintenance-bulk-stats dd {
    margin: 0;
    color: var(--text-primary);
}

.maintenance-bulk-stats-count {
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.maintenance-bulk-actions {
    display: flex;
    justify-content: flex-end;
    padding-top: 4px;
    border-top: 1px solid var(--border-subtle);
}

/* Empty-state icon — used in every tab when nothing's cleanable. */
.maintenance-empty-icon {
    font-size: 20px;
    color: var(--success, #28a745);
    margin-right: 8px;
    vertical-align: middle;
}

@media (max-width: 720px) {
    .maintenance-toolbar { flex-direction: column; align-items: stretch; }
    .maintenance-table { font-size: 12px; }
    .maintenance-col-desc { max-width: none; }
    .maintenance-bulk-stats { grid-template-columns: 1fr; row-gap: 2px; }
    .maintenance-bulk-stats dt { padding-top: 8px; }
}

/* ─── Elicit form page ──────────────────────────────────────────────────── */
/* /tasks page — top-level tab nav. The labels keep the visual weight of the
   previous `<h1>Tasks</h1>` heading, with a subtle underline on the active
   tab and a muted, hover-able treatment on the inactive one so the user can
   tell they're tabs without screaming "tabs!" at them. */

.tasks-tabnav {
    display: flex;
    gap: 6px;
    align-items: flex-end;
    /* Sit on top of the border so the active tab visually "merges" into
       the content area below (classic browser-tab affordance). */
    border-bottom: 1px solid var(--border-emphasis);
    margin-bottom: 1rem;
    padding: 0 0 0 0.25rem;
}
.tasks-tab {
    /* Down from the previous h1-class 2rem — at full size the tab triggers
       were oversized on desktop. 1rem keeps them at body-text scale, which
       reads as "navigation control" rather than "page heading". */
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--text-secondary);
    background: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-bottom: none;
    border-top-left-radius: var(--radius-md);
    border-top-right-radius: var(--radius-md);
    padding: 0.4rem 0.95rem 0.5rem 0.95rem;
    /* Active tab "sits on" the nav's bottom border by pulling itself down
       1px, so its bottom edge overlaps the line. Inactive tabs stay above
       the line and look detached. (Matched to the thinner 1px tabnav border
       above — the previous 2px pull would now leave a visible gap.) */
    margin: 0 0 -1px 0;
    cursor: pointer;
    transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease;
}
.tasks-tab:hover:not(.is-active) {
    color: var(--text-primary);
    background: var(--bg-surface);
    border-color: var(--border-emphasis);
}
/* Active tab: subtle light-blue vertical gradient with a thin accent border
   and dark-blue text for AA contrast against the gradient. The text colour
   is intentionally a fixed hex (not a theme variable) so it always reads
   correctly against the light-blue surface in both light and dark mode —
   dark mode overrides further down swap the gradient + text for darker
   blue + light text. */
.tasks-tab.is-active {
    color: #0d47a1;
    background: linear-gradient(180deg, #e3f2fd 0%, #bbdefb 100%);
    border-color: #90caf9;
    /* Cover the nav's bottom border across the width of this tab so the
       tab "merges" into the content below. Match the gradient's bottom
       colour so the seam is invisible. */
    box-shadow: 0 1px 0 0 #bbdefb;
}
.dark .tasks-tab.is-active {
    color: #bbdefb;
    background: linear-gradient(180deg, #1e3a5f 0%, #143055 100%);
    border-color: #2c5283;
    box-shadow: 0 1px 0 0 #143055;
}
.tasks-tab:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.tasks-tabcontent {
    /* Sits below the merged tab/border line so .tasks-tab.is-active's
       box-shadow has a `--bg-surface` background to "punch through" into.
       The pages inside (Inbox, Processes) bring their own backgrounds. */
}

/* Mobile: only padding / gap need to tighten — the base font + 1px borders
   are now the same as the desktop tab style, so most of the previous
   overrides became redundant. Same 720px breakpoint the Processes pane
   uses for its single-column stack — keeps thresholds consistent. */
@media (max-width: 720px) {
    .tasks-tabnav {
        gap: 2px;
        padding: 0;
    }
    .tasks-tab {
        padding: 0.3rem 0.6rem 0.4rem 0.6rem;
        border-top-left-radius: var(--radius-sm);
        border-top-right-radius: var(--radius-sm);
        min-width: 0;
    }
}

/* Processes tab — two-pane list + detail. Same shape as Types or Realms. */
.processes-shell {
    display: grid;
    grid-template-columns: 320px 1fr;
    gap: 1rem;
    min-height: 60vh;
}
.processes-list {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    overflow: auto;
    max-height: 75vh;
}
.processes-list ul {
    list-style: none;
    margin: 0;
    padding: 0;
}
.processes-list-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: 100%;
    text-align: left;
    padding: 10px 12px;
    border: none;
    border-bottom: 1px solid var(--border-subtle);
    background: transparent;
    cursor: pointer;
    color: var(--text-primary);
    transition: background 0.1s ease;
}
.processes-list-item:hover {
    background: var(--accent-soft);
}
.processes-list-item.is-selected {
    background: var(--accent-soft);
    border-left: 3px solid var(--accent);
    padding-left: 9px;
}
.processes-list-name { font-weight: 600; }
.processes-list-version { color: var(--text-secondary); font-size: 0.8rem; }
.processes-list-description {
    color: var(--text-secondary);
    font-size: 0.8rem;
    margin-top: 2px;
}
.processes-list-input {
    color: var(--text-muted);
    font-size: 0.75rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.processes-detail {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 1.5rem;
    overflow: auto;
}
.processes-detail-header { margin-bottom: 1rem; }
.processes-detail-header h3 { margin: 0 0 0.25rem 0; }
.processes-detail-description { color: var(--text-secondary); margin: 0.25rem 0; }
.processes-detail-meta { color: var(--text-muted); font-size: 0.85rem; }
.processes-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border-subtle);
}
.processes-response {
    margin-top: 1rem;
    background: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.75rem;
}
.processes-response pre {
    margin: 0.5rem 0 0 0;
    max-height: 240px;
    overflow: auto;
    font-size: 0.8rem;
}

/* Mobile-only "back to list" button — desktop layout already has both panels
   visible side-by-side, so the back-button only exists when the layout
   stacks vertically (the @media block below). */
.processes-back-mobile { display: none; }

/* Mobile layout: single column. Showing both panels at once on a narrow
   screen squeezes the detail off the viewport (the bug being fixed here).
   Instead, swap to a focused single-panel view — list when nothing is
   selected, detail when something is. `.has-selection` on the shell is the
   toggle, set by Processes.razor when `selected != null`. */
@media (max-width: 720px) {
    .processes-shell {
        grid-template-columns: 1fr;
        min-height: auto;
    }
    .processes-list {
        max-height: 70vh;
    }
    .processes-shell.has-selection .processes-list {
        display: none;
    }
    .processes-shell:not(.has-selection) .processes-detail {
        display: none;
    }
    .processes-back-mobile {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        margin-bottom: 0.75rem;
        background: transparent;
        border: 1px solid var(--border-subtle);
        color: var(--text-secondary);
        padding: 4px 10px;
        border-radius: var(--radius-sm);
        font-size: 0.9em;
        cursor: pointer;
    }
    .processes-back-mobile:hover {
        background: var(--accent-soft);
        border-color: var(--accent);
        color: var(--accent);
    }
}

/* /elicit/{uuid} — hosted form page backing CircleMCP's request_user_input.
   Two surfaces: the active form (`.elicit-form`) and the terminal states
   (`.elicit-terminal`) shown when the form is submitted / cancelled /
   expired / forbidden.

   The global `html, body { overflow: hidden }` rule above is intentional
   for the main Sphere shell (sidebar + topbar + internal scroll panes),
   but it traps tall elicit forms — the page won't scroll when content
   exceeds the viewport. `:has()` re-enables page-level scroll only when
   an elicit surface is present, leaving the rest of the app unaffected. */

html:has(.elicit-form),
html:has(.elicit-terminal),
body:has(.elicit-form),
body:has(.elicit-terminal) {
    height: auto;
    min-height: 100dvh;
    overflow: auto;
}

.elicit-form {
    max-width: 720px;
    margin: 24px auto;
    padding: 24px;
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.elicit-form-header {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--bs-border-color);
}
.elicit-form-title {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 600;
}
.elicit-form-meta {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    font-size: 0.875rem;
    color: var(--bs-secondary-color);
}
.elicit-form-meta i { margin-right: 4px; }
.elicit-form-description {
    margin-bottom: 16px;
    padding: 12px 16px;
    background: var(--bs-tertiary-bg);
    border-radius: 8px;
    white-space: pre-wrap;
}
.elicit-form-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 20px;
    padding-top: 20px;
    border-top: 1px solid var(--bs-border-color);
}

.elicit-terminal {
    max-width: 480px;
    margin: 64px auto;
    padding: 32px;
    text-align: center;
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.elicit-terminal h2 {
    margin: 16px 0 8px;
    font-size: 1.25rem;
    font-weight: 600;
}
.elicit-terminal p {
    margin: 0;
    color: var(--bs-secondary-color);
    line-height: 1.6;
}
.elicit-terminal-icon {
    font-size: 3rem;
    color: var(--bs-secondary-color);
    display: inline-block;
}
.elicit-terminal-icon-ok {
    color: var(--bs-success, #198754);
}

/* ───────────────────────────────────────────────────────────────────────────
   Rooms (chat) — /tasks → Rooms tab + chat pane.

   The list side reuses .processes-shell / .processes-list / .processes-detail
   from the BluePrint/Processes tabs (mobile-stack, focus-mode, back button —
   all free). Only the chat pane brings its own structure: a vertical grid of
   header / scroll-area / composer. Day separators are inline hr-style markers;
   message bubbles are flex rows that flip alignment for "mine" vs others.
   ─────────────────────────────────────────────────────────────────────────── */

/* Read-only eye icon in the room list */
.room-list-perm {
    margin-left: auto;
    color: var(--bs-secondary-color);
    font-size: 0.875rem;
    opacity: 0.7;
}
.processes-list-item.is-selected .room-list-perm {
    color: inherit;
    opacity: 0.85;
}

/* ─── Chat pane shell ─────────────────────────────────────────────────────── */
.room-chat {
    display: grid;
    grid-template-rows: auto 1fr auto;
    height: 100%;
    min-height: 0;            /* let the grid track-1fr actually scroll */
    gap: 0;
}
.room-chat-header {
    padding: 14px 20px 12px;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-body-bg, #fff);
}
.room-chat-header h3 {
    margin: 0 0 2px;
    font-size: 1.1rem;
    font-weight: 600;
}
.room-chat-header code {
    font-size: 0.8rem;
    background: transparent;
    color: var(--bs-secondary-color);
    padding: 0;
}
.room-chat-desc {
    margin: 6px 0 0;
    color: var(--bs-secondary-color);
    font-size: 0.875rem;
    line-height: 1.4;
}
.room-chat-perm-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 10px;
    padding: 1px 8px;
    border-radius: 10px;
    background: var(--bs-warning-bg-subtle, #fff3cd);
    color: var(--bs-warning-text-emphasis, #664d03);
    font-size: 0.7rem;
    font-weight: 500;
}

/* Header layout: title block on the left, members chip on the right. */
.room-chat-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}
.room-chat-header-main {
    min-width: 0;             /* let the title truncate cleanly if needed */
    flex: 1;
}

/* Members count chip — subtle, secondary action. Click reveals the
   members popover (room-members-dialog below). */
.room-chat-members-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 999px;
    border: 1px solid var(--bs-border-color, #dee2e6);
    background: transparent;
    color: var(--bs-secondary-color);
    font-size: 0.78rem;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
    flex-shrink: 0;
}
.room-chat-members-btn:hover {
    background: var(--bs-tertiary-bg, #f1f3f5);
    color: var(--bs-emphasis-color, #212529);
    border-color: var(--bs-secondary-color);
}
.room-chat-members-btn i {
    font-size: 0.95rem;
}

/* Members popover dialog — uses the existing .confirm-overlay / .confirm-dialog
   chrome from Apps.razor / RoomsTab.razor so the look matches other modals. */
.room-members-dialog {
    max-width: 380px;
}
.room-members-dialog h3 {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
}
.room-members-subtitle {
    margin: 0 0 14px;
    font-size: 0.82rem;
}
.room-members-list {
    list-style: none;
    margin: 0 0 14px;
    padding: 0;
    max-height: 50vh;
    overflow-y: auto;
}
.room-members-list li {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 4px;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}
.room-members-list li:last-child {
    border-bottom: none;
}
.room-members-list li .avatar {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--bs-primary, #0d6efd);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.85rem;
}
.room-members-list li.is-me .avatar {
    background: var(--bs-success, #198754);
}
.room-members-name {
    flex: 1;
    min-width: 0;
    font-weight: 500;
    color: var(--bs-body-color);
}
.room-members-self {
    margin-left: 6px;
    font-size: 0.7rem;
    font-weight: 400;
    color: var(--bs-secondary-color);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.room-members-perm {
    flex-shrink: 0;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 500;
    line-height: 1.5;
}
.room-members-perm--write {
    background: var(--bs-primary-bg-subtle, #cfe2ff);
    color: var(--bs-primary-text-emphasis, #052c65);
}
.room-members-perm--read {
    background: var(--bs-secondary-bg-subtle, #e9ecef);
    color: var(--bs-secondary-color, #6c757d);
}

/* ─── Message stream ─────────────────────────────────────────────────────── */
.room-chat-messages {
    overflow-y: auto;
    padding: 16px 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    background: var(--bs-tertiary-bg, #f8f9fa);
}
.room-chat-loadmore {
    align-self: center;
    margin-bottom: 8px;
}

/* Day separator (today, yesterday, weekday, date) */
.room-chat-daysep {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 8px 0;
    position: relative;
}
.room-chat-daysep::before,
.room-chat-daysep::after {
    content: "";
    flex: 1;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    opacity: 0.6;
}
.room-chat-daysep span {
    padding: 0 12px;
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Message bubble */
.room-chat-bubble {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    max-width: 80%;
}
.room-chat-bubble.is-mine {
    align-self: flex-end;
    flex-direction: row-reverse;
}
.room-chat-bubble .avatar {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--bs-primary, #0d6efd);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.85rem;
}
.room-chat-bubble-inner {
    background: #fff;
    border-radius: 12px;
    padding: 8px 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
    min-width: 0;
}
.room-chat-bubble.is-mine .room-chat-bubble-inner {
    background: var(--bs-primary, #0d6efd);
    color: #fff;
}
.room-chat-bubble.is-mine .room-chat-bubble-inner a {
    color: #fff;
    text-decoration: underline;
}
.room-chat-meta {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 2px;
    font-size: 0.78rem;
    color: var(--bs-secondary-color);
}
.room-chat-bubble.is-mine .room-chat-meta {
    color: rgba(255, 255, 255, 0.85);
}
.room-chat-meta strong {
    color: var(--bs-emphasis-color, #212529);
    font-weight: 600;
}
.room-chat-meta-mine {
    justify-content: flex-end;
    margin-top: 2px;
    margin-bottom: 0;
}
.room-chat-time {
    opacity: 0.8;
}
.room-chat-body {
    white-space: pre-wrap;       /* preserve newlines from the textarea */
    word-wrap: break-word;
    overflow-wrap: anywhere;     /* break long unbreakable strings (URLs, etc) */
    line-height: 1.45;
}

/* ─── Image attachments ──────────────────────────────────────────────────── */
.room-chat-image-link {
    display: block;
    line-height: 0;              /* kill the inline-image baseline gap */
}
.room-chat-image {
    max-width: 320px;
    max-height: 320px;
    width: auto;
    height: auto;
    border-radius: 8px;
    display: block;
    cursor: zoom-in;
}
.room-chat-bubble-inner:has(.room-chat-image) {
    /* When the bubble contains an image, the padding feels heavy. Pull it
       in so the image sits flush. The caption (if any) restores its own
       breathing room via the .room-chat-body margin below. */
    padding: 4px;
}
.room-chat-bubble-inner:has(.room-chat-image) .room-chat-body {
    padding: 4px 8px 6px;
    margin: 0;
}

/* ─── Composer: paperclip + pending image preview ───────────────────────── */
.room-chat-attach-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 8px;
    color: var(--bs-secondary-color);
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background 0.1s ease, color 0.1s ease;
    flex-shrink: 0;
    align-self: flex-end;
    margin-bottom: 2px;
    font-size: 1.1rem;
    position: relative;
    overflow: hidden;          /* clip the file input's native chrome */
}
.room-chat-attach-btn:hover {
    background: var(--bs-tertiary-bg, #f1f3f5);
    color: var(--bs-emphasis-color, #212529);
}
.room-chat-attach-btn.is-busy {
    color: var(--bs-primary, #0d6efd);
}
/* Blazor's <InputFile> renders a vanilla <input type="file"> as a child of
   the <label> here — no `::deep` (this is global CSS, not scoped razor.css).
   Stretch the input over the label, make it invisible + interactive, and
   ALSO hard-hide its native button + "no file chosen" text via the standard
   pseudo-elements every browser uses for that chrome. Belt + braces because
   not every browser respects opacity:0 the same way for hit-testing. */
.room-chat-attach-btn input[type="file"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    /* Strip the native button + label text — Safari/Chrome use a
       ::-webkit-file-upload-button pseudo, Firefox uses ::file-selector-button,
       and the trailing "No file chosen" comes from the input's own intrinsic
       text. Sizing it 0×0 and color: transparent kills both. */
    font-size: 0;
    color: transparent;
}
.room-chat-attach-btn input[type="file"]::-webkit-file-upload-button {
    display: none;
}
.room-chat-attach-btn input[type="file"]::file-selector-button {
    display: none;
}

/* Pending-image preview strip — sits above the composer while an image is
   selected and not yet sent. Caption can be typed underneath; clicking the
   X clears the pending image without sending. */
.room-chat-pending-image {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-tertiary-bg, #f8f9fa);
}
.room-chat-pending-image-thumb {
    width: 48px;
    height: 48px;
    object-fit: cover;
    border-radius: 6px;
}
.room-chat-pending-image-meta {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 0.85rem;
    color: var(--bs-secondary-color);
}
.room-chat-body a {
    color: var(--bs-link-color, #0d6efd);
    text-decoration: underline;
}

/* ─── Replies (v1: flat, inline-quoted, click-to-jump) ──────────────────── */

/* Quoted parent above the reply body. Left bar + slightly indented to read
   as a quote at a glance. Clicking calls RoomChat.flashScrollTo on the
   parent bubble. */
.room-chat-quote {
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: 0;
    padding: 4px 8px;
    margin: 0 0 6px 0;
    border-left: 3px solid var(--bs-primary, #0d6efd);
    border-radius: 4px;
    cursor: pointer;
    color: inherit;
    font-size: 0.82rem;
    line-height: 1.35;
    opacity: 0.85;
    transition: background-color 0.15s ease, opacity 0.15s ease;
}
.room-chat-quote:hover { opacity: 1; background: rgba(13, 110, 253, 0.08); }
.room-chat-quote-sender {
    display: block;
    font-weight: 600;
    color: var(--bs-primary, #0d6efd);
    margin-bottom: 1px;
}
.room-chat-bubble.is-mine .room-chat-quote {
    border-left-color: rgba(255, 255, 255, 0.7);
}
.room-chat-bubble.is-mine .room-chat-quote:hover {
    background: rgba(255, 255, 255, 0.1);
}
.room-chat-bubble.is-mine .room-chat-quote-sender {
    color: #fff;
}
.room-chat-quote-body {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.room-chat-quote-removed { font-style: italic; opacity: 0.7; }

/* Per-bubble Reply affordance — hidden until the bubble is hovered. Sits
   outside the inner bubble box so it doesn't visually compete with the
   message body. Stays visible on touch / when focused for a11y. */
.room-chat-bubble-action {
    align-self: center;
    border: 0;
    background: transparent;
    color: var(--bs-secondary-color);
    padding: 4px 6px;
    border-radius: 6px;
    opacity: 0;
    transition: opacity 0.15s ease, background-color 0.15s ease;
    font-size: 0.95rem;
    line-height: 1;
}
.room-chat-bubble:hover .room-chat-bubble-action,
.room-chat-bubble:focus-within .room-chat-bubble-action {
    opacity: 1;
}
.room-chat-bubble-action:hover {
    background: rgba(13, 110, 253, 0.1);
    color: var(--bs-primary, #0d6efd);
}

/* Composer reply chip — mirrors the pending-image strip styling so the
   composer head is visually coherent whether you've attached an image,
   started a reply, or both. */
.room-chat-reply-chip {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 12px;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-tertiary-bg, #f8f9fa);
    border-left: 3px solid var(--bs-primary, #0d6efd);
}
.room-chat-reply-chip-icon {
    color: var(--bs-primary, #0d6efd);
    font-size: 1rem;
}
.room-chat-reply-chip-body {
    flex: 1;
    min-width: 0;
    text-align: left;
    border: 0;
    background: transparent;
    padding: 0;
    cursor: pointer;
    overflow: hidden;
}
.room-chat-reply-chip-label {
    display: block;
    font-size: 0.78rem;
    color: var(--bs-secondary-color);
}
.room-chat-reply-chip-snippet {
    display: block;
    font-size: 0.85rem;
    color: var(--bs-body-color);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Flash highlight applied by RoomChat.flashScrollTo when the user clicks a
   quote or the composer reply chip to jump to the parent. Subtle outline +
   background pulse; fades out via the 1.4s setTimeout in the JS helper. */
.room-chat-bubble.is-flash .room-chat-bubble-inner {
    animation: room-chat-flash 1.4s ease-out;
    box-shadow: 0 0 0 2px var(--bs-primary, #0d6efd);
}
@keyframes room-chat-flash {
    0%   { box-shadow: 0 0 0 2px var(--bs-primary, #0d6efd); background: rgba(13, 110, 253, 0.15); }
    100% { box-shadow: 0 0 0 2px transparent; background: inherit; }
}

/* ─── Composer (sticky bottom) ───────────────────────────────────────────── */
.room-chat-composer {
    display: flex;
    gap: 8px;
    padding: 12px 20px;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-body-bg, #fff);
}
.room-chat-composer textarea {
    resize: none;
    min-height: 44px;
    max-height: 200px;
}
.room-chat-composer button[type="submit"] {
    flex: 0 0 auto;
    align-self: flex-end;
    padding: 8px 14px;
}
.room-chat-composer-error {
    padding: 0 20px 8px;
    background: var(--bs-body-bg, #fff);
}
.room-chat-readonly-hint {
    padding: 12px 20px;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-tertiary-bg, #f8f9fa);
    color: var(--bs-secondary-color);
    font-size: 0.875rem;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Dark-mode tweaks — bubble background follows the body bg / tertiary bg
   shifts Bootstrap already applies. The blue "mine" bubble stays blue. */
@media (prefers-color-scheme: dark) {
    .room-chat-bubble:not(.is-mine) .room-chat-bubble-inner {
        background: var(--bs-secondary-bg, #2b3035);
        color: var(--bs-body-color, #dee2e6);
    }
}
html.dark .room-chat-bubble:not(.is-mine) .room-chat-bubble-inner {
    background: var(--bs-secondary-bg, #2b3035);
    color: var(--bs-body-color, #dee2e6);
}

/* ─── Bound the chat container on desktop so the message stream — not the
   page — is what scrolls.
   ────────────────────────────────────────────────────────────────────────────
   `.room-chat` is `display: grid; grid-template-rows: auto 1fr auto; height:
   100%`. The middle row (`.room-chat-messages`) takes the remaining space
   and scrolls internally, BUT that only works when `.room-chat`'s parent
   (`.processes-detail`) has a definite height. Without this rule, the
   detail pane is `overflow: auto` and grows with content — the chat then
   has no fixed height, the `1fr` row doesn't constrain, and the whole
   /tasks page scrolls instead of the message list.

   Chrome budget below for desktop: topbar (~50px) + page padding (~40px)
   + tasks tab nav (~50px) + bottom breathing room (~20px) ≈ 160px reserved
   above the chat. `min-height: 500px` keeps the experience usable on
   short screens (laptop docked windows, etc.). The mobile media query
   further down restages this with `calc(100dvh - 130px)` for phones.

   `:has()` carries this without a Razor change — we already require modern
   browsers for the Web Push feature this all lives inside. */
.processes-detail:has(.room-chat) {
    padding: 0;
    border: none;
    background: transparent;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    height: calc(100dvh - 160px);
    min-height: 500px;
    max-height: none;
}
.processes-detail:has(.room-chat) .room-chat {
    flex: 1;
    min-height: 0;
}

/* ─── Mobile: full-screen chat experience ───────────────────────────────────
   On mobile when a room is selected, the chat should BE the page — no
   processes-detail padding, no stretched-out card chrome, no wasted
   space above the messages. The .processes-shell already swaps to
   single-column at this breakpoint; we further escalate the chat-pane
   case so it fills the viewport below the tasks tab nav and the
   composer anchors to the bottom (respecting iOS safe-area-inset).

   :has() carries this so we don't need a Razor change to add a special
   class — supported on every browser that supports modern Web Push
   (which we already require for the feature this lives inside). */
@media (max-width: 720px) {
    /* Step 1: tighten the chat-container height on mobile.
       Mobile chrome budget: ~50px topbar + ~48px tasks tab nav + ~32px
       page padding ≈ 130px (less than desktop because there's no h1 page
       header at this breakpoint). min-height drops to 0 — phones can be
       very short and any fixed min would push the composer off-screen. */
    .processes-detail:has(.room-chat) {
        height: calc(100dvh - 130px);
        min-height: 0;
    }
    .processes-shell.has-selection:has(.room-chat) {
        min-height: 0;
        height: auto;
    }
    /* The mobile back-to-list button moves inside the chat header so it
       doesn't get pushed off-screen by the fill-the-viewport logic above. */
    .processes-back-mobile { margin-bottom: 0; }

    .room-chat {
        background: var(--bs-body-bg, #fff);
        border-radius: 0;
    }

    /* Step 2: compact header. Slug + description hidden — name + perm pill
       is what matters at chat-time; slug lives in the URL if anyone needs
       it, and the description is in the admin tab. */
    .room-chat-header {
        padding: 8px 12px;
        flex-shrink: 0;
        border-bottom: 1px solid var(--bs-border-color, #dee2e6);
    }
    .room-chat-header h3 {
        font-size: 0.98rem;
        line-height: 1.2;
        font-weight: 600;
    }
    .room-chat-header code,
    .room-chat-desc {
        display: none;
    }
    .room-chat-perm-pill {
        margin-left: 0;
        margin-top: 2px;
        display: inline-flex;
    }
    /* Mobile: members chip stays in the header (icon + count only — slim). */
    .room-chat-members-btn {
        padding: 4px 8px;
        font-size: 0.72rem;
    }
    .room-members-dialog {
        max-width: calc(100vw - 24px);
    }

    /* Step 3: message stream. Bigger bubbles, smaller avatars, tighter
       gap — feels much more like a native chat app. */
    .room-chat-messages {
        padding: 12px 10px;
        gap: 6px;
        -webkit-overflow-scrolling: touch;  /* momentum scrolling on iOS */
    }
    .room-chat-bubble { max-width: 88%; }
    .room-chat-bubble .avatar {
        flex: 0 0 28px;
        width: 28px;
        height: 28px;
        font-size: 0.78rem;
    }
    .room-chat-bubble-inner {
        padding: 7px 11px;
        border-radius: 14px;
    }
    .room-chat-body {
        font-size: 0.95rem;
        line-height: 1.4;
    }
    .room-chat-meta {
        font-size: 0.7rem;
        margin-bottom: 1px;
    }
    .room-chat-time { font-size: 0.68rem; }

    .room-chat-daysep { margin: 6px 0; }
    .room-chat-daysep span {
        padding: 0 10px;
        font-size: 0.65rem;
    }

    /* Step 4: composer. font-size:16px prevents iOS auto-zoom on focus
       (Safari only zooms when the focused input is below the user's
       preferred minimum text size). 44×44 tap target on the send button
       hits Apple's HIG minimum. Safe-area-inset keeps the composer above
       the iPhone home indicator instead of being half-eaten by it. */
    .room-chat-composer {
        padding: 8px 10px;
        padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
        gap: 6px;
        flex-shrink: 0;
        border-top: 1px solid var(--bs-border-color, #dee2e6);
    }
    .room-chat-composer textarea {
        min-height: 40px;
        max-height: 120px;
        font-size: 16px;            /* iOS no-zoom threshold */
        padding: 8px 10px;
        border-radius: 8px;
    }
    .room-chat-composer button[type="submit"] {
        min-width: 44px;
        min-height: 44px;
        padding: 0;
        font-size: 1.1rem;
    }
    .room-chat-attach-btn {
        width: 44px;
        height: 44px;
        font-size: 1.2rem;
    }
    .room-chat-image {
        max-width: min(80vw, 280px);
        max-height: 280px;
    }
    .room-chat-pending-image {
        padding-left: 10px;
        padding-right: 10px;
    }
    .room-chat-composer-error {
        padding: 0 12px 6px;
    }
    .room-chat-readonly-hint {
        padding: 10px 12px;
        padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
        font-size: 0.82rem;
        line-height: 1.4;
    }

    /* Step 5: room-list rhythm on mobile — bigger tap targets, less side
       padding so the list-name has more horizontal room. */
    .processes-shell:not(.has-selection) .processes-list-item {
        padding: 14px 14px;
        min-height: 56px;
    }
    .room-list-perm {
        margin-left: auto;
        font-size: 1rem;
    }
}

/* Very small phones (≤480 px): tighten one more notch. */
@media (max-width: 480px) {
    .room-chat-bubble { max-width: 92%; }
    .room-chat-header h3 { font-size: 0.92rem; }
    .room-chat-messages { padding: 10px 8px; }
    .room-chat-composer { padding: 8px 8px; padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px)); }
    .room-chat-bubble-inner { padding: 6px 9px; }
}

/* ─── Contextual help (modal) ────────────────────────────────────────────── */
/* Triggered by the `?` icon in the topbar (MainLayout.razor → #helpBtn).
   Pure JS, driven by /js/help.js — no Blazor rendermode involvement. The
   modal renders the contents of /help/<key>.html into the .help-dialog__body
   panel. Styling aims for an enterprise feel: generous spacing, restrained
   accent stripe, sticky header so deep stubs stay oriented. */

.help-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.55);
    backdrop-filter: blur(2px);
    display: none;
    align-items: flex-start;
    justify-content: center;
    z-index: 10500;
    padding: clamp(24px, 5vh, 80px) 16px;
    overflow-y: auto;
}

.help-overlay.is-visible {
    display: flex;
    animation: helpFadeIn 140ms ease-out both;
}

@keyframes helpFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.help-dialog {
    position: relative;
    width: min(780px, 100%);
    background-color: var(--bg-surface);
    color: var(--text-primary);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow:
        0 24px 48px -12px rgba(15, 23, 42, 0.25),
        0 8px 16px -4px rgba(15, 23, 42, 0.12);
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - clamp(48px, 10vh, 160px));
    overflow: hidden;
    animation: helpDialogIn 180ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes helpDialogIn {
    from { transform: translateY(8px); opacity: 0; }
    to   { transform: translateY(0);   opacity: 1; }
}

/* Top accent stripe — subtle brand line that separates the header from the
   chrome above. Tasteful, not loud. */
.help-dialog::before {
    content: "";
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 3px;
    background: linear-gradient(90deg, var(--accent) 0%, #818cf8 100%);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.help-dialog__header {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
        "eyebrow close"
        "title   close";
    align-items: center;
    gap: 4px 12px;
    padding: 22px 28px 16px;
    border-bottom: 1px solid var(--border-subtle);
    background-color: var(--bg-surface);
    flex-shrink: 0;
}

.help-dialog__eyebrow {
    grid-area: eyebrow;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--accent);
}

.help-dialog__title {
    grid-area: title;
    margin: 0;
    font-size: 1.35rem;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.25;
}

.help-dialog__close {
    grid-area: close;
    align-self: start;
    width: 32px;
    height: 32px;
    border: 1px solid var(--border-subtle);
    background: transparent;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}

.help-dialog__close:hover,
.help-dialog__close:focus-visible {
    color: var(--text-primary);
    border-color: var(--border-emphasis);
    background-color: var(--bg-canvas);
    outline: none;
}

/* Banner — used when a stub is missing or the page key is unmatched. */
.help-dialog__banner {
    padding: 10px 28px;
    background-color: #fff7ed;
    color: #9a3412;
    border-bottom: 1px solid #fed7aa;
    font-size: 0.85rem;
}

.help-dialog__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 22px 28px 28px;
    line-height: 1.6;
    color: var(--text-secondary);
    outline: none;
}

.help-dialog__loading {
    padding: 32px;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.9rem;
}

/* Typography inside stubs — kept scoped so authoring HTML stays clean.
   Each stub provides h2 (page name), h3 (sections), p, ul/ol, code, a. */

.help-dialog__body h2 {
    margin: 0 0 6px;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-primary);
    /* Stubs' <h2> is hoisted into the dialog title by help.js so the dialog
       header reflects the page. We hide it inline to avoid duplicate titles. */
    display: none;
}

.help-dialog__body .help-lead {
    font-size: 1rem;
    color: var(--text-primary);
    margin: 0 0 20px;
    padding-bottom: 16px;
    border-bottom: 1px dashed var(--border-subtle);
}

.help-dialog__body h3 {
    margin: 22px 0 8px;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.help-dialog__body h3:first-of-type {
    margin-top: 4px;
}

.help-dialog__body p {
    margin: 0 0 14px;
}

.help-dialog__body ul,
.help-dialog__body ol {
    margin: 0 0 14px;
    padding-left: 18px;
}

.help-dialog__body li {
    margin-bottom: 6px;
}

.help-dialog__body li::marker {
    color: var(--accent);
}

.help-dialog__body code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 0.85em;
    padding: 1px 6px;
    background-color: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: 4px;
    color: var(--text-primary);
}

.help-dialog__body a {
    color: var(--accent);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color 120ms ease;
}

.help-dialog__body a:hover,
.help-dialog__body a:focus-visible {
    border-bottom-color: var(--accent);
    outline: none;
}

/* "Related" link list — visual delineation from regular bullet content. */
.help-dialog__body .help-related {
    list-style: none;
    padding-left: 0;
    display: grid;
    gap: 6px;
}

.help-dialog__body .help-related li {
    margin: 0;
}

.help-dialog__body .help-related a {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    background-color: var(--bg-canvas);
    border-bottom: 1px solid var(--border-subtle);
}

.help-dialog__body .help-related a::before {
    content: "→";
    color: var(--accent);
    font-weight: 700;
}

.help-dialog__body .help-related a:hover,
.help-dialog__body .help-related a:focus-visible {
    border-color: var(--accent);
    background-color: var(--accent-soft);
}

/* Footer — page key (for support tickets) + close hint */
.help-dialog__footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 28px;
    background-color: var(--bg-canvas);
    border-top: 1px solid var(--border-subtle);
    font-size: 11px;
    color: var(--text-muted);
    letter-spacing: 0.04em;
    flex-shrink: 0;
}

.help-dialog__key {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
}

/* Body scroll lock while the help modal is open. */
body.help-open {
    overflow: hidden;
}

/* Dark-mode tuning — the variables already swap, we only need to override
   the banner palette and the dialog overlay tint. */
.dark .help-overlay {
    background: rgba(2, 6, 23, 0.65);
}

.dark .help-dialog__banner {
    background-color: rgba(154, 52, 18, 0.18);
    color: #fdba74;
    border-bottom-color: rgba(254, 215, 170, 0.2);
}

@media (max-width: 640px) {
    .help-overlay { padding: 12px; align-items: flex-end; }
    .help-dialog { max-height: 92vh; border-radius: var(--radius-md); }
    .help-dialog__header { padding: 18px 20px 12px; }
    .help-dialog__body   { padding: 18px 20px 24px; }
    .help-dialog__footer { padding: 10px 20px; }
}

/* ─── Notes tab ─────────────────────────────────────────────────────────── */
/* List rail on the left, viewer/editor on the right — same shape as the
   Processes tab. Quill editor mounts into .notes-quill on edit. */
.notes-shell {
    display: grid;
    grid-template-columns: 300px 1fr;
    gap: 1rem;
    min-height: 60vh;
}

.notes-list {
    display: flex;
    flex-direction: column;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
    min-height: 0;
}
.notes-list-toolbar {
    display: flex;
    gap: 6px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border-subtle);
    align-items: center;
}
.notes-list-toolbar .notes-new { flex: 1; }
.notes-list-toolbar .notes-refresh {
    padding: 6px 10px;
    color: var(--text-secondary);
}
.notes-list-filter {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border-subtle);
}
.notes-list-filter .sphere-searchable-select {
    width: 100%;
}
.notes-items {
    list-style: none;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
}
.notes-item {
    padding: 6px 12px;
    border-bottom: 1px solid var(--border-subtle);
    cursor: pointer;
    transition: background-color 100ms ease, border-color 100ms ease;
    display: flex;
    flex-direction: column;
    gap: 1px;
    line-height: 1.25;
}
.notes-item:hover {
    background-color: var(--bg-canvas);
}
.notes-item.is-active {
    background-color: var(--accent-soft);
    border-left: 3px solid var(--accent);
    padding-left: 9px;        /* compensate for the 3px left border */
}
.notes-item-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.notes-item-meta {
    font-size: 10px;
    color: var(--text-muted);
}

/* Namespace grouping in the list rail.
   Root ("") notes render with no header. Any non-root namespace gets a tiny
   monospace header above its block of notes — same visual weight as a section
   divider, not a button. */
.notes-group-header {
    list-style: none;
    padding: 8px 12px 4px;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: var(--text-muted);
    background-color: var(--bg-canvas);
    border-bottom: 1px solid var(--border-subtle);
    font-family: var(--font-mono, monospace);
}

/* Namespace badge shown next to the title in the read-only viewer.
   Same gray-pill aesthetic as the "historical" marking badge so the two
   feel related. */
.notes-ns-chip {
    display: inline-block;
    margin-left: 8px;
    padding: 2px 8px;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: var(--text-muted);
    background-color: var(--bg-canvas);
    border-radius: 8px;
    vertical-align: middle;
}

/* Namespace input row in the editor — sits between the title row and the
   Quill mount. Single input, monospace placeholder to hint at structured
   strings like "platform/dw". */
.notes-ns-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 6px;
}
.notes-ns-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin: 0;
    min-width: 80px;
}
.notes-ns-input {
    flex: 1;
    font-family: var(--font-mono, monospace);
    font-size: 12px;
}
.notes-empty {
    padding: 24px 16px;
    text-align: center;
    color: var(--text-muted);
    font-size: 13px;
}

.notes-view {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 18px 22px;
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;        /* lets the inner Quill resize properly */
}
.notes-pick {
    padding: 48px 16px;
    text-align: center;
    color: var(--text-muted);
}

.notes-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border-subtle);
    flex-wrap: wrap;
}
.notes-title {
    font-size: 1.4rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
    line-height: 1.25;
    word-break: break-word;
    flex: 1 1 0;
    min-width: 0;
}
.notes-title-input {
    flex: 1 1 0;
    min-width: 0;
    font-size: 1.1rem;
    font-weight: 600;
}
.notes-header-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}
.notes-header-actions .btn-danger-soft {
    background: transparent;
    border: 1px solid var(--border-subtle);
    color: #c0392b;
}
.notes-header-actions .btn-danger-soft:hover {
    background-color: rgba(192, 57, 43, 0.08);
    border-color: #c0392b;
}

/* Mobile: stack the header so action buttons sit ABOVE the title row
   (and thus above the editor / read-view body). `order: -1` puts the
   actions first inside the column-flex header even though they come
   last in the DOM, so the visual order at the mobile breakpoint
   becomes: actions → title (or title input) → namespace row →
   Quill / body.
   `flex-wrap: nowrap` is non-negotiable here — the desktop rule sets
   `flex-wrap: wrap`, and when combined with `flex-direction: column`
   the browser builds *additional columns to the right* once content
   exceeds the available height, pushing the title off-canvas to the
   right of the actions instead of below them. */
@media (max-width: 720px) {
    .notes-header {
        flex-direction: column;
        align-items: stretch;
        gap: 8px;
        flex-wrap: nowrap;
    }
    .notes-header-actions {
        order: -1;
        flex-wrap: wrap;
        justify-content: flex-start;
    }
    /* Both the view-mode <h2> and the edit-mode <input> ship with
       `flex: 1 1 0` from the desktop rule. In a column-flex parent that
       sizes them on the wrong axis and the input's intrinsic width can
       still spill over the viewport. Pinning width: 100% + border-box
       keeps both inside their column; word-break on the h2 wraps long
       titles onto multiple lines instead of horizontal scrolling. */
    .notes-title,
    .notes-title-input {
        width: 100%;
        min-width: 0;
        max-width: 100%;
        box-sizing: border-box;
    }
    .notes-title {
        word-break: break-word;
    }
}

/* Quill mount slot — give it a comfortable min-height so the editor doesn't
   collapse into a one-line strip before content loads. Quill's `.ql-container`
   already grows to fit content; we set the floor only. */
.notes-quill {
    min-height: 320px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    margin-bottom: 12px;
}

/* Read view — the rendered HTML body of a saved note. Constrain widths and
   make typography read as enterprise content (not a code block). */
.notes-body-view {
    flex: 1 1 auto;
    overflow-y: auto;
    line-height: 1.6;
    color: var(--text-primary);
    word-wrap: break-word;
    padding-bottom: 12px;
}
.notes-body-view h1,
.notes-body-view h2,
.notes-body-view h3 {
    color: var(--text-primary);
    margin-top: 1em;
    margin-bottom: 0.4em;
}
.notes-body-view p { margin: 0 0 0.8em; }
.notes-body-view ul,
.notes-body-view ol { margin: 0 0 0.8em; padding-left: 1.4em; }
.notes-body-view code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
    font-size: 0.9em;
    background-color: var(--bg-canvas);
    padding: 1px 6px;
    border-radius: 4px;
    border: 1px solid var(--border-subtle);
}
.notes-body-view a { color: var(--accent); }
.notes-body-view blockquote {
    margin: 0 0 0.8em;
    padding-left: 14px;
    border-left: 3px solid var(--accent);
    color: var(--text-secondary);
}
.notes-empty-body {
    color: var(--text-muted);
    font-style: italic;
}

/* AI-assist card — deliberately understated: sits between the note header and
   body on a secondary surface, no strong accent until the Run button. Hidden
   until the user clicks the header's AI toggle. */
.notes-ai-toggle.is-active {
    border-color: var(--accent);
    color: var(--accent);
}

.notes-ai-card {
    margin: 4px 0 12px;
    padding: 12px;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md, 8px);
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.notes-ai-prompt {
    width: 100%;
    resize: vertical;
    min-height: 60px;
}

.notes-ai-controls {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.notes-ai-controls .sphere-searchable-select {
    flex: 1 1 200px;
    min-width: 160px;
}

.notes-ai-submit {
    flex: 0 0 auto;
    white-space: nowrap;
}

.notes-meta-row {
    margin-top: 8px;
    font-size: 11px;
    color: var(--text-muted);
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.notes-share-modal {
    max-width: 520px;
}

@media (max-width: 900px) {
    .notes-shell {
        grid-template-columns: 1fr;
    }
    .notes-list {
        max-height: 240px;
    }
}

/* Narrow viewport: the action trio (Edit / Share / Delete) was crowding the
   title because `.notes-title` had `flex: 1 1 0` and would shrink to zero
   before forcing a wrap. Force the actions onto their own row so the title
   keeps the full width on phones. Same rule applies to the edit-mode header
   so the title input doesn't get squeezed against the Save/Cancel buttons. */
@media (max-width: 640px) {
    .notes-header {
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
    }
    .notes-title,
    .notes-title-input {
        flex: 1 1 100%;
    }
    .notes-header-actions {
        justify-content: flex-end;
    }
    .notes-view {
        padding: 14px 16px;
    }
}



/* ─── Cloud Admin (/admin/cluster) ───────────────────────────────────────── */

.cloud-admin-shell {
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-height: 0;
}

/* Migrate-tab placeholder. Slightly larger empty state than the default
   `.page-empty` so the "this feature is intentional, just not built yet"
   message reads cleanly instead of looking like a missing UI. */
.cloud-admin-migrate-stub {
    padding: 48px 16px;
    text-align: center;
}
.cloud-admin-migrate-icon {
    font-size: 2.4rem;
    color: var(--accent);
    margin-bottom: 12px;
}
.cloud-admin-migrate-stub h3 {
    margin: 0 0 6px;
    color: var(--text-primary);
}
.cloud-admin-migrate-stub p {
    color: var(--text-secondary);
    max-width: 540px;
    margin: 0 auto;
    line-height: 1.5;
}

/* Cluster-tab toolbar + summary tiles. Phase-3 minimal UI; tables and
   donuts arrive in Phase 4 and will sit underneath the summary row. */
.cluster-tab {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-height: 0;
}
.cluster-tab-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}
.cluster-refresh-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.cluster-refresh-spin {
    animation: cluster-refresh-spin 0.8s linear infinite;
}
@keyframes cluster-refresh-spin {
    to { transform: rotate(360deg); }
}
.cluster-tab-stamp {
    color: var(--text-muted);
    font-size: 12.5px;
    font-feature-settings: 'tnum';
}
.cluster-tab-banner {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0;
}
.cluster-tab-banner i { font-size: 1rem; margin-top: 2px; }

.cluster-tab-summary {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
}
.cluster-tab-summary-tile {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px 16px;
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}
.cluster-tab-summary-label {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}
.cluster-tab-summary-value {
    font-size: 1.4rem;
    font-weight: 600;
    color: var(--text-primary);
    font-feature-settings: 'tnum';
}
.cluster-tab-phase-note {
    margin: 4px 0 0;
    font-size: 13px;
    font-style: italic;
}

/* ─── Cluster tab — tables + section heads + usage bars + pills ──────────── */

.cluster-section {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}
.cluster-section-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 4px 0 2px;
}
.cluster-section-head h3 {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-primary);
}
.cluster-section-count {
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 12px;
    font-weight: 600;
    font-feature-settings: 'tnum';
}
.cluster-section-empty {
    padding: 24px 12px;
    text-align: center;
    color: var(--text-muted);
}

/* Tables inherit .data-table base; add cluster-specific row padding tweaks
   so the inline progress bars on Nodes don't blow the row height up. */
.cluster-nodes-table td,
.cluster-pods-table td {
    vertical-align: middle;
}

/* Node usage bars — compact, single-row, label sits to the right of the
   bar so the operator can glance at percentage WITHOUT having to read the
   numbers. Tone colour shifts at 60/85% (mid → high) so saturated nodes
   are visually obvious before you read the number. */
.cluster-usage-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.cluster-bar-track {
    flex: 1 1 auto;
    height: 6px;
    background: var(--bg-canvas);
    border: 1px solid var(--border-subtle);
    border-radius: 999px;
    overflow: hidden;
    min-width: 60px;
}
.cluster-bar-fill {
    height: 100%;
    border-radius: 999px;
    transition: width 250ms ease;
}
.cluster-bar-fill-low  { background: #10b981; }   /* emerald */
.cluster-bar-fill-mid  { background: #f59e0b; }   /* amber */
.cluster-bar-fill-high { background: var(--danger); }
.cluster-bar-label {
    flex: 0 0 auto;
    font-size: 12px;
    color: var(--text-secondary);
    font-feature-settings: 'tnum';
    white-space: nowrap;
}

/* Pod phase pill — small coloured tag, matching the kube vocabulary the
   operator already has in their head from `kubectl get pods`. */
.cluster-phase-pill {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.cluster-phase-running   { background: rgba(16, 185, 129, 0.18); color: #34d399; }
.cluster-phase-pending   { background: rgba(245, 158, 11, 0.18); color: #fcd34d; }
.cluster-phase-succeeded { background: rgba(129, 140, 248, 0.18); color: #a5b4fc; }
.cluster-phase-failed    { background: rgba(239, 68, 68, 0.18);  color: #fca5a5; }
.cluster-phase-unknown   { background: rgba(148, 163, 184, 0.18); color: #94a3b8; }

/* Node status pill — same shape as phase pill. Ready=green, NotReady=red,
   Unknown=gray. Matches `kubectl get nodes` STATUS column. */
.cluster-status-pill {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 600;
}
.cluster-status-ready    { background: rgba(16, 185, 129, 0.18); color: #34d399; }
.cluster-status-notready { background: rgba(239, 68, 68, 0.18);  color: #fca5a5; }
.cluster-status-unknown  { background: rgba(148, 163, 184, 0.18); color: #94a3b8; }

/* Restart count > 0 — pull the eye. Subtle, not alarming. */
.cluster-restart-warn {
    color: var(--danger);
    font-weight: 600;
    font-feature-settings: 'tnum';
}

/* ─── Cluster tab donut row ──────────────────────────────────────────────── */

.cluster-donut-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 14px;
}
.cluster-donut-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 14px 16px;
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    min-width: 0;
}
.cluster-donut-card h4 {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
}

/* ─── Cluster tab — Phase 6 polish ─────────────────────────────────────── */

/* Sticky table headers — long pod lists stay scannable when you scroll.
   The table sits inside the page's natural overflow context, so `top: 0`
   pins to the viewport. Z-index above the row content but below the
   page-level chrome. */
.cluster-pods-table thead th,
.cluster-nodes-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--bg-surface);
}

/* Pods section header gets the filter input — wider than the nodes
   header. Stretch the input so it flows to the right of the count chip. */
.cluster-section-head-pods {
    flex-wrap: wrap;
    gap: 8px;
}
.cluster-pod-filter {
    flex: 1 1 220px;
    max-width: 320px;
    height: 30px;
    padding: 4px 10px;
    font-size: 13px;
}
.cluster-pod-filter-clear {
    padding: 4px 8px;
    color: var(--text-secondary);
    line-height: 1;
}
.cluster-pod-filter-clear:hover {
    color: var(--text-primary);
}

/* Auto-poll toggle — same shape as the existing `.stats-toggle` on
   StatisticsTab so the two surfaces look consistent. */
.cluster-autopoll-toggle {
    margin-left: 6px;
}

/* Multi-container badge on the pod name cell. Sized so it tucks next to
   the name without blowing the row height up. */
.cluster-container-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 0 6px;
    background: var(--accent-soft);
    color: var(--accent);
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 600;
    line-height: 1.5;
    vertical-align: middle;
    font-feature-settings: 'tnum';
}

/* ─── Cluster tab — pod resource bars + node tinting ──────────────────── */

/* Node tint cell — the actual gradient is set inline (per-pod, hash-driven
   in the Razor). This rule only carries baseline cell styling so the tint
   reads consistently. Note the cell-muted color is preserved; we're just
   layering a gentle wash UNDER the text. */
.cluster-pod-node {
    border-radius: 4px;
}

/* BestEffort pod (no request/limit set) — show usage as plain text aligned
   right, matching the visual weight of the bar's label so columns stay
   coherent. */
.cluster-pod-usage-plain {
    display: inline-block;
    width: 100%;
    text-align: right;
    font-size: 12.5px;
    color: var(--text-secondary);
    font-feature-settings: 'tnum';
}

/* Pod-table sticky header retains its solid surface fill so node-tint
   gradients on row 1 don't bleed up under the header text. (Already
   applied via the sticky thead rule earlier — declaring here too in case
   the cascade order changes.) */
.cluster-pods-table thead th {
    background: var(--bg-surface);
}

/* ─── Cluster tab — Premise section (Hades + Flux per-realm tables) ───── */

.cluster-premise-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 14px;
}
.cluster-premise-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px 14px;
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    min-width: 0;
}
.cluster-premise-card h4 {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 4px;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
}
/* Tighter padding on premise tables — they're inside a smaller card and
   typically carry 1-5 rows; full data-table padding would feel cavernous. */
.cluster-premise-table {
    margin: 0;
}
.cluster-premise-table th,
.cluster-premise-table td {
    padding: 6px 10px;
}

/* ─── Cloud Admin → Control tab ─────────────────────────────────────────
   Cards live side-by-side via grid auto-fit so the layout collapses to
   one column on narrower viewports without a breakpoint. Visual vocabulary
   mirrors .configurator-card so the admin area reads as one design system. */

.control-tab {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.control-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px;
}

.control-card {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.control-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.control-card-header h3 {
    margin: 0;
    font-size: 15px;
    color: var(--text-primary);
    display: flex;
    align-items: center;
    gap: 8px;
}

.control-card-running {
    font-size: 12.5px;
    color: var(--text-muted);
    font-feature-settings: 'tnum';
    white-space: nowrap;
}

.control-card-running b {
    color: var(--text-primary);
    font-weight: 600;
}

.control-card-body {
    /* form-row provides its own spacing — wrapper exists for future
       sibling content (status sub-rows, etc.) without re-flow. */
    min-height: 0;
}

.control-card-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    padding-top: 12px;
    border-top: 1px solid var(--border-subtle);
    margin-top: auto; /* sticks actions to the card bottom for uniform card heights */
}

.control-card-actions > button {
    flex: 1 1 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.control-card-actions .spinner-border {
    width: 14px;
    height: 14px;
    border-width: 2px;
}

/* Number-stepper for the replica count. Custom +/- on either side of a
   borderless input so the whole widget reads as one control rather than
   three siblings. */
.control-spinner {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    overflow: hidden;
    background-color: var(--bg-surface);
}

.control-spinner > button {
    background: transparent;
    border: none;
    padding: 4px 12px;
    color: var(--text-secondary);
    cursor: pointer;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.control-spinner > button:hover:not(:disabled) {
    background-color: var(--bg-hover, rgba(0, 0, 0, 0.04));
    color: var(--text-primary);
}

.control-spinner > button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.control-spinner > input[type="number"] {
    width: 4em;
    text-align: center;
    border: none;
    border-left: 1px solid var(--border-subtle);
    border-right: 1px solid var(--border-subtle);
    padding: 4px 0;
    font-variant-numeric: tabular-nums;
    background-color: transparent;
    color: var(--text-primary);
}

.control-spinner > input[type="number"]:focus {
    outline: none;
    background-color: var(--bg-hover, rgba(0, 0, 0, 0.03));
}

/* Suppress the browser-native number-spinner buttons — our custom +/-
   are the intended controls; the native ones get clipped by overflow
   anyway and look out of place. */
.control-spinner > input[type="number"]::-webkit-outer-spin-button,
.control-spinner > input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.control-spinner > input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

html.dark .control-spinner > button:hover:not(:disabled) {
    background-color: rgba(255, 255, 255, 0.06);
}
html.dark .control-spinner > input[type="number"]:focus {
    background-color: rgba(255, 255, 255, 0.04);
}

/* ─── Rooms: video call (WebRTC mesh, 4-cap) ────────────────────────────
   Lives between the room header and the message scroll. Tile grid scales
   1-up / 2x2 depending on participant count; controls are a pill row at
   the bottom of the panel, visually distinct from the chat composer below. */

/* Header right-side cluster. Holds the call button + members chip, kept
   vertically aligned so the header reads as one row even when actions
   stack on narrow viewports. */
.room-chat-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

.room-chat-call-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border-radius: 999px;
    border: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, #212529);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.room-chat-call-btn:hover:not(:disabled) {
    background: var(--bs-tertiary-bg, #f6f7f9);
    border-color: var(--bs-primary, #0d6efd);
    color: var(--bs-primary, #0d6efd);
}
/* Active-call state — someone is in the call, you can join. Greener to
   read as "active" / live, not as "warning". */
.room-chat-call-btn.is-join {
    background: var(--bs-success-bg-subtle, #d1e7dd);
    border-color: var(--bs-success-border-subtle, #a3cfbb);
    color: var(--bs-success-text-emphasis, #0a3622);
}
.room-chat-call-btn.is-join:hover:not(:disabled) {
    background: var(--bs-success-border-subtle, #a3cfbb);
}
.room-chat-call-btn.is-full {
    background: var(--bs-tertiary-bg, #f6f7f9);
    color: var(--bs-secondary-color, #6c757d);
    cursor: not-allowed;
}
.room-chat-call-btn.is-leave {
    background: var(--bs-danger-bg-subtle, #f8d7da);
    border-color: var(--bs-danger-border-subtle, #f1aeb5);
    color: var(--bs-danger-text-emphasis, #58151c);
}
.room-chat-call-btn.is-leave:hover:not(:disabled) {
    background: var(--bs-danger-border-subtle, #f1aeb5);
}
.room-chat-call-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* Call panel — sits between the header and the message scroll. The grid
   takes available height; controls are a fixed pill row beneath. */
.room-call-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 20px;
    background: var(--bs-tertiary-bg, #f6f7f9);
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
    /* Cap the panel's growth so the chat scroll always stays visible. */
    max-height: 50vh;
    min-height: 0;
}

.room-call-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 0.875rem;
}
.room-call-banner.is-error {
    background: var(--bs-danger-bg-subtle, #f8d7da);
    color: var(--bs-danger-text-emphasis, #58151c);
}

/* Tile grid — auto-fit minmax(180px, 1fr) gives 1, 2, 3, or 4 tiles per
   row up to whatever fits. Above the 4-cap there's no need to scale
   further. Aspect-ratio keeps tiles legible regardless of participant
   count. */
.room-call-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 10px;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

.room-call-tile {
    position: relative;
    aspect-ratio: 4 / 3;
    background: #000;
    border-radius: 8px;
    overflow: hidden;
    border: 2px solid transparent;
    transition: border-color 150ms ease;
}
.room-call-tile.is-local {
    /* Subtle accent so the user can spot their own tile at a glance,
       without it dominating the grid. */
    border-color: var(--bs-primary, #0d6efd);
}
.room-call-tile > video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* Mirror only the LOCAL tile so the user sees themselves the way a
       mirror would. Remote video is left untransformed. */
}
.room-call-tile.is-local > video {
    transform: scaleX(-1);
}

.room-call-tile-name {
    position: absolute;
    left: 8px;
    bottom: 8px;
    padding: 2px 8px;
    border-radius: 4px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 0.75rem;
    font-weight: 500;
    pointer-events: none;
}

.room-call-tile-badges {
    position: absolute;
    right: 8px;
    top: 8px;
    display: inline-flex;
    gap: 4px;
}
.room-call-tile-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 0.75rem;
}

/* Controls row — sticky bottom of the panel. */
.room-call-controls {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding-top: 4px;
}

.room-call-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    /* 44 × 44 = Apple HIG + Material's minimum recommended tap target. The
       previous 40 was borderline on phones — a finger tap missed the icon
       roughly 1-in-10. Desktop hover is unaffected by the bump. */
    min-width: 44px;
    height: 44px;
    padding: 0 14px;
    border-radius: 999px;
    border: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, #212529);
    font-size: 0.9rem;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
.room-call-btn:hover:not(:disabled) {
    background: var(--bs-tertiary-bg, #f6f7f9);
}
.room-call-btn.is-off {
    /* Mic / camera in the off state — red-ish so the user can spot at a
       glance that they're muted, without screaming about it. */
    background: var(--bs-danger-bg-subtle, #f8d7da);
    border-color: var(--bs-danger-border-subtle, #f1aeb5);
    color: var(--bs-danger-text-emphasis, #58151c);
}
.room-call-btn.is-leave {
    background: var(--bs-danger, #dc3545);
    border-color: var(--bs-danger, #dc3545);
    color: #fff;
}
.room-call-btn.is-leave:hover:not(:disabled) {
    filter: brightness(0.92);
}
.room-call-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* Dark mode — tile container stays black (live video reads correctly on
   any backdrop), only the panel + control surfaces need adjusting. */
html.dark .room-call-panel {
    background: rgba(255, 255, 255, 0.03);
    border-bottom-color: rgba(255, 255, 255, 0.08);
}
html.dark .room-chat-call-btn {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.12);
    color: var(--bs-body-color, #e9ecef);
}
html.dark .room-chat-call-btn:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.08);
}
html.dark .room-call-btn {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.12);
    color: var(--bs-body-color, #e9ecef);
}
html.dark .room-call-btn:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.08);
}

/* ─── Mobile (portrait phones, narrow viewports) ──────────────────────────
   The desktop layout puts a lot in the chat pane: header / call panel /
   message scroll / composer. On a 360-400px viewport that stacks too
   densely without these adjustments. Breakpoint at 768px matches Bootstrap
   5's `md` cutoff which the rest of the app uses. */
@media (max-width: 768px) {
    /* Tighten the header padding so the title + call button + members chip
       stay on one line on narrow viewports. The call button shrinks via
       its content; the members chip already truncates. */
    .room-chat-header {
        padding: 10px 14px 8px;
        gap: 8px;
    }
    .room-chat-call-btn span {
        /* Collapse the label on phones — the icon carries the meaning, the
           green/red color carries the state. Keeps the header readable on
           360px wide screens where every pixel counts. */
        display: none;
    }
    .room-chat-call-btn {
        padding: 5px 10px;
    }

    .room-call-panel {
        padding: 10px 14px;
        /* Reduce the panel ceiling on phones so the chat scroll remains
           usable underneath. 50vh on landscape mobile is the entire screen. */
        max-height: 40vh;
        gap: 8px;
    }

    .room-call-grid {
        /* On phones below ~600px wide, two columns of 180px doesn't fit
           (180 × 2 + gap > 360). Drop the minmax floor so 1-tile fills the
           width and 2-tile splits evenly. */
        grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
        gap: 8px;
    }

    .room-call-tile {
        /* 16:9 fits a phone's portrait aspect better than 4:3 — less
           letterboxing on remote streams that come from laptops. */
        aspect-ratio: 16 / 9;
    }

    .room-call-tile-name {
        font-size: 0.7rem;
        padding: 2px 6px;
        bottom: 6px;
        left: 6px;
    }

    .room-call-controls {
        /* More breathing room between tap targets so a sloppy thumb doesn't
           hit the wrong control. The 44px button width + this gap = ~52px
           center-to-center which matches the rule-of-thumb minimum. */
        gap: 12px;
        padding-top: 2px;
    }

    .room-call-btn {
        /* Shrink the leave button's text label on phones — icon alone is
           enough at the action's color (solid red) and the touch target
           remains 44 × 44. */
        padding: 0 12px;
    }
    .room-call-btn.is-leave span {
        display: none;
    }
}
