/* === Murikual Shared Foundation === */

:root {
    /* Colors — core */
    --sidebar-bg: #111827;
    /* Sidebar body text must clear AA against ANY branded --sidebar-bg an app sets (a solid,
       saturated brand colour is lighter than the default near-black), so this is deliberately
       brighter than --gray-400: 6.8:1 on Murikual's ultramarine, 11.6:1 on the default. */
    --sidebar-text: #cbd5e1;
    --sidebar-text-hover: #f3f4f6;
    --sidebar-active-bg: rgba(255, 255, 255, 0.08);
    --content-bg: #f9fafb;
    --card-bg: #fff;
    --text: #111827;
    --text-secondary: #6b7280;
    --border: #e5e7eb;
    --accent: #2563eb;
    --accent-hover: #1d4ed8;
    --accent-ring: rgba(37, 99, 235, 0.18);
    --green: #059669;
    --red: #dc2626;
    --yellow: #d97706;

    /* Colors — gray scale */
    --gray-50: #f9fafb;
    --gray-100: #f3f4f6;
    --gray-200: #e5e7eb;
    --gray-300: #d1d5db;
    --gray-400: #9ca3af;
    --gray-500: #6b7280;
    --gray-600: #4b5563;
    --gray-900: #111827;

    /* Colors — semantic (light bg + dark fg for badges) */
    --green-50: #ecfdf5;
    --green-500: #059669;
    --green-700: #047857;
    --red-50: #fef2f2;
    --red-200: #fecaca;
    --red-600: #dc2626;
    --red-700: #b91c1c;
    --blue-50: #eff6ff;
    --blue-100: #dbeafe;
    --blue-700: #1d4ed8;
    --yellow-50: #fffbeb;
    --yellow-100: #fef3c7;
    --yellow-700: #b45309;
    --purple-50: #f5f3ff;
    --purple-100: #ede9fe;
    --purple-700: #6d28d9;
    --indigo-100: #e0e7ff;
    --indigo-700: #4338ca;
    --cyan-100: #cffafe;
    --cyan-700: #0e7490;

    /* Shadows */
    --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.05);
    --shadow: 0 1px 3px rgba(16, 24, 40, 0.1), 0 1px 2px rgba(16, 24, 40, 0.06);
    --shadow-md: 0 6px 16px -4px rgba(16, 24, 40, 0.1), 0 2px 6px -2px rgba(16, 24, 40, 0.06);

    /* Border radius */
    --radius: 10px;
    --radius-sm: 5px;
    --radius-md: 7px;
    --radius-lg: 14px;
    --radius-full: 9999px;

    /* Spacing scale */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.25rem;
    --space-6: 1.5rem;
    --space-7: 2rem;
    --space-8: 2.5rem;
    --space-9: 4rem;

    /* Typography scale */
    --text-xs: 0.75rem;
    --text-sm: 0.8125rem;
    --text-base: 0.875rem;
    --text-lg: 1.125rem;

    /* Monospace. Two callers, one stack: identifiers a person reads character by character
       and compares by eye (card keys, hashes, short codes), and the terminal surface. Already
       referenced with a bare `monospace` fallback further down this file; defined here so that
       reference resolves to the real stack.

       The terminal keeps its own dark palette in both themes: it renders a real ANSI stream
       whose colours assume a dark background, so flipping it to light would make standard
       terminal output unreadable. */
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    --terminal-bg: #1a1b26;
    --terminal-fg: #c0caf5;

    /* App font — admin-overridable via App.razor's inline <style> (see
       IPlatformTypographyStore / TypographyCssBuilder). These defaults reproduce
       today's look, so the tokens are a no-op until an admin sets a custom font.
       --app-font-size drives the document root, so it scales the whole rem-based
       UI; 100% keeps the browser default (and honours user zoom). */
    --app-font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --app-font-size: 100%;
    --app-font-weight: 400;

    /* Layout — single source of truth for page width & responsive gutters.
       The whole app (sidebar + content) is fluid full-width up to --app-max,
       then centres, so ultra-wide screens get symmetric gutters instead of
       endlessly stretched content. --content-pad-x is the page side padding and
       is overridden per breakpoint at the bottom of this file. */
    --app-max: 1920px;
    --sidebar-w: 260px;
    --content-pad-x: 2rem;

    /* Breakpoints (documentation tokens — CSS media queries can't read vars,
       so these mirror the literal values used in @media rules below). */
    --bp-sm: 700px;
    --bp-md: 992px;
    --bp-lg: 1200px;
    --bp-xl: 1600px;
}

/* === Dark mode ===
   Applied via data-theme on <html>, emitted server-side by App.razor.
   Explicit dark: [data-theme="dark"]. OS-driven: [data-theme="system"] under
   prefers-color-scheme. Only content/surface tokens change here — the branded
   sidebar (--sidebar-bg) and the admin accent are set by App.razor's inline
   override (which also lightens the accent in dark mode). */
[data-theme="dark"] {
    color-scheme: dark;
    --content-bg: #0f1729;
    --card-bg: #1c2538;
    --text: #e5e7eb;
    --text-secondary: #9aa5b8;
    --border: #313c52;
    --gray-50: #1c2538;
    --gray-100: #243049;
    --gray-200: #313c52;
    --gray-300: #475069;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow: 0 1px 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4);
    /* The status tint ramps (--green-50/--red-*/--blue-*/--yellow-*) for dark
       mode are emitted by ThemeCssBuilder (single source of truth, palette-driven). */
}

@media (prefers-color-scheme: dark) {
    [data-theme="system"] {
        color-scheme: dark;
        --content-bg: #0f1729;
        --card-bg: #1c2538;
        --text: #e5e7eb;
        --text-secondary: #9aa5b8;
        --border: #313c52;
        --gray-50: #1c2538;
        --gray-100: #243049;
        --gray-200: #313c52;
        --gray-300: #475069;
        --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
        --shadow: 0 1px 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4);
        /* See [data-theme="dark"] above — the status tint ramps are emitted by
           ThemeCssBuilder, not duplicated here. */
    }
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
    /* clip (not hidden): still prevents a horizontal scrollbar, but does NOT
       turn the root into a scroll container — which `hidden` does, and which
       disables position:sticky for descendants (the sidebar + sticky table
       header would otherwise scroll away with the page). */
    overflow-x: clip;
}

/* Root font size drives every rem in the UI — the admin "font size" setting
   overrides --app-font-size here to scale the whole app up/down. */
html {
    font-size: var(--app-font-size);
}

body {
    font-family: var(--app-font-family);
    font-weight: var(--app-font-weight);
    color: var(--text);
    background: var(--content-bg);
    margin: 0;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: none; }

/* ======================== LAYOUT ======================== */
/* The app shell is the width authority: fluid full-width up to --app-max, then
   centred (margin-inline:auto) so ultra-wide screens get symmetric gutters.
   NOTE: must NOT set overflow (clip/hidden/auto) here — any of those would turn
   the shell into a clip/scroll context and disable the sidebar's position:sticky
   (it would scroll away with the page). The collapsed sidebar is hidden with
   opacity instead (see SIDEBAR TOGGLE). */
.app-layout {
    display: flex;
    min-height: 100vh;
    max-width: var(--app-max);
    margin-inline: auto;
    position: relative;
}

/* Sidebar — sticky and in-flow so it lives inside the centred shell (not pinned
   to the raw viewport). It reserves its column via flex; collapsing pulls it out
   with a negative margin (see SIDEBAR TOGGLE). On mobile it becomes a fixed
   overlay (see the responsive block). */
.sidebar {
    width: var(--sidebar-w);
    background: var(--sidebar-bg);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    position: sticky;
    top: 0;
    align-self: flex-start;
    height: 100vh;
    z-index: 100;
}

.sidebar-brand {
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.sidebar-brand a {
    color: #fff;
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Co-brand wordmark: product name + a small parent-brand tagline stacked under it. */
.sidebar-brand-text { display: flex; flex-direction: column; line-height: 1.1; }
.sidebar-brand-name { font-size: 1.125rem; font-weight: 600; }
.sidebar-brand-tagline { font-size: 0.7rem; font-weight: 500; opacity: 0.72; letter-spacing: 0.01em; }

.sidebar-collapse-btn {
    background: none;
    border: none;
    color: var(--sidebar-text);
    cursor: pointer;
    padding: 0.25rem;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: color 0.15s ease, background 0.15s ease;
    flex-shrink: 0;
}

.sidebar-collapse-btn:hover {
    color: var(--sidebar-text-hover);
    background: var(--sidebar-active-bg);
}

.sidebar-collapse-btn svg {
    width: 18px;
    height: 18px;
}

.sidebar-logo {
    width: 28px;
    height: 28px;
}

.sidebar-brand a:hover { color: #fff; }

/* Workspace (tenant) menu — current workspace + switch/create dropdown, under
   the brand in the dark sidebar. */
.workspace-menu { position: relative; margin: 0.5rem 0.75rem 0; }

.workspace-badge {
    flex-shrink: 0;
    width: 24px; height: 24px;
    border-radius: var(--radius-sm, 6px);
    background: var(--accent);
    color: #fff;
    font-size: 0.72rem; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
    text-transform: uppercase;
}
.workspace-name {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    text-align: left;
}

.workspace-trigger {
    width: 100%;
    display: flex; align-items: center; gap: 0.55rem;
    padding: 0.5rem 0.6rem;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.04);
    color: #fff;
    font-size: 0.875rem; font-weight: 500;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.workspace-trigger:hover { border-color: rgba(255, 255, 255, 0.24); background: rgba(255, 255, 255, 0.07); }
.workspace-chevron { width: 15px; height: 15px; flex-shrink: 0; color: var(--sidebar-text); }

.workspace-backdrop { position: fixed; inset: 0; z-index: 190; }

.workspace-dropdown {
    position: absolute;
    top: calc(100% + 6px); left: 0; right: 0;
    z-index: 200;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, 0.18));
    padding: 6px;
}
.workspace-dropdown-label {
    font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-secondary);
    padding: 6px 8px 4px;
}
.workspace-option {
    width: 100%;
    display: flex; align-items: center; gap: 0.55rem;
    padding: 0.5rem 0.5rem;
    border: none; background: transparent;
    color: var(--text-primary);
    font-size: 0.875rem; font-weight: 500;
    border-radius: var(--radius-sm, 6px);
    cursor: pointer;
    transition: background 0.12s ease;
}
.workspace-option:hover { background: var(--hover-bg, rgba(0, 0, 0, 0.05)); }
.workspace-option.active { font-weight: 600; }
.workspace-check { width: 16px; height: 16px; flex-shrink: 0; color: var(--accent); }
.workspace-divider { height: 1px; background: var(--border); margin: 6px 4px; }

.workspace-badge-add { background: transparent; color: var(--text-secondary); border: 1px dashed var(--border); font-weight: 600; }
.workspace-new { color: var(--text-secondary); }

.workspace-create { display: flex; gap: 6px; padding: 4px; }
.workspace-create-input {
    flex: 1 1 auto; min-width: 0;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border); border-radius: var(--radius-sm, 6px);
    /* Theme-aware: --content-bg reads against the card-bg dropdown in light AND dark
       (a bare #fff fallback made the input white with light text in dark mode). */
    background: var(--content-bg); color: var(--text-primary);
    font-size: 0.85rem;
}
.workspace-create-input::placeholder { color: var(--text-secondary); }
.workspace-create-input:focus { outline: none; border-color: var(--accent); }
.workspace-create-btn {
    flex-shrink: 0;
    padding: 0.4rem 0.7rem;
    border: none; border-radius: var(--radius-sm, 6px);
    background: var(--accent); color: #fff;
    font-size: 0.82rem; font-weight: 600; cursor: pointer;
}
.workspace-create-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.sidebar-nav {
    padding: 0.75rem 0.75rem;
    flex: 1 1 auto;
    min-height: 0;          /* let flex children shrink so overflow-y kicks in */
    overflow-y: auto;       /* scroll when there are more entries than slots */
    overscroll-behavior: contain;
    display: flex;
    flex-direction: column;
    gap: 2px;
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
}

.sidebar-nav::-webkit-scrollbar { width: 6px; }
.sidebar-nav::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.15);
    border-radius: 3px;
}
.sidebar-nav::-webkit-scrollbar-track { background: transparent; }

/* ── Arranging the sidebar ──────────────────────────────────────────────────
   Only rendered while the user is customizing (MainLayout's Customizing), so none of this
   affects the sidebar anyone is actually navigating with. */

.sidebar-customize-bar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-2);
    background: var(--card-bg);
    border: 1px solid var(--accent);
    border-radius: var(--radius-md);
    font-size: 0.8125rem;
}

.nav-item-row {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.nav-item-row > .nav-item { flex: 1 1 auto; min-width: 0; }

.nav-row-controls {
    display: inline-flex;
    gap: var(--space-1);
    margin-left: auto;
    flex: 0 0 auto;
}

.nav-section-controls { padding: 0 var(--space-3) var(--space-1); }

.nav-row-controls button {
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
    line-height: 1;
    padding: 0 var(--space-1);
}

.nav-row-controls button:hover { border-color: var(--accent); color: var(--accent); }

.nav-row-controls button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Hidden entries stay on screen while arranging, so they can be brought back. */
.nav-item-row.is-hidden { opacity: 0.5; }
.nav-item-row.is-hidden > .nav-item { text-decoration: line-through; }

.sidebar-section-label {
    padding: 0.5rem 0.75rem 0.25rem;
    font-size: 0.6875rem;
    font-weight: 500;
    /* Sentence case, not uppercase. Uppercase reads slower, shouts a label that is meant to be
       skimmed past, and — set from a module identifier — produced PROJECTMANAGEMENT. The label is
       humanised in NavSectionNaming now, so it arrives as "Project management" and stays that way.
       Letter-spacing goes with it: it exists to make uppercase legible. */
    letter-spacing: 0;
    /* Was rgba(255,255,255,0.3), which composites to roughly 3.3:1 on the default sidebar — under
       AA for text this small, on a control that is also a button. --sidebar-text is the value
       already documented as clearing AA against any branded sidebar an app sets. */
    color: var(--sidebar-text);
    opacity: 0.7;
}

/* Clickable variant — used as a collapse toggle for module sections.
   Visually identical to the static label, but acts as a button. */
button.sidebar-section-label {
    width: 100%;
    background: none;
    border: none;
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    transition: color 0.15s ease;
}

button.sidebar-section-label:hover { opacity: 1; }

/* It is a button, so it needs to be findable by keyboard — it had no focus style at all. */
button.sidebar-section-label:focus-visible {
    opacity: 1;
    outline: 2px solid var(--sidebar-text);
    outline-offset: -2px;
    border-radius: var(--radius-sm);
}

button.sidebar-section-label .chevron {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
    transition: transform 0.15s ease;
}

button.sidebar-section-label[aria-expanded="false"] .chevron {
    transform: rotate(-90deg);
}

/* Hidden module section — items folded away by the toggle. */
.sidebar-section-items[hidden] { display: none; }

.nav-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    font-size: 0.875rem;
    color: var(--sidebar-text);
    transition: all 0.15s ease;
    font-weight: 400;
}

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

.nav-item.active {
    color: #fff;
    background: var(--sidebar-active-bg);
    font-weight: 500;
}

.nav-item .nav-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    opacity: 0.5;
    flex-shrink: 0;
}

/* Make sure SVGs nested inside a .nav-icon span fill it; outline strokes
   that approach the viewBox edge stay visible thanks to overflow:visible. */
span.nav-icon > svg {
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}

.nav-item.active .nav-icon,
.nav-item:hover .nav-icon {
    opacity: 0.8;
}

.sidebar-footer {
    padding: 1rem 1.5rem;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.sidebar-footer a,
.sidebar-footer button {
    color: var(--sidebar-text);
    font-size: 0.8125rem;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    font-family: inherit;
}

.sidebar-footer a:hover,
.sidebar-footer button:hover {
    color: var(--sidebar-text-hover);
}

/* Main content — fills the remaining shell width. It owns the page's outer
   padding (horizontal via --content-pad-x) so every page aligns identically,
   whether or not it uses a .content-body wrapper. */
.main-content {
    flex: 1;
    min-width: 0;
    min-height: 100vh;
    /* clip (not hidden) — hidden would make this a scroll container and disable
       the sticky table header inside it (see the note on html/body above). */
    overflow-x: clip;
    background: var(--content-bg);
    padding: var(--space-6) var(--content-pad-x) var(--space-8);
}

.content-header {
    background: var(--card-bg);
    border-bottom: 1px solid var(--border);
    padding: 1.25rem 2rem;
    position: sticky;
    top: 0;
    z-index: 50;
}

.content-header h1 {
    font-size: 1.25rem;
    font-weight: 600;
    margin: 0;
    color: var(--text);
}

.content-header .subtitle {
    font-size: 0.8125rem;
    color: var(--text-secondary);
    margin: 0.125rem 0 0;
}

/* .content-body is now just a semantic full-width wrapper. Width capping lives
   on .app-layout and padding on .main-content, so this no longer sets its own
   max-width (which used to cap content at 1200px, left-aligned, on top of the
   main-content padding — the source of the wasted horizontal space). Pages that
   need a narrow reading measure (forms) opt in with .form-card / --form-max. */
.content-body {
    width: 100%;
}

/* ======================== CARDS ======================== */
.card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);

    /* Tables rendered inside a card must match the card surface, not the page
       background — otherwise the sticky header paints a mismatched band (clearly
       darker in dark mode: #0f1729 header on a #1c2538 card). */
    --table-head-bg: var(--card-bg);
}

.card-header {
    padding: 1rem 1.25rem;
    background: var(--card-bg);
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: 0.875rem;
}

/*
 * A card header with an action on the right: title (and its hint) left, buttons right.
 *
 * A modifier rather than a change to .card-header itself, because most headers in the platform are
 * a plain block whose children are meant to flow, and turning every one of them into a flex row
 * would rearrange pages nobody was touching. Several pages predate this and inline the same three
 * declarations; they should move onto this class as they are next edited.
 */
.card-header.with-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-3);
}

.card-body { padding: 1.25rem; }
.card-body.p-0 { padding: 0; }

/* ======================== STAT CARDS ======================== */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.stat-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: 1.25rem 1.375rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}

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

.stat-sub {
    font-size: var(--text-xs);
    color: var(--text-secondary);
    font-weight: 500;
}

.stat-value {
    font-size: 1.75rem;
    font-weight: 600;
    line-height: 1;
    color: var(--text);
}

.stat-label {
    font-size: var(--text-xs);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--text-secondary);
    margin: 0;
}

/* ======================== TABLES ======================== */
/* Table container — a transparent passthrough: the individual rows are the
   visual cards (see .data-table below). Deliberately NOT a scroll container,
   so the sticky column header pins at page level as you scroll a long list.
   Genuinely wide tables that must scroll sideways opt into .table-card--scroll
   (which, being a scroll container, gives up the sticky header — that trade-off
   is intentional and documented in docs/UI-UX-PAGE-GUIDE.md). */
.table-card {
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    overflow: visible;

    /* Transparent, sits on the page: its header matches the page background. Declared
       on this element so a .table-card nested inside a .card still wins for its own
       descendants. */
    --table-head-bg: var(--content-bg);
}

.table-card--scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.table-card-header {
    padding: 1rem 1.25rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid var(--border);
}

.table-card-header h2 {
    font-size: 0.875rem;
    font-weight: 600;
    margin: 0;
}

/* Rows are separated cards: border-collapse:separate + a vertical border-spacing
   gap, with the border/radius drawn on each row's cells. */
.data-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0 var(--space-2);
    font-size: 0.875rem;
}

/* Flat, sticky column header. No fill / uppercase — just a quiet label row that
   stays pinned to the top of the viewport while a long list scrolls. The fill is
   opaque so scrolled rows pass cleanly beneath it, and follows --table-head-bg so it
   matches whichever surface the table sits on (page for .table-card, card for .card). */
.data-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--table-head-bg, var(--content-bg));
    padding: 0.5rem 1.25rem;
    text-align: left;
    font-weight: 600;
    font-size: var(--text-sm);
    color: var(--text-secondary);
    white-space: nowrap;
}

/* Each row is a soft card. */
.data-table tbody tr {
    position: relative;                 /* containing block for .row-link */
    background: var(--card-bg);
    box-shadow: var(--shadow-sm);
    transition: box-shadow 0.15s ease;
}

.data-table tbody td {
    padding: 0.9375rem 1.25rem;
    vertical-align: middle;
    background: transparent;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}

.data-table tbody td:first-child {
    border-left: 1px solid var(--border);
    border-top-left-radius: var(--radius);
    border-bottom-left-radius: var(--radius);
}

.data-table tbody td:last-child {
    border-right: 1px solid var(--border);
    border-top-right-radius: var(--radius);
    border-bottom-right-radius: var(--radius);
}

/* Hover: accent outline + subtle lift (reads as clickable when a .row-link is
   present, and as a gentle highlight otherwise). */
.data-table tbody tr:hover td { border-color: var(--accent); }
.data-table tbody tr:hover { box-shadow: 0 1px 4px var(--accent-ring); }

.data-table .col-nowrap {
    white-space: nowrap;
}

/* Numeric columns: right-aligned with tabular figures so digits line up. */
.data-table .text-end,
.data-table .num { text-align: right; }
.data-table .num { font-variant-numeric: tabular-nums; color: var(--text-secondary); }

/* Primary cell — the row's headline. Rendered as body text (not link-blue):
   with .row-link the whole row is clickable, so the row hover + pointer carry
   the affordance. */
.data-table .cell-strong { font-weight: 600; color: var(--text); }
.data-table .row-link { color: var(--text); font-weight: 600; }
.data-table tbody tr:hover .row-link { color: var(--accent); }

/* Row-level clickability (opt-in per table): put class="row-link" on the row's
   primary <a>; its stretched ::after covers the whole row. Real interactive
   controls (buttons, other links, the actions cell) sit above it via z-index. */
.data-table .row-link::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
}

.data-table td .btn,
.data-table td button,
.data-table td a:not(.row-link),
.data-table td .cell-actions,
.data-table td.cell-actions { position: relative; z-index: 1; }

/* Action cells stay a normal (right-aligned) table cell so the table's column
   layout still sizes the actions column — the buttons (already inline-flex) flow
   inline and sit side-by-side when the column has room, wrapping only when it is
   genuinely too narrow (mobile switches to the flex card layout below). Making
   the <td> itself `display:flex` collapses the flex box to one-button min-content
   width inside the table, so a 2-button set stacks even in a wide column. */
.data-table td.cell-actions {
    text-align: right;
}
.data-table td.cell-actions > .btn,
.data-table td.cell-actions > button,
.data-table td.cell-actions > a,
.data-table td.cell-actions > .btn-group {
    vertical-align: middle;
}
/* Spacing between adjacent actions (replaces the old flex gap). */
.data-table td.cell-actions > * + * { margin-left: var(--space-2); }
/* Any button group inside a table cell (in a .cell-actions cell or a plain one,
   e.g. UserManagement's user-row actions) reflows instead of overflowing. */
.data-table td .btn-group { flex-wrap: wrap; }

/* Inline label + leading icon/dot (e.g. a group colour swatch beside a name). */
.inline-center {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

/* Long machine values (image refs, upstream URLs, paths) wrap instead of
   forcing the whole table into a horizontal scroll on narrow desktops. */
.data-table code {
    overflow-wrap: anywhere;
    word-break: break-word;
}

.data-table .cell-wrap {
    max-width: 22rem;
    overflow-wrap: anywhere;
    word-break: break-word;
    white-space: normal;
}

/* Coloured pill tagging a row with its site group; the group colour is
   data-driven so it stays inline, everything else lives here. */
.group-badge {
    display: inline-block;
    color: #fff;
    font-size: 0.6875rem;
    line-height: 1.4;
    padding: 0.125rem 0.375rem;
    border-radius: var(--radius-full);
    margin-left: 0.25rem;
    vertical-align: middle;
}

/* Drag-to-resize column handles (added by js/table-resize.js). The header cell
   becomes the positioning context; the handle is a thin hit-area on its right
   edge. Widths themselves are applied via an injected stylesheet, not here. */
/* Keep sticky (the base thead th is position:sticky, which is also a valid
   positioning context for the absolutely-positioned resizer handle below). */
.data-table thead th.mkr-resizable-th {
    position: sticky;
}

.mkr-col-resizer {
    position: absolute;
    top: 0;
    right: 0;
    width: 8px;
    height: 100%;
    cursor: col-resize;
    user-select: none;
    touch-action: none;
    z-index: 1;
}

.mkr-col-resizer::after {
    content: "";
    position: absolute;
    top: 25%;
    right: 3px;
    width: 2px;
    height: 50%;
    background: var(--border);
    opacity: 0;
    transition: opacity 0.12s ease;
}

.data-table thead th.mkr-resizable-th:hover .mkr-col-resizer::after,
.mkr-col-resizer:hover::after {
    opacity: 1;
}

.mkr-col-resizer:hover::after {
    background: var(--accent);
}

/* While dragging: suppress text selection and force the resize cursor globally. */
body.mkr-col-resizing {
    user-select: none;
    cursor: col-resize !important;
}

/* ======================== BADGES ======================== */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 0.2em 0.6em;
    border-radius: 9999px;
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1;
}

.badge-proxy { background: #dbeafe; color: #1e40af; }
.badge-static { background: #dcfce7; color: #166534; }

/* ======================== STATUS ======================== */
.status {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
}

.status-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex-shrink: 0;
}

.status-active .status-dot { background: var(--green); }
.status-active { color: var(--green); }
.status-disabled .status-dot { background: var(--gray-400); }
.status-disabled { color: var(--text-secondary); }

/* ======================== BUTTONS ======================== */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.5rem 1rem;
    border-radius: 6px;
    font-size: 0.875rem;
    font-weight: 500;
    border: 1px solid transparent;
    cursor: pointer;
    transition: all 0.15s ease;
    text-decoration: none;
    line-height: 1.25;
}

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

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

.btn-primary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.btn-primary:active {
    transform: translateY(1px);
}

.btn-secondary {
    background: var(--card-bg);
    border-color: var(--border);
    color: var(--text);
}

.btn-secondary:hover {
    background: var(--gray-100);
    color: var(--text);
}

.btn-secondary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.btn-secondary:active {
    transform: translateY(1px);
}

.btn-danger {
    background: var(--card-bg);
    border-color: var(--border);
    color: var(--red);
}

.btn-danger:hover {
    background: var(--red-50);
    border-color: var(--red-200);
    color: var(--red);
}

.btn-danger:focus-visible {
    outline: 2px solid var(--red);
    outline-offset: 2px;
}

.btn-danger:active {
    transform: translateY(1px);
}

/* Quiet delete: neutral at rest (blends into a row of secondary actions), reveals danger only on
   hover/focus — the confirm step stays red. Pair with .btn-secondary. See docs/UX-FINDINGS.md. */
.btn-danger-ghost { color: var(--text-muted); }
.btn-danger-ghost:hover,
.btn-danger-ghost:focus-visible {
    color: var(--red);
    border-color: var(--red-200);
    background: var(--red-50);
}

.btn-sm {
    padding: 0.375rem 0.75rem;
    font-size: 0.8125rem;
}

.btn-group {
    display: inline-flex;
    gap: 0.5rem;
}

/* ======================== FORMS ======================== */
.form-group {
    margin-bottom: 1.25rem;
}

.form-label {
    display: block;
    font-weight: 500;
    font-size: 0.875rem;
    color: var(--text);
    margin-bottom: 0.375rem;
}

.form-hint {
    font-weight: 400;
    color: var(--text-secondary);
    font-size: 0.8125rem;
}
/* Inline field validation message — same size/weight as a hint, in the danger colour. */
.form-error {
    font-weight: 400;
    color: var(--red-600);
    font-size: 0.8125rem;
}

.form-control,
.form-select {
    width: 100%;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 0.875rem;
    color: var(--text);
    background: var(--card-bg);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    font-family: inherit;
}

/* Pages use <FocusOnNavigate Selector="h1"> to move focus to the heading after
   each navigation (screen-reader UX). That target gets tabindex="-1" and is not
   keyboard-focusable, so suppress its visible focus ring — real keyboard focus
   rings (:focus-visible on interactive controls) are unaffected. */
[tabindex="-1"]:focus { outline: none; }

.form-control:focus,
.form-select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}

.form-control::placeholder { color: var(--gray-400); }

.form-control-sm {
    padding: 0.3125rem 0.5rem;
    font-size: 0.8125rem;
}

/* A select that sits inline beside other controls (e.g. a picker + button in a table's
   action cell) rather than filling a form column: sizes to its own content, capped so a
   long option can't stretch the column. */
.form-select-inline {
    width: auto;
    max-width: 14rem;
}

.form-card {
    max-width: 640px;
}

.form-card .card-body {
    padding: var(--space-6);
}

.form-actions {
    margin-top: var(--space-6);
}

/* A <fieldset> used purely to disable a whole form at once, with no chrome of its own.
   Browsers give fieldset a default border, margin, padding and — the one that actually
   bites — an intrinsic min-width that stops children from shrinking inside flex/grid.
   Resetting all four lets the cards inside own the layout, so a wide element like a
   positions table is not trapped at the width of the narrow .form-card around it. */
.form-fieldset {
    border: 0;
    margin: 0;
    padding: 0;
    min-width: 0;
}

/* A grid of selectable cards — the configurator pattern. Use it where somebody picks from a set of
   extras and the picks have to read at a glance; a column of checkboxes in a table hides which ones
   are on. */
.choice-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
    gap: var(--space-4);
}

.choice-tile {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

/* Chosen is marked by weight and ground, not by colour alone — the state has to survive a
   greyscale print and a colour-blind reader. */
.choice-tile--chosen {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
}

.choice-tile__pick {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 600;
    cursor: pointer;
}

.choice-tile__fields {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
}

.choice-tile__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: auto;
}

.choice-tile__amount {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* The descriptive column in a .data-table — the one holding a name or a sentence rather than a
   figure. A share of the table rather than `width: 100%` or a `min-width`: 100% makes this column
   claim every spare pixel and crushes the numeric columns into their own minimum, and a min-width
   is simply outvoted by the other columns' content under the auto table layout this design system
   uses. A percentage is the one form the auto algorithm honours without starving its neighbours. */
.data-table th.col-grow,
.data-table td.col-grow {
    width: 26%;
}

.btn:disabled,
.btn[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
}

/* ======================== PAGE HEADERS ======================== */
.page-actions {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

/* Spacing for the back control that PageHeader renders above the title. */
.page-back {
    margin-bottom: var(--space-2);
}

/* ======================== DETAIL VIEW ======================== */
.detail-grid {
    display: grid;
    gap: 0;
}

.detail-row {
    display: grid;
    grid-template-columns: 160px 1fr;
    border-bottom: 1px solid var(--gray-100);
    padding: 0.75rem 0;
}

.detail-row:last-child { border-bottom: none; }

.detail-label {
    font-size: 0.8125rem;
    color: var(--text-secondary);
    font-weight: 500;
}

.detail-value {
    font-size: 0.875rem;
    color: var(--text);
}

.detail-value code {
    background: var(--gray-100);
    padding: 0.15em 0.4em;
    border-radius: 4px;
    font-size: 0.8125rem;
    color: var(--text);
}

/* ======================== EMPTY STATE ======================== */
.empty-state {
    text-align: center;
    padding: 3rem 1rem;
}

.empty-state h3 {
    font-size: 1rem;
    font-weight: 600;
    margin: 0 0 0.375rem;
}

.empty-state p {
    color: var(--text-secondary);
    font-size: 0.875rem;
    margin: 0 0 1.25rem;
}

.empty-state-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    margin: 0 auto var(--space-3);
    color: var(--text-secondary);
}

.empty-state-icon svg { width: 1.5rem; height: 1.5rem; }

/* ======================== AUTH PAGES ======================== */
.auth-layout {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--content-bg);
}

.auth-container {
    width: 100%;
    max-width: 400px;
    padding: 0 1rem;
}

.auth-brand {
    text-align: center;
    margin-bottom: 2rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
}

.auth-logo {
    width: 48px;
    height: 48px;
}

.auth-brand span {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--text);
}

.auth-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 2rem;
}

.auth-card h2 {
    font-size: 1.25rem;
    font-weight: 600;
    text-align: center;
    margin: 0 0 0.25rem;
}

.auth-card .auth-subtitle {
    text-align: center;
    color: var(--text-secondary);
    font-size: 0.875rem;
    margin: 0 0 1.5rem;
}

.auth-footer {
    text-align: center;
    margin-top: 1.5rem;
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

.auth-footer a { font-weight: 500; }

/* ======================== ALERTS ======================== */
.alert,
.alert-error {
    padding: 0.75rem 1rem;
    border-radius: var(--radius-md);
    font-size: var(--text-base);
    margin-bottom: var(--space-4);
    border: 1px solid transparent;
}

/* .alert-error kept for existing markup; .alert-danger matches the
   Bootstrap-style naming used across the feature modules. */
.alert-error,
.alert-danger {
    background: var(--red-50);
    border-color: var(--red-200);
    color: var(--red-700);
}

.alert-success {
    background: var(--green-50);
    border-color: var(--green-500);
    color: var(--green-700);
}

.alert-info {
    background: var(--blue-100);
    border-color: var(--blue-700);
    color: var(--blue-700);
}

.alert-warning {
    background: var(--yellow-50);
    border-color: var(--yellow-700);
    color: var(--yellow-700);
}

/* Alert with an inline action (e.g. a Retry button on an error banner). */
.alert-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    justify-content: space-between;
    flex-wrap: wrap;
}

/* ======================== BREADCRUMB ======================== */
.breadcrumb {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
    color: var(--text-secondary);
    margin: 0;
    padding: 0;
    list-style: none;
}

.breadcrumb a { color: var(--text-secondary); }
.breadcrumb a:hover { color: var(--text); }
.breadcrumb .separator { color: var(--gray-400); }

/* ======================== SPINNER ======================== */
.spinner {
    display: flex;
    justify-content: center;
    padding: 3rem 0;
}

.spinner::after {
    content: "";
    width: 24px;
    height: 24px;
    border: 2px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}

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

/* ======================== MISC ======================== */
/* Screen-reader-only text: visually hidden but announced (e.g. a loading label). */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.form-switch {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

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

code {
    background: var(--gray-100);
    padding: 0.15em 0.4em;
    border-radius: 4px;
    font-size: 0.8125rem;
}

/* ======================== MODAL ======================== */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 200;
}

.modal-dialog {
    background: var(--card-bg);
    border-radius: var(--radius);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
    width: 100%;
    max-width: 400px;
}

.modal-header {
    padding: 1rem 1.25rem;
    font-weight: 600;
    font-size: 0.9375rem;
    border-bottom: 1px solid var(--border);
}

.modal-body { padding: 1.25rem; }

.modal-footer {
    padding: 0.75rem 1.25rem;
    border-top: 1px solid var(--border);
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
}

/* ======================== MOBILE HEADER ======================== */
.mobile-header {
    display: none;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    background: var(--card-bg);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 50;
}

.mobile-logo {
    width: 24px;
    height: 24px;
}

.mobile-brand {
    font-weight: 600;
    font-size: 1rem;
}

.hamburger-btn {
    background: none;
    border: none;
    cursor: pointer;
    padding: 0.25rem;
    color: var(--text);
    display: flex;
    align-items: center;
}

/* ======================== SIDEBAR TOGGLE (CSS-only, no JS needed) ======================== */
.sidebar-checkbox {
    display: none;
}

/* Sidebar animates its collapse (desktop: negative margin + fade) and its
   off-canvas slide (mobile: transform). */
.sidebar {
    transition: margin-left 0.25s ease, transform 0.25s ease, opacity 0.2s ease;
}

/* Desktop: unchecked = open (default), checked = collapsed.
   Because the sidebar is in-flow, the negative margin reclaims its column for the
   content automatically (no main-content margin toggle needed). We fade it out +
   disable pointer events rather than clip an ancestor — clipping the shell would
   break the sidebar's sticky pinning. On normal screens body{overflow-x:hidden}
   also clips it off the left edge; the fade is what covers the ultra-wide gutter. */
@media (min-width: 769px) {
    .sidebar-checkbox:checked ~ .app-layout .sidebar {
        margin-left: calc(-1 * var(--sidebar-w));
        opacity: 0;
        pointer-events: none;
    }

    .sidebar-checkbox:not(:checked) ~ .app-layout .desktop-expand-tab {
        display: none;
    }

    .sidebar-checkbox:checked ~ .app-layout .desktop-expand-tab {
        display: flex;
    }
}

/* Mobile: the sidebar leaves the flow and becomes a fixed off-canvas overlay
   (unchecked = closed, checked = open). Reset the sticky/in-flow desktop rules. */
@media (max-width: 768px) {
    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        height: 100vh;
        margin-left: 0;
        transform: translateX(-100%);
    }

    .sidebar-checkbox:checked ~ .app-layout .sidebar {
        transform: translateX(0);
    }
}

/* Expand tab: visible when sidebar is closed on desktop */
.desktop-expand-tab {
    position: fixed;
    top: 50%;
    /* Hug the app shell's left edge — 0 on normal screens, the gutter width on
       ultra-wide where the centred shell has pulled away from the viewport. */
    left: max(0px, calc((100vw - var(--app-max)) / 2));
    transform: translateY(-50%);
    z-index: 101;
    background: var(--sidebar-bg);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-left: none;
    border-radius: 0 6px 6px 0;
    width: 20px;
    height: 48px;
    display: none;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--sidebar-text);
    transition: background 0.15s ease;
}

.desktop-expand-tab:hover {
    background: rgba(255, 255, 255, 0.12);
    color: var(--sidebar-text-hover);
}

.desktop-expand-tab svg {
    width: 14px;
    height: 14px;
}

.sidebar-overlay {
    display: none;
}

/* ======================== CONTENT HEADER SPLIT ======================== */
.content-header-split {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
}

.search-bar {
    margin-bottom: 1rem;
    max-width: 400px;
}

/* ======================== MOBILE CARDS (hidden on desktop) ======================== */
.mobile-cards {
    display: none;
}

/* mobile-card-body / mobile-card-row pattern — used by metric tables when
   stacked on mobile (label / value pairs). */
.mobile-card-body {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.mobile-card-row {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: 0.8125rem;
}

.mobile-card-row > span:first-child { color: var(--text-secondary); }
.mobile-card-row > span:last-child  { color: var(--text); font-weight: 500; }

/* ======================== OVERVIEW CARDS ======================== */
/* Top-of-page summary tiles — used by Dashboard.razor and Metrics.razor */
.overview-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-6);
}

.overview-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: var(--space-5);
}

.overview-card .label {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin-bottom: var(--space-1);
}

.overview-card .value {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--text);
}

/* ======================== CARD SECTION HEADER ======================== */
.card-section-header {
    padding: var(--space-4) var(--space-5) 0;
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
}

/* ======================== STATUS BADGES (AI tasks etc.) ======================== */
.status-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.25rem 0.625rem;
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    font-weight: 500;
}

/* Compact status pill with a leading colour dot. The dot inherits the pill's text
   colour, so a caller supplies only a colour class (background + color). Shared by
   order-status, discount-status and newsletter-status badges. */
.dot-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 2px var(--space-2);
    border-radius: var(--radius-pill, 999px);
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
}
.dot-badge::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
}

.status-draft           { background: var(--gray-100);   color: var(--gray-600); }
.status-ready           { background: var(--blue-100);   color: var(--blue-700); }
.status-queued          { background: var(--indigo-100); color: var(--indigo-700); }
.status-inprogress      { background: var(--yellow-100); color: var(--yellow-700); }
.status-prcreated       { background: var(--green-50);   color: var(--green-700); }
.status-inreview        { background: var(--purple-100); color: var(--purple-700); }
.status-rework          { background: var(--red-50);     color: var(--red-700); }
.status-approved        { background: var(--green-50);   color: var(--green-700); }
.status-merged          { background: var(--cyan-100);   color: var(--cyan-700); }
.status-done            { background: var(--green-50);   color: var(--green-700); }
.status-failed          { background: var(--red-50);     color: var(--red-700); }
.status-abandoned       { background: var(--gray-100);   color: var(--gray-500); }
.status-pending         { background: var(--yellow-100); color: var(--yellow-700); }
.status-rejected        { background: var(--red-50);     color: var(--red-700); }
.status-overridden      { background: var(--indigo-100); color: var(--indigo-700); }
.status-selfreviewing   { background: var(--purple-100); color: var(--purple-700); }
.status-qualitychecking { background: var(--yellow-100); color: var(--yellow-700); }
.status-proposed        { background: var(--yellow-100); color: var(--yellow-700); }

/* ======================== TEXT UTILITIES ======================== */
.text-success   { color: var(--green); }
.text-warning   { color: var(--yellow); }
.text-danger    { color: var(--red); }
.text-secondary { color: var(--text-secondary); }

/* ======================== SPACING UTILITIES ======================== */
.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.mb-5 { margin-bottom: var(--space-5); }
.mb-6 { margin-bottom: var(--space-6); }

/* Top-margin counterparts. Same scale, same tokens — for the cases where the spacing belongs
   to the element that needs it rather than to whatever happens to precede it. */
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }
.mt-6 { margin-top: var(--space-6); }

/* ======================== METRICS / TRENDS CHART ======================== */
.trends-chart {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 120px;
    padding: var(--space-4) var(--space-5);
}

.trend-bar-group {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100%;
    min-width: 0;
}

.trend-bars {
    display: flex;
    gap: 1px;
    align-items: flex-end;
    height: 100%;
    width: 100%;
}

.trend-bar {
    flex: 1;
    border-radius: 1px 1px 0 0;
    min-height: 2px;
    transition: height 0.3s ease;
}

.trend-bar-completed { background: var(--green); }
.trend-bar-failed    { background: var(--red); }
.trend-bar-empty     { background: var(--gray-200); }

.trend-label {
    font-size: 0.625rem;
    color: var(--text-secondary);
    margin-top: var(--space-1);
    white-space: nowrap;
}

.trends-legend {
    display: flex;
    gap: var(--space-4);
    justify-content: center;
    padding: var(--space-2) var(--space-5) var(--space-4);
}

.legend-item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.legend-dot {
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 2px;
}

.legend-completed { background: var(--green); }
.legend-failed    { background: var(--red); }

/* ======================== FEATURE TREE (scrollable list of permissions) ======================== */
.feature-tree-scroll {
    max-height: min(60vh, 480px);
    overflow-y: auto;
    overscroll-behavior: contain;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.75rem;
    background: var(--gray-50);
    scrollbar-width: thin;
    scrollbar-color: var(--gray-300) transparent;
}

.feature-tree-scroll::-webkit-scrollbar { width: 6px; }
.feature-tree-scroll::-webkit-scrollbar-thumb {
    background: var(--gray-300);
    border-radius: 3px;
}
.feature-tree-scroll::-webkit-scrollbar-track { background: transparent; }

/* ======================== FAILURE PATTERNS ======================== */
.failure-pattern {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border);
}

.failure-pattern:last-child { border-bottom: none; }

.failure-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2rem;
    height: 1.5rem;
    padding: 0 var(--space-2);
    border-radius: var(--radius-sm);
    background: var(--red-50);
    color: var(--red-700);
    font-size: var(--text-sm);
    font-weight: 600;
    flex-shrink: 0;
}

.failure-text {
    font-size: var(--text-sm);
    word-break: break-all;
    color: var(--text-secondary);
}

/* ======================== FORM STATUS PILL ======================== */
/* Inline status label with a colored dot, used by FormService and
   EmailService list pages: <span class="form-status active">. */
.form-status {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
}

.form-status.active { color: var(--green); }
.form-status.active .status-dot { background: var(--green); }
.form-status.inactive { color: var(--text-secondary); }
.form-status.inactive .status-dot { background: var(--gray-400); }

/* ======================== STATUS BADGE (sent/failed) ======================== */
.email-status {
    display: inline-flex;
    align-items: center;
    padding: 0.2em 0.6em;
    border-radius: var(--radius-full);
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1;
}

.email-status.sent { background: var(--green-50); color: var(--green-700); }
.email-status.failed { background: var(--red-50); color: var(--red-700); }

/* ======================== VALIDATION SUMMARY ======================== */
.validation-summary {
    margin: 0 0 var(--space-4);
    padding: 0.75rem 1rem 0.75rem 1.5rem;
    list-style: disc inside;
    background: var(--red-50);
    border: 1px solid var(--red-200);
    border-radius: var(--radius-md);
    color: var(--red-700);
    font-size: 0.8125rem;
}

.validation-summary ul { margin: 0; padding-left: 0.5rem; }

/* ======================== FILTER BAR ======================== */
.filter-bar {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    align-items: center;
    margin-bottom: 1.5rem;
}

/* One control in a filter bar. Both keep a usable minimum and wrap onto their own line
   on narrow screens; the free-text search takes whatever room is left over. */
.filter-bar > .filter-control { flex: 0 1 14rem; min-width: 12rem; }
.filter-bar > .filter-search { flex: 1 1 16rem; min-width: 12rem; }

/* ======================== PAGINATION ======================== */
.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-top: 1.5rem;
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

/* ======================== ENDPOINT DISPLAY + COPY ======================== */
/* A read-only URL/code value paired with a copy-to-clipboard button. */
.endpoint-display {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.endpoint-display code {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.endpoint-display--inline {
    display: inline-flex;
    margin-top: 0.25rem;
    max-width: 100%;
}

.copy-btn {
    flex-shrink: 0;
    padding: 0.25rem 0.625rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    color: var(--text-secondary);
    font-size: 0.75rem;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    transition: all 0.15s ease;
}

.copy-btn:hover {
    background: var(--gray-100);
    color: var(--text);
    border-color: var(--gray-300);
}

/* ======================== SUBMISSION META ======================== */
.submission-count {
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

.sub-preview {
    max-width: 360px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-secondary);
    font-size: 0.8125rem;
}

/* ======================== RESPONSIVE ======================== */
@media (max-width: 768px) {
    /* Tighten the page gutter on phones (one knob drives header + body). */
    :root {
        --content-pad-x: 1rem;
    }

    .sidebar-overlay {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.4);
        z-index: 99;
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.25s ease;
        cursor: default;
    }

    .sidebar-checkbox:checked ~ .app-layout .sidebar-overlay {
        opacity: 1;
        pointer-events: auto;
    }

    /* Trim vertical page padding on phones (horizontal comes from the token). */
    .main-content {
        padding-top: var(--space-4);
        padding-bottom: var(--space-6);
    }

    .mobile-header {
        display: flex;
    }

    /* Hide desktop expand tab on mobile */
    .desktop-expand-tab {
        display: none !important;
    }

    /* Header/body padding is owned by .main-content (via --content-pad-x), so
       no per-element padding override is needed here — only the split-header
       layout needs to stack. */
    .content-header-split {
        flex-direction: column;
        gap: 0.75rem;
    }

    /* Hide desktop tables, show mobile cards */
    .desktop-table {
        display: none;
    }

    .mobile-cards {
        display: flex;
        flex-direction: column;
        gap: 0.75rem;
    }

    .mobile-card {
        background: var(--card-bg);
        border: 1px solid var(--border);
        border-radius: var(--radius);
        box-shadow: var(--shadow-sm);
        padding: 1rem;
    }

    .mobile-card-header {
        display: flex;
        justify-content: space-between;
        align-items: flex-start;
        margin-bottom: 0.5rem;
    }

    .mobile-card-title {
        font-weight: 500;
        font-size: 0.9375rem;
        color: var(--text);
    }

    .mobile-card-title a {
        color: var(--text);
    }

    .mobile-card-meta {
        display: flex;
        flex-wrap: wrap;
        gap: 0.5rem 1rem;
        font-size: 0.8125rem;
        color: var(--text-secondary);
        margin-bottom: 0.75rem;
    }

    .mobile-card-meta-item {
        display: flex;
        align-items: center;
        gap: 0.25rem;
    }

    .mobile-card-actions {
        display: flex;
        gap: 0.5rem;
        flex-wrap: wrap;
        padding-top: 0.75rem;
        border-top: 1px solid var(--border);
    }

    /* ── Responsive tables → cards on phone ─────────────────────────────
       Any .data-table automatically stacks into one card per row. Each
       <td> shows its column name from data-label="…"; cells without the
       attribute simply stack their value (graceful for un-retrofitted
       tables elsewhere on the platform). This replaces hand-rolled
       per-page mobile-card markup. */

    /* The table wrapper stops being a card itself — the rows become the
       cards — so we don't double-frame or force horizontal scroll. */
    .table-card {
        border: none;
        box-shadow: none;
        background: transparent;
        overflow-x: visible;
    }

    /* The column-resize JS (js/table-resize.js) pins an INLINE
       `table-layout: fixed` plus a pixel `width` summed from the header
       row's measured columns. On phone the header is hidden, so it measures
       0 and collapses the table to width:0 — crushing every cell to a single
       character. Inline styles only yield to !important, so reclaim the
       block/auto model here. */
    .data-table {
        display: block !important;
        width: 100% !important;
        table-layout: auto !important;
        /* Undo the desktop spaced-card model — rows become full cards below. */
        border-collapse: collapse;
        border-spacing: 0;
    }

    .data-table tbody {
        display: block;
        width: 100%;
    }

    .data-table thead {
        display: none;
    }

    .data-table tbody tr {
        display: block;
        background: var(--card-bg);
        border: 1px solid var(--border);
        border-radius: var(--radius);
        box-shadow: var(--shadow-sm);
        padding: 0.25rem 1rem;
        margin-bottom: 0.75rem;
    }

    .data-table tbody tr:last-child {
        margin-bottom: 0;
    }

    /* Row hover is a desktop affordance — neutralise it in card mode. */
    .data-table tbody tr:hover {
        background: var(--card-bg);
    }

    /* Block cells with an absolutely-positioned label gutter. NOT flexbox:
       a flex value item with overflow-wrap collapses to its 1-character
       min-content width and wraps vertically. A block cell always keeps
       its full inline width, so long values wrap like normal prose. */
    .data-table tbody td {
        display: block;
        position: relative;
        padding: 0.5rem 0;
        text-align: left;
        overflow-wrap: anywhere;
        word-break: break-word;
        /* Reset the desktop spaced-card cell borders/radius — the row itself is
           the card here. */
        border: none;
        border-bottom: 1px solid var(--gray-100);
        border-radius: 0;
    }

    /* Kill the desktop first/last-cell side borders + radius. Kept as a
       borders-only rule (NOT touching padding) so it doesn't out-specify the
       td[data-label] label-gutter padding below. */
    .data-table tbody td:first-child,
    .data-table tbody td:last-child {
        border-left: none;
        border-right: none;
        border-radius: 0;
    }

    /* Neutralise the desktop accent hover in card mode. */
    .data-table tbody tr:hover td { border-color: transparent; border-bottom-color: var(--gray-100); }

    /* Only labelled cells reserve the left gutter for their caption; cells with
       no data-label (un-retrofitted tables elsewhere) just stack full-width. */
    .data-table td[data-label] {
        padding-left: 6.5rem;
        min-height: 1.75rem;
    }

    /* Numeric cells stack left in card mode (the desktop right-align would push
       the value to the card's far edge, away from its label). */
    .data-table td.num,
    .data-table td.text-end { text-align: left; }

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

    /* The desktop width cap is only there to tame columns; in card mode the
       value should use the full card width. */
    .data-table .cell-wrap {
        max-width: none;
    }

    .data-table td[data-label]::before {
        content: attr(data-label);
        position: absolute;
        left: 0;
        top: 0.5rem;
        width: 6rem;
        font-size: 0.6875rem;
        font-weight: 600;
        line-height: 1.5;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        color: var(--text-secondary);
    }

    /* Action cells: full-width flex row (the buttons below stretch to fill the
       card), overriding the desktop inline-cell layout. */
    .data-table td.cell-actions {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-2);
        padding-left: 0;
        padding-top: 0.75rem;
    }
    /* gap handles spacing here — clear the desktop inter-button margin. */
    .data-table td.cell-actions > * + * {
        margin-left: 0;
    }

    .data-table td.cell-actions::before {
        display: none;
    }

    .data-table td.cell-actions .btn-group {
        display: flex;
        flex-wrap: wrap;
        width: 100%;
    }

    /* Buttons share the row and grow to fill it, but keep a sensible minimum
       so a long action set wraps onto extra rows instead of shrinking each
       button until its label breaks mid-word. Covers buttons placed directly in
       the cell as well as those inside a .btn-group. */
    .data-table td.cell-actions > .btn,
    .data-table td.cell-actions > button,
    .data-table td.cell-actions > a,
    .data-table td.cell-actions .btn-group,
    .data-table td.cell-actions .btn-group > .btn,
    .data-table td.cell-actions .btn-group > button,
    .data-table td.cell-actions .btn-group > a {
        flex: 1 1 auto;
        min-width: 5rem;
    }

    /* Detail grid: stack on mobile */
    .detail-row {
        grid-template-columns: 1fr;
        gap: 0.25rem;
    }

    /* Stat grid: full width on small screens */
    .stat-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    /* Button groups: wrap */
    .btn-group {
        flex-wrap: wrap;
    }

    /* Page actions */
    .page-actions {
        flex-wrap: wrap;
    }

    /* Modal: nearly full width */
    .modal-dialog {
        max-width: calc(100% - 2rem);
        margin: 1rem;
    }

    /* Cards: no max-width constraint on mobile */
    .card[style*="max-width"] {
        max-width: 100% !important;
    }

    /* Breadcrumbs: allow wrapping */
    .breadcrumb {
        flex-wrap: wrap;
    }

    /* Code: prevent horizontal overflow */
    code {
        word-break: break-all;
    }

    /* Full-width buttons on mobile */
    .btn-group .btn {
        flex: 1;
        justify-content: center;
        text-align: center;
    }
}

@media (max-width: 400px) {
    :root {
        --content-pad-x: 0.75rem;
    }

    .stat-grid {
        grid-template-columns: 1fr;
    }
}

/* Setting info icon + tooltip */
.setting-info {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    margin-left: 0.375rem;
    font-size: 0.6875rem;
    font-weight: 600;
    line-height: 1;
    color: var(--accent);
    border: 1.5px solid var(--accent);
    border-radius: 50%;
    cursor: help;
    vertical-align: middle;
    flex-shrink: 0;
    user-select: none;
}

.setting-info .setting-tooltip {
    display: none;
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    background: var(--text);
    color: #fff;
    font-size: 0.75rem;
    font-weight: 400;
    line-height: 1.4;
    padding: 0.5rem 0.625rem;
    border-radius: 6px;
    white-space: normal;
    width: max-content;
    max-width: 280px;
    z-index: 100;
    pointer-events: none;
    text-align: left;
}

.setting-info .setting-tooltip::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--text);
}

.setting-info:hover .setting-tooltip {
    display: block;
}

/* ── App Hosting settings form ─────────────────────────────────────────── */
.settings-form { display: flex; flex-direction: column; gap: 1.25rem; }
/* One tab's worth of sections; same vertical rhythm as the form itself. */
.settings-tabpanel { display: flex; flex-direction: column; gap: 1.25rem; }
/* A class's display:flex outranks the UA [hidden]{display:none}, so re-hide explicitly. */
.settings-tabpanel[hidden] { display: none; }
.settings-group { display: flex; flex-direction: column; gap: 0.5rem; }
.settings-group-title {
    margin: 0; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-secondary); border-bottom: 1px solid var(--border); padding-bottom: 0.25rem;
}
.settings-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 0.75rem; align-items: start;
}
.settings-grid label, .settings-form section label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.875rem; }
.settings-wide { grid-column: 1 / -1; }
/* Two even columns instead of auto-fit's variable count — use when the grid ALSO holds a
   .settings-wide field: that field spans every track, so auto-fit can't collapse the empty
   ones and a 2-field row would otherwise fill only 2 of many narrow columns (half width). */
.settings-grid--pairs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 640px) { .settings-grid--pairs { grid-template-columns: 1fr; } }
.settings-checks { display: flex; flex-wrap: wrap; gap: 1rem; }
.settings-check { display: flex; flex-direction: row; align-items: center; gap: 0.4rem; }
.settings-actions { display: flex; gap: 0.5rem; margin-top: 1rem; }
.managed-env {
    background: var(--gray-50); border: 1px solid var(--border); border-radius: var(--radius-sm, 4px);
    padding: 0.6rem 0.85rem; margin-top: 0.5rem; font-size: 0.85rem;
}
.managed-env-title { font-weight: 600; }
.managed-env ul { margin: 0.4rem 0; padding-left: 1.1rem; }
.managed-env code { font-size: 0.8125rem; }

/* Separates a standalone block (e.g. the Groups selector) from the form above it. */
.form-section-gap { margin-top: 1.5rem; }

/* ── Env var table editor (image-app advanced settings) ─────────────────── */
.env-editor { display: flex; flex-direction: column; gap: 0.5rem; }
.env-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.env-editor-title { font-size: 0.875rem; font-weight: 600; }
.env-table { display: flex; flex-direction: column; gap: 0.4rem; }
.env-row {
    display: grid;
    grid-template-columns: minmax(6rem, 1fr) minmax(8rem, 1.6fr) minmax(7rem, 1.4fr) auto auto;
    gap: 0.5rem;
    align-items: center;
}
.env-row--head {
    font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-secondary); font-weight: 600;
}
.env-value { display: flex; gap: 0.4rem; align-items: center; }
.env-value .form-control { flex: 1; min-width: 0; }
.env-col-secret { justify-self: center; text-align: center; }
.env-secret-toggle { display: inline-flex; align-items: center; justify-content: center; margin: 0; }
.env-col-actions { justify-self: center; }

@media (max-width: 640px) {
    /* Stack each variable as its own block so the table never overflows on phones. */
    .env-row { grid-template-columns: 1fr; gap: 0.35rem; }
    .env-row--head { display: none; }
    .env-row { border: 1px solid var(--border); border-radius: var(--radius); padding: 0.6rem; }
    .env-col-secret, .env-col-actions { justify-self: start; }
}

/* Inline deploy failure reason under a status badge — visible without hovering. */
.deploy-error {
    margin-top: 0.3rem; max-width: 28rem;
    font-size: 0.8125rem; line-height: 1.4;
    color: var(--danger, #b42318); word-break: break-word; overflow-wrap: anywhere;
}

/* Clickable failure indicator beside a status badge — opens a modal with the full error. */
.error-icon-btn {
    margin-left: 0.4rem; padding: 0 0.25rem;
    background: none; border: none; cursor: pointer;
    color: var(--danger, #b42318); font-size: 1rem; line-height: 1;
}
.error-icon-btn:hover { opacity: 0.75; }
/* Full error text inside the error modal. */
.error-detail {
    margin: 0; white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere;
    font-size: 0.8125rem; line-height: 1.4;
}

/* ======================== KANBAN & POLISH (added) ======================== */
/* Reusable kanban board — columns take a --stage-color for a coloured accent. */
.kanban {
    display: flex;
    gap: var(--space-4);
    overflow-x: auto;
    padding-bottom: var(--space-3);
    -webkit-overflow-scrolling: touch;
}
.kanban-col {
    flex: 1 0 264px;
    min-width: 264px;
    background: var(--gray-50);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-3);
}
.kanban-col-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: var(--space-3);
    margin-bottom: var(--space-3);
    border-bottom: 2px solid var(--stage-color, var(--border));
    font-weight: 600;
    font-size: var(--text-base);
    color: var(--text);
}
.kanban-count {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    padding: 1px 9px;
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--text-secondary);
}
.kanban-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-left: 3px solid var(--stage-color, var(--border));
    border-radius: var(--radius-md);
    padding: var(--space-3);
    box-shadow: var(--shadow-sm);
    margin-bottom: var(--space-2);
    transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}
.kanban-card:last-child { margin-bottom: 0; }
.kanban-card:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}
.kanban-card-title { font-weight: 600; color: var(--text); line-height: 1.3; }
.kanban-card-meta { font-size: var(--text-xs); color: var(--text-secondary); margin-top: 2px; }
.kanban-card-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: var(--space-2);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.kanban-select { margin-top: var(--space-3); font-size: var(--text-sm); }
.kanban-empty { color: var(--text-secondary); font-size: var(--text-sm); text-align: center; padding: var(--space-5) 0; }

/* Section headings between tables/cards on a page. */
.section-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
    margin: var(--space-6) 0 var(--space-3);
}

/* Gentle interactivity on cards and table rows. */
.card { transition: box-shadow 0.18s ease, border-color 0.18s ease; }
.data-table tbody tr { transition: background-color 0.12s ease; }
.data-table tbody tr:hover { background: var(--gray-50); }

/* ======================== PRINTABLE DOCUMENT (added) ======================== */
/* An on-screen "sheet" that prints cleanly on its own (invoices, quotes…). */
/* Document toolbar: pick which template renders/prints, actions pushed right. */
.doc-template-picker { display: flex; align-items: center; gap: var(--space-2); }
.doc-template-picker > span { font-size: var(--text-sm); color: var(--text-secondary); }
.doc-template-picker select { min-width: 190px; }
.doc-toolbar-spacer { flex: 1 1 auto; }

.doc-sheet {
    background: var(--card-bg);
    color: var(--text);
    max-width: 820px;
    margin: 0 auto;
    padding: var(--space-8);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
}
.doc-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-6); }
.doc-title { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.01em; }
.doc-issuer { font-weight: 600; }
.doc-muted { color: var(--text-secondary); font-size: var(--text-sm); }
.doc-parties { display: flex; justify-content: space-between; gap: var(--space-6); margin: var(--space-7) 0 var(--space-6); }
.doc-label { text-transform: uppercase; letter-spacing: 0.05em; font-size: var(--text-xs); font-weight: 600; color: var(--text-secondary); margin-bottom: var(--space-2); }
.doc-table { width: 100%; border-collapse: collapse; margin-top: var(--space-4); }
.doc-table th { text-align: left; font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-secondary); padding: var(--space-2) var(--space-3); border-bottom: 2px solid var(--border); }
.doc-table td { padding: var(--space-3); border-bottom: 1px solid var(--border); font-variant-numeric: tabular-nums; }
.doc-table td.num, .doc-table th.num { text-align: right; }
.doc-totals { margin-left: auto; margin-top: var(--space-5); width: 320px; max-width: 100%; }
.doc-totals .row { display: flex; justify-content: space-between; padding: var(--space-2) 0; font-variant-numeric: tabular-nums; }
.doc-totals .row.grand { border-top: 2px solid var(--border); margin-top: var(--space-2); padding-top: var(--space-3); font-weight: 700; font-size: 1.05rem; }
.doc-totals .row.due { color: var(--text); font-weight: 600; }

@media print {
    body * { visibility: hidden !important; }
    .doc-sheet, .doc-sheet * { visibility: visible !important; }
    .doc-sheet { position: absolute; inset: 0; width: 100%; max-width: none; margin: 0; border: none; border-radius: 0; box-shadow: none; padding: 0; }
    .no-print { display: none !important; }
    body { background: #fff !important; }
}

/* ======================== TABS & SWITCH (added) ======================== */
.tabs {
    display: flex;
    gap: var(--space-1);
    border-bottom: 1px solid var(--border);
    margin-bottom: var(--space-4);
    flex-wrap: wrap;
}
.tab {
    padding: var(--space-2) var(--space-3);
    border: none;
    background: none;
    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--text-secondary);
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color 0.12s ease, border-color 0.12s ease;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
/* A dot after a tab label flags that the tab holds a validation error to fix. */
.tab-error-dot {
    display: inline-block; width: 0.4rem; height: 0.4rem; margin-left: 0.35rem;
    border-radius: 50%; background: var(--red-600); vertical-align: middle;
}

/* Toggle switch — <label class="switch"><input type="checkbox"><span class="track"></span> Label</label> */
.switch { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; font-size: var(--text-sm); user-select: none; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
    width: 36px; height: 20px; flex: none; position: relative;
    background: var(--gray-300); border-radius: var(--radius-full);
    transition: background 0.15s ease;
}
.switch .track::after {
    content: ""; position: absolute; top: 2px; left: 2px;
    width: 16px; height: 16px; border-radius: 50%;
    background: #fff; box-shadow: var(--shadow-sm);
    transition: transform 0.15s ease;
}
.switch input:checked + .track { background: var(--accent); }
.switch input:checked + .track::after { transform: translateX(16px); }
.switch input:focus-visible + .track { outline: 2px solid var(--accent); outline-offset: 2px; }

.field-hint { font-size: var(--text-xs); color: var(--text-secondary); margin: calc(-1 * var(--space-2)) 0 var(--space-3); }

/* ---- Segmented control (a small set of mutually-exclusive modes) ---- */
/* The container is the RECESSED surface and the active button the raised one, and that ordering
   has to survive both themes. It used to pair --gray-100 with --card-bg, which is correct in light
   (#f3f4f6 behind #fff) and inverted in dark: --card-bg #1c2538 is darker than --gray-100 #243049,
   so the selected segment sank into its own container and the unselected ones read as chosen.
   --content-bg is below --card-bg in both themes, which is the relationship this control needs. */
.segmented { display: inline-flex; gap: 2px; padding: 3px; background: var(--content-bg); border-radius: var(--radius-md); border: 1px solid var(--border); }
.segmented button {
    border: none; background: transparent; cursor: pointer;
    padding: 0.4rem 0.9rem; border-radius: calc(var(--radius-md) - 3px);
    font-size: var(--text-sm); font-weight: 600; color: var(--text-secondary);
    transition: background .12s, color .12s;
}
.segmented button:hover { color: var(--text-primary); }
.segmented button.active { background: var(--card-bg); color: var(--accent); box-shadow: var(--shadow-sm); font-weight: 600; }

/* ---- Visual document builder ---- */
.builder-block {
    display: flex; align-items: center; gap: var(--space-2);
    padding: 0.55rem 0.7rem; border: 1px solid var(--border); border-radius: var(--radius-md);
    background: var(--card-bg); cursor: pointer; margin-bottom: var(--space-2);
    transition: border-color .12s, box-shadow .12s;
}
.builder-block:hover { border-color: var(--accent); }
.builder-block.selected { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring, rgba(109,40,217,.15)); }
.builder-block .bb-icon { width: 22px; height: 22px; flex-shrink: 0; border-radius: 6px; background: var(--accent-soft, #f1ecfb); color: var(--accent); display: grid; place-items: center; font-size: 0.7rem; font-weight: 700; }
.builder-block .bb-label { flex: 1 1 auto; min-width: 0; font-weight: 600; font-size: var(--text-sm); }
.builder-block .bb-sub { color: var(--text-secondary); font-weight: 400; }
.builder-block .bb-actions { display: flex; gap: 2px; flex-shrink: 0; }
.builder-block .bb-actions button { border: none; background: transparent; cursor: pointer; color: var(--text-secondary); padding: 3px; border-radius: 5px; line-height: 0; }
.builder-block .bb-actions button:hover { background: var(--gray-100, #f3f4f6); color: var(--text-primary); }
.builder-block .bb-actions button:disabled { opacity: .3; cursor: default; }

.builder-add { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.builder-add button {
    border: 1px dashed var(--border); background: transparent; cursor: pointer;
    padding: 0.35rem 0.6rem; border-radius: var(--radius-md); font-size: var(--text-xs); font-weight: 600; color: var(--text-secondary);
}
.builder-add button:hover { border-color: var(--accent); color: var(--accent); border-style: solid; }

.swatch-row { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.swatch { width: 30px; height: 30px; border-radius: 7px; border: 2px solid transparent; cursor: pointer; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.swatch.selected { border-color: var(--accent); }

/* ---- Document studio: variable guide (clickable Scriban tokens) ---- */
.var-guide { margin-top: var(--space-4); border-top: 1px solid var(--border); padding-top: var(--space-4); }
.var-group { margin-bottom: var(--space-3); }
.var-group-label { font-size: var(--text-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--text-secondary); margin-bottom: var(--space-2); }
.var-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.var-chip {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 3px 8px; border: 1px solid var(--border); border-radius: var(--radius-md);
    background: var(--card-bg); color: var(--text-primary);
    font-family: var(--font-mono, monospace); font-size: 11px; cursor: pointer;
    transition: background .12s, border-color .12s, color .12s;
}
.var-chip:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.var-chip.block { font-family: inherit; font-weight: 500; }
.var-chip .var-desc { color: var(--text-secondary); font-family: inherit; }
.var-chip:hover .var-desc { color: #fff; }

/* ---- Terminal surface (<Terminal>, backed by xterm.js) ----
   Keeps its own dark palette in both themes: the content is a real ANSI stream whose colours
   assume a dark background. Sized by its container, and scrolls internally so a long session
   never pushes the page sideways. */
.terminal-surface {
    background: var(--terminal-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    /* NO padding on this element. It is the box the fit addon measures, and clientHeight
       includes padding — so padding here made it compute more rows than actually fit, and the
       last ones were clipped by the overflow below. That is why the two lines you were typing
       on were the two you could not see. The inset is put back on the scrolling content. */
    padding: 0;
    height: 30rem;
    max-height: 70vh;
    overflow: hidden;
}
.terminal-surface .xterm-viewport { overflow-y: auto; }
.terminal-surface .xterm { padding: var(--space-3); }

@media (max-width: 768px) {
    .terminal-surface { height: 22rem; }
    .terminal-surface .xterm { padding: var(--space-2); }
}

/* ── profile picture ─────────────────────────────────────────────────────────
   The upload on /profile. The picture itself is drawn wherever people appear;
   this is only the editor. */
.profile-picture-row {
    display: flex;
    gap: var(--space-4);
    align-items: flex-start;
    flex-wrap: wrap;
}

.profile-picture {
    width: 5rem;
    height: 5rem;
    border-radius: var(--radius-full);
    object-fit: cover;
    flex: 0 0 auto;
    border: 1px solid var(--border);
}

.profile-picture-empty {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--gray-200);
    color: var(--text);
    font-weight: 600;
    font-size: var(--text-lg);
}

.profile-picture-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    align-items: flex-start;
    min-width: 14rem;
    flex: 1;
}

/* ── Agent session log ───────────────────────────────────────────────────────
   A live stream of what an agent is doing. Monospace because most of it is
   tool output and paths, where proportional spacing costs you the ability to
   scan a column. It scrolls in its own box rather than growing the page: a
   session that has been running an hour must not push its own compose box off
   the bottom of the screen. */
.session-log {
  list-style: none;
  margin: 0 0 var(--space-4) 0;
  padding: var(--space-3);
  max-height: 28rem;
  overflow-y: auto;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--text-sm);
}

.session-log-line {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--border);
}

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

.session-log-kind {
  flex: 0 0 auto;
  min-width: 6rem;
  color: var(--text-secondary);
}

/* Wrap rather than scroll sideways: the page body must never scroll
   horizontally, and agent output contains long paths and JSON. */
.session-log-text {
  flex: 1 1 auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.session-compose {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-items: flex-end;
}

.session-compose .form-control { width: 100%; }
