/* Compossa.UI.Blazor — Component Styles
 *
 * Shipped as a static web asset of the RCL. Hosts opt in by adding:
 *   <link rel="stylesheet" href="_content/Compossa.UI.Blazor/components.css" />
 * BEFORE their own app.css link (so host overrides win).
 *
 * Design tokens (--accent-color, --bg-primary, --primary, --radius-*, --font-mono, …)
 * are defined HERE in the canonical :root block below. Hosts override only the
 * tokens that differ from default — typically via body.theme-light or similar
 * theme-variant selectors (higher specificity than :root). Host-specific tokens
 * NOT used by RCL components (e.g. --avatar-bg, --onboarding-color in Commerce.Web)
 * stay in the host's app.css.
 *
 * Previously each host duplicated the entire token set in its own :root block,
 * which guaranteed drift — Commerce.Web missed --primary, --surface-secondary,
 * --text-tertiary, etc., causing scoped RCL CSS (e.g. .tag-chip pill background)
 * to resolve var() calls to nothing and render unstyled. Single source of truth
 * here eliminates that class of bug.
 */

/* ==================== Canonical Design Tokens (dark default) ==================== */
:root {
    /* ----- Brand colors ----- */
    --primary: #81BA2A;
    --primary-color: var(--primary);
    --accent-color: #81BA2A;
    --accent-hover: #97DF18;
    --accent-text: #ffffff;
    --accent-rgb: 129, 186, 42;
    --dark-color: #535D62;

    /* ----- Semantic colors ----- */
    --success-color: #81BA2A;
    --success-color-light: rgba(129, 186, 42, 0.15);
    --success-color-muted: rgba(129, 186, 42, 0.6);
    --success-text-color: var(--success-color);
    --warning-color: #F7931E;
    --warning-color-light: rgba(247, 147, 30, 0.15);
    --error-color: #E52520;
    --info-color: #75BCFF;
    --info-color-muted: rgba(117, 188, 255, 0.6);
    --info-bg: rgba(117, 188, 255, 0.1);
    --bg-accent-subtle: rgba(117, 188, 255, 0.12);

    /* ----- Chart series (categorical, colour-blind safe) -----
       A fixed sequence for multi-series charts (e.g. usage-by-model). chart-1..5 are the Okabe-Ito
       CVD-safe core (blue / orange / green / reddish-purple / vermillion), brightened for the dark
       surface; 6..10 extend it. Deliberately distinct from the brand/skin accent AND the semantic
       status colours, so a series never reads as "cost" (gold), "healthy" (green) or "breach" (red),
       and there's no yellow (reserved for cost). Theme-independent EXCEPT .theme-light, which darkens
       them to read on its light surface (same hue per slot). Consumers cap at ~10, then lump to "Other". */
    --chart-1: #56B4E9;
    --chart-2: #E69F00;
    --chart-3: #1CC9A0;
    --chart-4: #E58FBE;
    --chart-5: #F2784B;
    --chart-6: #9C8CFF;
    --chart-7: #59C3C9;
    --chart-8: #D0A0E0;
    --chart-9: #C2885A;
    --chart-10: #9AA0A6;

    /* ----- Surfaces (dark default) ----- */
    --bg-primary: #1e1e1e;
    --bg-secondary: #252526;
    --bg-tertiary: #333333;
    --bg-hover: #3c3c3c;
    --bg-active: #4a4a4a;
    --bg-selected: rgba(129, 186, 42, 0.2);
    --bg-subtle: var(--bg-primary);
    --bg-elevated: var(--bg-secondary);
    --bg-code: var(--bg-tertiary);
    --bg-surface: var(--bg-secondary);
    --surface-secondary: #333333;
    --input-bg: #2a2a2a;

    /* ----- Text (dark default) ----- */
    --text-primary: #ffffff;
    --text-secondary: #cccccc;
    --text-muted: #858585;
    --text-tertiary: #6e6e6e;
    --text: var(--text-primary);
    --text-color: var(--text-primary);
    --text-code: var(--text-primary);
    --error-text-code: var(--error-color);
    --success-text-code: var(--success-color);

    /* ----- Borders ----- */
    --border-color: #3c3c3c;
    --border: var(--border-color);
    --border-subtle: var(--border-color);
    --border-color-subtle: var(--border-color);

    /* ----- Radii ----- */
    --corner-radius: 6px;
    --radius-xs: 4px;
    --radius-sm: 6px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --radius-full: 100px;

    /* ----- Links ----- */
    --link-color: #75BCFF;
    --link-hover: #9CDCFE;

    /* ----- Typography ----- */
    --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    --font-monospace: var(--font-mono);

    /* ----- Layout offsets ----- */
    --command-bar-left: 0;

    /* ----- Buttons (RCL component defaults; hosts may override) ----- */
    --button-primary-bg: var(--accent-color);
    --button-primary-hover: var(--accent-hover);
    --button-primary-text: var(--accent-text);
    --button-secondary-bg: var(--bg-secondary);
    --button-secondary-hover: var(--bg-hover);
    --button-secondary-text: var(--text-primary);

    /* ----- Chrome (hosts may override with brand variants) ----- */
    --filter-bar-bg: var(--bg-secondary);
    --secondary-accent: var(--dark-color);
}

/* Re-substitute the alias tokens where host themes actually override their bases.
   A var() inside a custom-property VALUE is substituted on the element that defines it —
   so the :root aliases above (--bg-subtle: var(--bg-primary), --border-subtle: …) freeze
   against the dark-shell defaults at :root, and a host theme that only sets the base
   tokens (--bg-primary, --border-color, …) on the shell element (.app-shell.theme-*)
   never reaches them: light themes rendered chat bubbles / tool chips / code blocks
   near-black. Re-declaring the aliases on .app-shell makes substitution happen on the
   element the theme classes live on, so they track the themed bases in every host.
   Deliberately EXCLUDES --filter-bar-bg / --secondary-accent / --button-* — hosts set
   those directly inside theme blocks and an element-level re-alias would race them on
   source order. */
.app-shell {
    --bg-subtle: var(--bg-primary);
    --bg-elevated: var(--bg-secondary);
    --bg-code: var(--bg-tertiary);
    --bg-surface: var(--bg-secondary);
    --text: var(--text-primary);
    --text-color: var(--text-primary);
    --text-code: var(--text-primary);
    --error-text-code: var(--error-color);
    --success-text-code: var(--success-color);
    --border: var(--border-color);
    --border-subtle: var(--border-color);
    --border-color-subtle: var(--border-color);

    /* Base shell paint (display/height/background/color/font) — shared across every
       .app-shell root regardless of host component. Blazor's CSS scope isolation means a
       rule declared in one component's .razor.css (e.g. MainLayout) never reaches an
       .app-shell rendered by a DIFFERENT component (e.g. Composer.UI's KioskLayout) even
       though both use the same class — this is the one home both can actually reach. Found
       live: KioskLayout correctly resolved the org's dark theme onto .app-shell.theme-dark,
       but the page still rendered white because nothing painted the shell background itself. */
    display: flex;
    flex-direction: column;
    height: 100vh; /* NOT min-height — locks the shell to the viewport per ux-rule-host-shell-height-locked-no-outer-scrollbars (01KWJEVXXXPXC3C1PT4QV9BYA8) */
    overflow: hidden; /* No outer <html> scrollbar — scroll responsibility falls through to ContentPanel's own .content-panel-body */
    background: var(--bg-primary);
    color: var(--text-secondary);
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* The ONE surface-based override of the app-global chart palette: .theme-light has a light background,
   so the dark-tuned series above need darker, higher-contrast variants (same hue per slot, so a series
   keeps its identity across surfaces). The accent themes (ember/crimson/ocean/sunset/dark) all share the
   dark surface and use the :root set unchanged — chart colours never vary by accent, only by surface. */
.theme-light {
    --chart-1: #0072B2;
    --chart-2: #B36F00;
    --chart-3: #009E73;
    --chart-4: #B85C97;
    --chart-5: #D55E00;
    --chart-6: #5A4FCF;
    --chart-7: #1E8E94;
    --chart-8: #9B4FB0;
    --chart-9: #8C5E36;
    --chart-10: #5A6068;
}

/* ==================== Buttons ==================== */

/* Theme-aware Button Styles */
.btn-primary-themed {
    background: var(--button-primary-bg, var(--accent-color));
    color: var(--button-primary-text, var(--accent-text, #ffffff));
    border: none;
    padding: 0.5rem 1rem;
    border-radius: var(--radius-sm);
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
}

.btn-primary-themed:hover {
    background: var(--button-primary-hover, var(--accent-hover));
}

.btn-secondary-themed {
    background: var(--button-secondary-bg, var(--bg-tertiary));
    color: var(--button-secondary-text, var(--text-primary));
    border: none;
    padding: 0.5rem 1rem;
    border-radius: var(--radius-sm);
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
}

.btn-secondary-themed:hover {
    background: var(--button-secondary-hover, var(--bg-hover));
}

/* Command-button family (cmd-btn) — a GENERAL button style (NOT CommandBar-specific). Shared here so
   every host + surface (inline settings, CommandBars, …) uses ONE definition. Danger = ghost/outline
   that fills on hover (destructive convention). NOTE: StickyCommandBar still redefines these
   ::deep-scoped — a pre-existing 'no ::deep' violation + duplication; follow-up is to drop that. */
.cmd-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1.25rem;
    border-radius: 4px;
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
    border: 1px solid transparent;
}

.cmd-btn-primary {
    background: var(--accent-color);
    border-color: var(--accent-color);
    color: #fff;
}

.cmd-btn-primary:hover:not(:disabled) {
    filter: brightness(0.94);
}

.cmd-btn-secondary {
    background: var(--bg-hover);
    border-color: var(--border-color);
    color: var(--text-color, #384145);
}

.cmd-btn-secondary:hover {
    background: var(--bg-active);
}

.cmd-btn-danger {
    background: transparent;
    border-color: var(--error-color, #E52520);
    color: var(--error-color, #E52520);
}

.cmd-btn-danger:hover {
    background: var(--error-color, #E52520);
    color: #fff;
}

/* Inline copy button — immediate on-button feedback (Copy → ✓ Copied!). Shared so any host/page reuses it. */
.copy-inline-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.5rem 0.75rem;
    background: var(--bg-hover);
    border: 1px solid var(--border-color);
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.8125rem;
    color: var(--text-color, #384145);
    white-space: nowrap;
    flex-shrink: 0;
    transition: all 0.15s;
}

.copy-inline-btn:hover {
    background: var(--bg-active);
    border-color: var(--accent-color);
}

.copy-inline-btn.copied {
    color: var(--success-color);
    border-color: var(--success-color);
}

/* Read-only value + copy row — a derived, non-editable value (SCIM base URL, tokens, feed URLs) shown
   as a <code> display, NOT an editable <input>, so it reads as "copy this", not "edit this". */
.copy-value-row {
    display: flex;
    align-items: stretch;
    gap: 0.5rem;
}

.readonly-value {
    flex: 1;
    min-width: 0;
    padding: 0.5rem 0.75rem;
    background: var(--bg-hover);
    border: 1px solid var(--border-color);
    border-radius: 6px;
    font-family: 'Cascadia Code', 'Fira Code', monospace;
    font-size: 0.8125rem;
    color: var(--text-muted);
    line-height: 1.6;
    overflow-x: auto;
    white-space: nowrap;
    user-select: all;
}

/* Summary meta row — a wrapping strip of labeled facts beneath a detail page's identity block
   (version, registered-by/on, token budget, freshness, ...). Second surface to need this exact
   pattern (NuGetMcpServerDetail, then NuGetWorkflowDetail) promoted it here per ux-guidelines §8
   rule 1 rather than letting a third page re-duplicate it in its own *.razor.css. */
.summary-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 2rem;
    font-size: 0.85rem;
}

.summary-meta .meta-item {
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
}

.summary-meta .meta-label {
    color: var(--text-muted);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Usage hint — a titled, copyable configuration/code block ("paste this into your tool").
   Shared home (ux-guidelines §8): NuGet API-token wizard + MCP integration detail both render these. */
.usage-hint {
    margin-top: 1.5rem;
}

.usage-hint h4 {
    font-size: 0.875rem;
    font-weight: 500;
    margin: 0 0 0.5rem 0;
}

/* Code-segment styling is shared by the detail page's .usage-hint snippets AND any bare
   .snippet-block (e.g. the Getting started deploy guide, which has no .usage-hint wrapper) —
   promoting it here keeps every config block styled the same (CSS scope §8). */
.usage-hint pre,
.snippet-block pre {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 1rem;
    overflow-x: auto;
}

.usage-hint code,
.snippet-block code {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    color: var(--text-primary);
}

/* Titled code card (ngrok-style onboarding): filename/command header + Copy, body with an
   optional line-number gutter. Reusable — the CodeSnippet component. The Copy button crosses
   into the shared CommandButton, so this lives in the shared sheet (CSS scope §8), not a scoped
   .razor.css. */
.code-card {
    border: 1px solid var(--border-color);
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg-tertiary);
    margin: 0.5rem 0;
}

.code-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0.5rem 0.4rem 0.75rem;
    background: var(--bg-secondary);
    border-bottom: 1px solid var(--border-color);
}

.code-card-title {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-width: 0;
    color: var(--text-muted);
}

.code-card-title code {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.code-card-title svg {
    flex-shrink: 0;
    color: var(--text-muted);
}

.code-card-copy {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    flex-shrink: 0;
    background: transparent;
    color: var(--text-muted);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xs);
    padding: 0.2rem 0.5rem;
    font-size: 0.75rem;
    cursor: pointer;
    transition: all 0.15s;
}

.code-card-copy:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.code-card-body {
    display: flex;
    overflow-x: auto;
}

.code-gutter {
    display: flex;
    flex-direction: column;
    text-align: right;
    padding: 0.75rem 0.5rem 0.75rem 0.75rem;
    color: var(--text-muted);
    opacity: 0.55;
    user-select: none;
    font-family: var(--font-mono);
    font-size: 0.8rem;
    line-height: 1.5;
    border-right: 1px solid var(--border-color);
    flex-shrink: 0;
}

.code-card-body pre {
    /* Match the gutter's line box, including the pre element's inline strut. */
    font-family: var(--font-mono);
    font-size: 0.8rem;
    line-height: 1.5;
    margin: 0;
    padding: 0.75rem;
    background: transparent;
    border: none;
    flex: 1;
    min-width: 0;
}

.code-card-body pre code {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text-primary);
    white-space: pre;
}

/* Config-file location above a client's code card — a wrapping muted line, not a truncating
   readonly-value chip (a path is context, not a copyable value). */
.client-config-path {
    margin: 0.25rem 0 0.5rem;
    word-break: break-word;
}

/* Corner copy — floats in the top-right of a static snippet (<pre>), mirroring the Prism
   code-toolbar Copy button in markdown previews (§2.11: always visible, no hover-reveal)
   and .lightbox-action-btn. Theme-aware colors where Prism's hardcoded dark-overlay pill
   would vanish on light code backgrounds. Wrap the <pre> in .snippet-block. */
.snippet-block {
    position: relative;
}

.snippet-copy-btn {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    background: var(--bg-hover);
    color: var(--text-muted);
    border: none;
    border-radius: 4px;
    padding: 4px 8px;
    font-size: 0.75rem;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.snippet-copy-btn:hover {
    background: var(--bg-active);
    color: var(--text-primary);
}

.snippet-copy-btn:focus-visible {
    outline: 2px solid var(--accent-color, #81BA2A);
    outline-offset: 1px;
}

.snippet-copy-btn.copied {
    color: var(--success-color);
}

.snippet-copy-btn > svg {
    flex-shrink: 0;
}

/* Standard Button Styles */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.55rem 1rem;
    border-radius: var(--radius-xs);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
    transition: all 0.15s;
    border: none;
}

.btn-primary {
    background: var(--accent-color);
    color: var(--accent-text, #ffffff);
}

.btn-primary:hover {
    background: var(--accent-hover);
}

.btn-primary:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-secondary {
    background: var(--bg-tertiary);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
}

.btn-secondary:hover {
    background: var(--bg-hover);
}

.btn-danger {
    background: var(--error-color, #ef4444);
    color: #ffffff;
}

.btn-danger:hover {
    background: #dc2626;
}

.btn-danger-outline {
    background: transparent;
    color: var(--error-color, #ef4444);
    border: 1px solid var(--error-color, #ef4444);
}

.btn-danger-outline:hover {
    background: var(--error-color, #ef4444);
    color: #ffffff;
}

.btn-ghost {
    background: transparent;
    color: var(--text-muted);
}

.btn-ghost:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

/* Link-style button — renders as an inline <button> styled like an anchor.
   Backfilled per ux-guidelines §2.5. Color matches link-color so it harmonizes
   with native <a> elements (Commerce.Web pairs them under one rule). */
.btn-link {
    background: transparent;
    border: 0;
    color: var(--link-color);
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    text-decoration: none;
}

.btn-link:hover {
    color: var(--link-hover);
}

.btn-sm {
    padding: 0.35rem 0.65rem;
    font-size: 0.8rem;
}

.btn-xs {
    padding: 0.25rem 0.5rem;
    font-size: 0.75rem;
}

.btn-lg {
    padding: 0.75rem 1.5rem;
    font-size: 0.95rem;
}

/* Inline Action Buttons - Validate, Test Connection, etc.
   Used with CommandButton inside forms/settings pages. */
.btn-validate,
.btn-test-connection {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 1rem;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    color: var(--text-secondary);
    font-size: 0.85rem;
    cursor: pointer;
    transition: all 0.15s;
    white-space: nowrap;
}

.btn-validate:hover:not(:disabled),
.btn-test-connection:hover:not(:disabled) {
    background: var(--bg-hover);
    border-color: var(--accent-color);
    color: var(--text-primary);
}

.btn-validate:disabled,
.btn-test-connection:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-validate.cmd-busy,
.btn-test-connection.cmd-busy {
    background: var(--bg-hover);
}

.btn-cancel-test {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.75rem;
    background: transparent;
    border: 1px solid var(--error-color, #ef4444);
    border-radius: 4px;
    color: var(--error-color, #ef4444);
    font-size: 0.85rem;
    cursor: pointer;
    transition: all 0.15s;
}

.btn-cancel-test:hover {
    background: rgba(239, 68, 68, 0.1);
}

.spinner {
    width: 14px;
    height: 14px;
    border: 2px solid var(--border-color);
    border-top-color: var(--accent-color);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

/* ==================== Content Panel & Command Bar ==================== */
/* Content Panel - Scrollable content with sticky command bar at bottom */
.content-panel {
    display: flex;
    flex-direction: column;
    height: calc(100% + 40px); /* Compensate for negative margin (20px top + 20px bottom) */
    min-height: 0; /* Important for flex children to shrink */
    margin: -20px; /* Extend to edges of content-area padding */
}

.content-panel-body {
    flex: 1;
    display: flex; /* Must be a flex CONTAINER, not just a flex item, or the height-chain
                      rule below (.content-panel-body > .tab-content { flex: 1 1 auto })
                      has no effect — .tab-content falls back to auto-height and the tab's
                      .tab-commandbar floats after the content instead of sticking to the
                      panel's visible bottom (found 2026-07-29: the 2026-07-29 fix added the
                      selector but missed this — .doc-edit/.workspace-flex .target-pane, the
                      other height-chain roots, were already flex containers). Safe for the
                      common non-tab case too: a column flex container stacks block-like
                      children top-to-bottom the same as normal flow. */
    flex-direction: column;
    min-height: 0; /* Required for a tab-content child's height-chain fill below to bound
                      correctly instead of growing to fit all content (see §2.4.1 height
                      chain rule below) — without it, a tab's .tab-commandbar floats after
                      the content instead of sticking to the panel's visible bottom. */
    overflow-y: auto;
    padding: 20px 20px 40px; /* Extra bottom clearance: content-panel-commandbar sits in its
                      own flex row below this scroll region (not overlapping), but 20px left
                      the last row of tall lists (e.g. Insights' expandable breakdown grid)
                      flush against that edge with no breathing room once scrolled fully down. */
}

.content-panel-commandbar {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.875rem 1.5rem;
    min-height: 56px; /* Consistent height across all pages */
    /* v3 (stronger): --bg-tertiary still blended on pages with dense dark content.
       Using --bg-hover (#3c3c3c — clearly lifted from --bg-primary #1e1e1e) plus a
       2px crisp border in a brighter neutral and a deeper shadow so the footer reads
       as a raised surface even against busy content. Top highlight line simulates
       a subtle edge lift. */
    background: var(--bg-hover);
    border-top: 2px solid rgba(255, 255, 255, 0.12);
    box-shadow:
        0 -1px 0 var(--border-color),
        0 -8px 20px rgba(0, 0, 0, 0.5);
}

/* Command bar buttons */
.cmd-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1.25rem;
    border-radius: 4px;
    font-size: 0.9rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition: all 0.15s;
    border: none;
}

.cmd-btn-primary {
    background: var(--accent-color);
    color: #fff;
}

.cmd-btn-primary:hover:not(:disabled) {
    background: var(--accent-hover);
}

.cmd-btn-primary:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.cmd-btn-secondary {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
}

.cmd-btn-secondary:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.cmd-btn-danger {
    background: transparent;
    border: 1px solid var(--error-color, #f44336);
    color: var(--error-color, #f44336);
}

.cmd-btn-danger:hover {
    background: var(--error-color, #f44336);
    color: #ffffff;
}

/* CommandButton busy indicator — bouncing bar */
.cmd-busy {
    position: relative;
    overflow: hidden;
    pointer-events: none;
    opacity: 0.7;
    cursor: not-allowed;
}

.cmd-busy::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    height: 4px;
    width: 30%;
    background: var(--accent-color, #81BA2A);
    border-radius: 2px;
    animation: cmd-busy-bar 1.8s ease-in-out infinite;
}

.cmd-btn-primary.cmd-busy::after,
.cmd-btn-danger.cmd-busy::after {
    background: rgba(255, 255, 255, 0.85);
}

@keyframes cmd-busy-bar {
    0%   { left: -30%; }
    50%  { left: 100%; }
    100% { left: -30%; }
}

/* Command bar info text */
.cmd-info {
    color: var(--text-muted);
    font-size: 0.85rem;
}

.cmd-info-right {
    color: var(--text-muted);
    font-size: 0.85rem;
    margin-left: auto;
}

/* Command bar pagination */
.cmd-pagination {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.cmd-btn-icon {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.15s;
}

.cmd-btn-icon:hover:not(:disabled) {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.cmd-btn-icon:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.cmd-page-info {
    font-size: 0.85rem;
    color: var(--text-secondary);
    min-width: 50px;
    text-align: center;
}

.cmd-page-size {
    padding: 0.4rem 0.6rem;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    color: var(--text-secondary);
    font-size: 0.85rem;
    cursor: pointer;
}

.cmd-page-size:focus {
    outline: none;
    border-color: var(--accent-color);
}

/* VAT status badge in command bar */
.vat-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    border-radius: 6px;
    font-size: 0.8125rem;
    font-weight: 500;
    margin-left: auto;
}

.vat-status-badge.valid {
    background: rgba(34, 197, 94, 0.15);
    color: #16a34a;
}

.vat-status-badge.valid svg {
    color: #22c55e;
}

.vat-status-badge.invalid {
    background: rgba(239, 68, 68, 0.15);
    color: #dc2626;
}

.vat-status-badge.invalid svg {
    color: #ef4444;
}

/* Command bar back link (moved here from Form Styles section to keep
   command bar elements together; .back-link variant lives in Misc).
   Both <a> and <button> carry this class (in-page stack-pop Backs are buttons) -
   the button element's UA chrome must not leak: one dimmed arrow-link look everywhere. */
.cmd-back-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--text-muted);
    text-decoration: none;
    font-size: 0.85rem;
    background: none;
    border: none;
    padding: 0;
    font-family: inherit;
    cursor: pointer;
}

.cmd-back-link:hover {
    color: var(--link-color);
}

/* Responsive command bar */
@media (max-width: 768px) {
    .content-panel-commandbar {
        padding: 0.5rem 0.75rem;
        flex-wrap: wrap;
        gap: 0.375rem;
    }

    .cmd-btn span {
        display: none; /* Icon-only buttons on mobile */
    }

    .cmd-btn {
        flex: unset;
        padding: 6px 10px;
        justify-content: center;
    }

    .cmd-back-link span {
        display: none; /* Show only ← arrow */
    }

    .cmd-info,
    .cmd-info-right {
        width: 100%;
        margin-bottom: 0.5rem;
        margin-left: 0;
    }

    .cmd-pagination {
        margin-left: 0;
    }
}

/* ==================== Modals ==================== */
/* Global Modal Styles */
.modal-overlay {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    background: rgba(0, 0, 0, 0.6) !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    z-index: 9999 !important;
}

.modal-overlay > .modal {
    display: block !important;
    position: relative !important;
    width: 100% !important;
    max-width: 480px !important;
    max-height: 90vh !important;
    height: auto !important;
    margin: auto !important;
    background: var(--bg-secondary) !important;
    border: 1px solid var(--border-color) !important;
    border-radius: var(--radius-lg) !important;
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3) !important;
    opacity: 1 !important;
    visibility: visible !important;
    transform: none !important;
    overflow: hidden !important;
    flex-shrink: 0 !important;
}

/* Flex modal with sticky command bar - for wizards and forms with scrollable content */
.modal-overlay > .modal.modal-flex {
    display: flex !important;
    flex-direction: column !important;
}

.modal-flex > .modal-header,
.modal-flex > .modal-footer {
    flex-shrink: 0;
}

.modal-flex > .modal-body {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--border-color);
}

.modal-header h2 {
    margin: 0;
    font-size: 1.1rem;
    color: var(--text-primary);
}

.modal .btn-close {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 1.5rem;
    cursor: pointer;
    border-radius: var(--radius-xs);
    transition: all 0.15s;
}

.modal .btn-close:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.modal-body {
    padding: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
    padding: 1rem 1.25rem;
    border-top: 1px solid var(--border-color);
}

/* Modal size variants */
.modal-overlay > .modal.modal-sm {
    max-width: 360px !important;
}

.modal-overlay > .modal.modal-lg {
    max-width: 600px !important;
}

.modal-overlay > .modal.modal-xl {
    max-width: 800px !important;
}

@media (max-width: 640px) {
    .modal-overlay > .modal.modal-xl {
        max-width: 100% !important;
    }
}

/* Modal with dropdown overflow (DatePicker, AutoComplete, etc.) */
.modal-overlay > .modal.modal-dropdown {
    overflow: visible !important;
}

.modal-dropdown > .modal-body {
    overflow: visible;
}

.modal .form-group {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.modal .form-group label {
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--text-secondary);
}

.modal .form-group input,
.modal .form-group select,
.modal .form-group textarea {
    padding: 0.6rem 0.75rem;
    background: var(--input-bg, var(--bg-primary));
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xs);
    color: var(--text-primary);
    font-size: 0.9rem;
}

.modal .form-group input:focus,
.modal .form-group select:focus,
.modal .form-group textarea:focus {
    outline: none;
    border-color: var(--accent-color);
}

.modal .form-hint {
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-top: 0.25rem;
}

.modal .btn-primary {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.55rem 1rem;
    background: var(--accent-color);
    border: none;
    border-radius: var(--radius-sm);
    color: var(--accent-text, #ffffff);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s;
}

.modal .btn-primary:hover {
    background: var(--accent-hover);
}

.modal .btn-primary:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.modal .btn-secondary {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.55rem 1rem;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
}

.modal .btn-secondary:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

/* Radio and Checkbox options in modals */
.modal .schedule-options,
.modal .license-checkboxes {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.modal .radio-option,
.modal .checkbox-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    color: var(--text-secondary);
    cursor: pointer;
}

.modal .radio-option input,
.modal .checkbox-option input {
    width: auto;
    margin: 0;
}

/* Import info styling */
.modal .import-info {
    padding: 0.75rem;
    background: var(--bg-tertiary);
    border-radius: 6px;
    font-size: 0.85rem;
    color: var(--text-secondary);
}

.modal .import-info code {
    display: block;
    margin-top: 0.5rem;
    padding: 0.5rem;
    background: var(--bg-primary);
    border-radius: 4px;
    font-family: monospace;
    color: var(--info-color);
}

.modal .import-preview {
    font-size: 0.85rem;
    color: var(--info-color);
}

/* Assign developer info */
.modal .assign-developer-info {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem;
    background: var(--bg-tertiary);
    border-radius: 6px;
}

.modal .assign-developer-info .avatar {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--accent-color);
    color: white;
    font-weight: 600;
    font-size: 0.9rem;
    border-radius: 50%;
}

.modal .assign-developer-info strong {
    color: var(--text-primary);
}

.modal .assign-developer-info .dev-email {
    display: block;
    font-size: 0.8rem;
    color: var(--text-muted);
}

/* Current licenses in assign modal */
.modal .current-licenses {
    padding-top: 0.5rem;
}

.modal .current-licenses label {
    display: block;
    margin-bottom: 0.5rem;
}

.modal .license-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.modal .license-tag {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.25rem 0.5rem;
    background: var(--bg-tertiary);
    border-radius: 4px;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.modal .btn-remove-license {
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 0;
    font-size: 1rem;
    line-height: 1;
}

.modal .btn-remove-license:hover {
    color: var(--error-color);
}

/* Export Dialog Pattern
   Reusable export dialog with format selection (PDF/JSON/CSV).
   Used by Data Analysis (Audit Trail), HRM (Leave Types, Working Patterns). */

/* Export Dialog - wider than standard dialog */
.export-dialog {
    max-width: 520px;
    overflow: visible;
}

.export-dialog .dialog-body {
    overflow: visible;
}

.export-dialog .dialog-header {
    flex-direction: row;
    gap: 1rem;
    text-align: left;
    padding: 1.25rem 1.5rem;
}

/* Export icon - indigo accent */
.dialog-icon.export {
    width: 48px;
    height: 48px;
    background: rgba(99, 102, 241, 0.15);
    color: #818cf8;
}

/* Export form layout */
.export-form {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

/* Format selection - toggle buttons */
.format-options {
    display: flex;
    gap: 0.75rem;
}

.format-option {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.875rem 1rem;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: all 0.15s;
}

.format-option:hover {
    background: var(--bg-hover);
}

.format-option.selected {
    background: rgba(99, 102, 241, 0.1);
    border-color: #818cf8;
}

.format-option input[type="radio"] {
    display: none;
}

.format-label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: var(--text-primary);
}

/* Export info box - indigo tinted */
.export-info {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.75rem 1rem;
    background: rgba(99, 102, 241, 0.08);
    border: 1px solid rgba(99, 102, 241, 0.2);
    border-radius: var(--radius-sm);
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.export-info svg {
    flex-shrink: 0;
    color: #818cf8;
    margin-top: 2px;
}

/* Template Picker Dialog — wider variant for tile grids (Workspace "Revise with template",
   Intent Architect-style create-new-solution). Qualified with .dialog-container so the
   selector specificity beats the .dialog-container { max-width: 480px } default that sits
   later in this file; otherwise equal-specificity source order would win. */
.dialog-container.template-picker-dialog {
    max-width: min(960px, calc(100vw - 4rem));
    max-height: calc(100vh - 4rem);
    display: flex;
    flex-direction: column;
}

.dialog-container.template-picker-dialog .dialog-header {
    flex-direction: row;
    gap: 1rem;
    text-align: left;
    padding: 1.25rem 1.5rem;
}

.dialog-container.template-picker-dialog .dialog-body {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
}

.dialog-icon.template {
    width: 48px;
    height: 48px;
    background: rgba(129, 186, 42, 0.15);
    color: var(--accent-color, #81BA2A);
}

/* Preview Lightbox Pattern (ux-guidelines §2.15)
   Distinct from .dialog-* — a lightbox shows CONTENT (markdown, image, diagram) without
   action buttons. A dialog asks for a DECISION (export format, confirm delete, pick a
   template). Both overlay the viewport; the semantic difference is "read" vs "decide".

   Triggers: Expand button on a toolbar, OR double-click on the previewable content.
   Close: overlay click, explicit close button, or Esc (each consumer wires its own ESC
   since Blazor can't listen window-level without JS interop). */
.lightbox-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.9);
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    backdrop-filter: blur(4px);
}

/* Panel fills the frame — the user can SEE the whole viewport belongs to the lightbox,
   rather than a small pill floating over greyed-out chrome. Short content still yields a
   full-height panel; the body region handles scrolling, not the panel itself. */
.lightbox-panel {
    position: relative;
    width: min(1600px, calc(100vw - 2rem));
    height: calc(100vh - 2rem);
    background: var(--bg-primary, #111);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg, 8px);
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
    display: flex;
    flex-direction: column;
    cursor: default;
    overflow: hidden;
}

.lightbox-close {
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    background: transparent;
    border: none;
    color: var(--text-secondary);
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.7;
    transition: opacity 0.15s, color 0.15s;
    z-index: 2;
}

.lightbox-close:hover {
    opacity: 1;
    color: var(--text-primary);
}

.lightbox-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 2.5rem 2.5rem 1.5rem;
    font-size: 1rem;
    line-height: 1.75;
}

.image-lightbox-body {
    display: grid;
    place-items: center;
    padding: 3.5rem 2rem 2rem;
}

.image-lightbox-image {
    display: block;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* Optional CommandBar row pinned to the bottom of the lightbox panel — mirrors the
   ContentPanel convention so action placement is consistent across the app. Secondary
   actions sit left, the spacer pushes the Close action to the right (per §2.5 layout
   rules — the × glyph at top-right is the primary close, so this row holds secondary
   actions only, typically Print / Export). */
/* Action group — floats at the top-right of the lightbox panel, left of the close
   glyph. Styled like the Prism code-toolbar Copy button so the lightbox affordances
   read consistent with the inline code-block actions inside the markdown content. */
.lightbox-actions {
    position: absolute;
    top: 0.5rem;
    right: 3.25rem; /* leave 2.25rem for .lightbox-close (right:0.75rem + 2.25rem width) */
    z-index: 10;
    display: flex;
    gap: 0.4rem;
}

.lightbox-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    background: rgba(255, 255, 255, 0.1);
    color: #9a9a9a;
    border: none;
    border-radius: 4px;
    padding: 4px 8px;
    font-size: 0.75rem;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

    .lightbox-action-btn:hover {
        background: rgba(255, 255, 255, 0.2);
        color: #fff;
    }

    .lightbox-action-btn:focus-visible {
        outline: 2px solid var(--accent-color, #81BA2A);
        outline-offset: 1px;
    }

    .lightbox-action-btn > svg {
        flex-shrink: 0;
    }

/* Print stylesheet for the lightbox path is gone — MarkdownEditor.PrintAsync now opens
   a hidden iframe (window.printRenderedMarkdown in markdownHelpers.js), clones the page's
   stylesheets in, waits for fonts.ready, then prints from there. That carries dynamic
   app styles + web fonts the previous visibility-hack couldn't reproduce, and avoids the
   "only first paragraph prints" bug caused by hidden ancestors retaining layout space. */

/* Dialog overlay - centered with blur backdrop */
.dialog-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    backdrop-filter: blur(4px);
}

/* Dialog container */
.dialog-container {
    background: var(--bg-secondary);
    border-radius: var(--radius-lg);
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
    width: 100%;
    max-width: 480px;
    border: 1px solid var(--border-color);
    overflow: hidden;
}

.dialog-header {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
    padding: 1.5rem 1.5rem 1rem;
    text-align: center;
}

.dialog-icon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.dialog-icon.danger {
    background: rgba(239, 68, 68, 0.15);
    color: #ef4444;
}

.dialog-title {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
}

.dialog-body {
    padding: 0 1.5rem 1.5rem;
}

.dialog-message {
    color: var(--text-secondary);
    font-size: 0.95rem;
    margin: 0 0 1rem;
    text-align: center;
}

.dialog-message strong {
    color: var(--text-primary);
}

.dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
    padding: 1rem 1.5rem;
    background: var(--bg-tertiary);
    border-top: 1px solid var(--border-color);
}

.btn-dialog-cancel {
    padding: 0.6rem 1.25rem;
    background: var(--bg-hover);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
}

.btn-dialog-cancel:hover {
    background: var(--bg-active);
    color: var(--text-primary);
}

.btn-dialog-primary {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1.25rem;
    background: var(--accent-color);
    border: none;
    border-radius: var(--radius-sm);
    color: var(--accent-text, #ffffff);
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
}

.btn-dialog-primary:hover:not(:disabled) {
    opacity: 0.9;
}

.btn-dialog-primary:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* Destructive dialog action (Delete confirm dialogs) - used by ContactDetail/Contacts/Launchpad
   session delete, referenced in markup for a while with no matching rule (rendered as an unstyled
   gray browser-default button until this was added). Mirrors .btn-dialog-primary's shape with the
   house danger color instead of the accent. */
.btn-dialog-danger {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1.25rem;
    background: var(--error-color);
    border: none;
    border-radius: var(--radius-sm);
    color: #ffffff;
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
}

.btn-dialog-danger:hover:not(:disabled) {
    opacity: 0.9;
}

.btn-dialog-danger:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* Import Dialog - Drop zone styles for file import dialogs */
.import-drop-zone {
    min-height: 120px;
    margin-top: 0.5rem;
}

.import-drop-hint,
.import-file-selected {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    color: var(--text-muted);
}

.import-drop-hint svg,
.import-file-selected svg {
    opacity: 0.6;
}

.import-file-selected {
    color: var(--accent-color);
}

.import-file-selected svg {
    color: var(--accent-color);
    opacity: 1;
}

.import-file-name {
    font-weight: 600;
    color: var(--text-primary);
}

.import-file-count {
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* ==================== Forms ==================== */
/* Shared non-editable state, including plain inputs outside .form-group.
   State colours deliberately override page-local input backgrounds. Do not dim
   the whole control: its value must remain readable and readonly text copyable. */
:is(input:not(:where([type="checkbox"], [type="radio"], [type="button"], [type="submit"], [type="reset"], [type="range"], [type="color"], [type="file"], [type="image"], [type="hidden"])), textarea, select):is(:disabled, [readonly]) {
    background: var(--bg-tertiary, var(--input-bg)) !important;
    background: color-mix(in srgb, var(--input-bg) 80%, var(--text-muted) 20%) !important;
    color: var(--text-secondary) !important;
}

:is(input, textarea, select):disabled {
    cursor: not-allowed;
}

.form-group {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.form-group label,
.form-label {
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--text-secondary);
}

.form-group input,
.form-group select,
.form-group textarea,
.form-input {
    padding: 0.6rem 0.75rem;
    background: var(--input-bg);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    color: var(--text-secondary);
    font-size: 0.85rem;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus,
.form-input:focus {
    outline: none;
    border-color: var(--accent-color);
}

.form-group textarea {
    width: 100%;
    font-family: inherit;
    resize: vertical;
    min-height: 80px;
}

.form-hint {
    font-size: 0.75rem;
    color: var(--text-muted);
}

.form-hint.warning {
    color: var(--warning-color, #f59e0b);
    display: flex;
    align-items: center;
    gap: 0.375rem;
}

/* Icon-only square buttons used in moderation tile footers AND bulk commandbars.
   GLOBAL on purpose: the per-tile context renders plain <button> inside the host
   page's CSS-isolation scope, while the bulk context uses <CommandButton> from
   Compossa.UI.Blazor — that's a different scope, so a host-scoped rule wouldn't
   apply to the bulk variant. Living here keeps both contexts pixel-identical. */
.moderation-tile-iconbtn {
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    background: transparent;
    border: 1px solid var(--border-color);
    color: var(--text-muted);
    padding: 0;
    border-radius: var(--radius-sm, 4px);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: color 0.12s, border-color 0.12s, background-color 0.12s;
}

.moderation-tile-iconbtn:hover:not(:disabled) {
    color: var(--text-primary);
    border-color: var(--text-muted);
}

.moderation-tile-iconbtn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.moderation-tile-iconbtn.ai:not(:disabled) {
    color: var(--accent-color);
}

.moderation-tile-iconbtn.ai:hover:not(:disabled) {
    border-color: var(--accent-color);
    background: rgba(129, 186, 42, 0.08);
}

.form-error {
    font-size: 0.75rem;
    color: var(--error-color, #ef4444);
    font-weight: 500;
}

.input-error {
    border-color: var(--error-color, #ef4444) !important;
}

.form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
}

@media (max-width: 768px) {
    .form-grid {
        grid-template-columns: 1fr;
    }
}

/* Verification Section Pattern
   Reusable pattern for verification/validation UI (VAT, FTP, SMTP, etc.) */

.verification-section {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border-color);
}

.verification-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.verification-result {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1rem;
    border-radius: var(--radius-xs);
    font-size: 0.875rem;
}

.verification-result.success {
    background: rgba(129, 186, 42, 0.15);
    color: var(--success-color);
    border: 1px solid rgba(129, 186, 42, 0.3);
}

.verification-result.error {
    background: rgba(229, 37, 32, 0.1);
    color: var(--error-color);
    border: 1px solid rgba(229, 37, 32, 0.2);
}

.verification-info {
    padding: 0.875rem 1rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xs);
}

.verification-info .vat-info-row {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.35rem;
}

.verification-info .vat-info-row:last-child {
    margin-bottom: 0;
}

/* Toggle with inline action button (like VAT input + Validate button) */
.toggle-with-action {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.toggle-with-action .toggle-group {
    padding: 0;
}

/* Verification results (no top border, just results) */
.verification-results {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 1rem;
}

/* Section Enable Toggle Pattern
   Use in section headers to enable/disable entire sections */

.section-enable-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-left: auto;
    cursor: pointer;
    font-size: 0.85rem;
    color: var(--text-secondary);
}

.section-enable-toggle input[type="checkbox"] {
    width: 18px;
    height: 18px;
    accent-color: var(--accent-color);
    cursor: pointer;
}

.section-enable-toggle input[type="checkbox"]:checked + span {
    color: var(--accent-color);
    font-weight: 500;
}

/* Windows 11 Style Toggle Switch - Use for section enable/disable or feature toggles */
.toggle-switch {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 20px;
    flex-shrink: 0;
}

.toggle-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

.toggle-slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: 20px;
    transition: all 0.2s ease;
}

.toggle-slider:before {
    position: absolute;
    content: "";
    height: 14px;
    width: 14px;
    left: 2px;
    top: 2px;
    background-color: var(--text-muted);
    border-radius: 50%;
    transition: all 0.2s ease;
}

.toggle-switch input:checked + .toggle-slider {
    background-color: var(--accent-color);
    border-color: var(--accent-color);
}

.toggle-switch input:checked + .toggle-slider:before {
    transform: translateX(20px);
    background-color: white;
}

.toggle-switch input:focus + .toggle-slider {
    box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
}

.toggle-switch:hover .toggle-slider {
    border-color: var(--text-muted);
}

.toggle-switch input:checked:hover + .toggle-slider {
    background-color: var(--accent-hover);
    border-color: var(--accent-hover);
}

/* Form Validation Pattern
   Use with FluentValidation for consistent error display */

.validation-message {
    color: var(--error-color);
    font-size: 0.8rem;
    margin-top: 0.25rem;
}

.form-field.has-error .form-input,
.form-field.has-error .form-select {
    border-color: var(--error-color);
}

.form-field.has-error .form-label {
    color: var(--error-color);
}

/* Required field indicator (SDD-Presentation version preferred over earlier
   .form-label.required block which used var(--error-color, #ef4444) fallback) */
.form-label.required::after {
    content: " *";
    color: var(--error-color);
}

/* Centralized Form Styles (SDD-Presentation) */
/* Edit/Form page container and header */
.edit-page {
    max-width: 900px;
}

.edit-page-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 1.5rem;
}

.edit-page-header .header-left {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.edit-page-title {
    font-size: 1.75rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
}

/* Auto-save Status Indicator */
.save-status {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.8rem;
    padding: 0.25rem 0.6rem;
    border-radius: var(--radius-xs);
    margin-left: 1rem;
    opacity: 0;
    transition: opacity 0.2s ease-in-out;
}

.save-status.saving,
.save-status.saved,
.save-status.error {
    opacity: 1;
}

.save-status.saving {
    color: var(--text-muted);
}

.save-status.saved {
    color: var(--success-color);
}

.save-status.saved-with-errors {
    color: var(--warning-color);
}

.save-status.has-errors {
    color: var(--warning-color);
}

.save-status.error {
    color: var(--error-color);
}

.save-status .spin {
    animation: spin 1s linear infinite;
}

/* Shared spin keyframes — used by .save-status .spin and .spinner.
   Source had two definitions (lines 2792 and 4179); kept the from/to form. */
@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Organization Section - Form sections with icon headers */
.settings-layout {
    display: flex;
    align-items: flex-start;
    gap: 1.5rem;
    width: 100%;
    min-width: 0;
}

.settings-nav {
    width: 240px;
    flex: 0 0 240px;
    padding-top: .25rem;
}

.settings-content {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
}

@media (max-width: 768px) {
    .settings-layout { flex-direction: column; }
    .settings-nav { width: 100%; flex-basis: auto; display: flex; gap: .25rem; overflow-x: auto; }
    .settings-nav .tree-node { flex: 1 0 auto; margin-bottom: 0; }
}

.org-section {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 1.25rem;
    margin-bottom: 1rem;
}

.org-section-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.25rem;
}

.org-section-header svg {
    color: var(--accent-color);
}

/* Letter-badge fallback for a provider with no verified brand SVG path data — plain HTML/CSS, not an
   SVG <text> element, so it never depends on font metrics or CSS var() resolving inside a bare SVG
   presentation attribute (both proved unreliable at small sizes). */
.org-section-icon-fallback {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--accent-color);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1;
}

.org-section-header h2 {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
}

.org-section-description {
    color: var(--text-muted);
    font-size: 0.85rem;
    margin: 0 0 1rem;
}

/* Form Field elements (form-grid already defined above; second source
   definition at line 2831 was identical, omitted for dedup) */
.form-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.form-field.full-width {
    grid-column: 1 / -1;
}

.form-label {
    color: var(--text-muted);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

/* Info badge for field hints/tooltips */
.info-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--accent-color);
    cursor: help;
    opacity: 0.7;
    transition: opacity 0.15s ease;
    text-transform: none;
}

.info-badge:hover {
    opacity: 1;
}

.info-badge svg {
    width: 14px;
    height: 14px;
}

.form-input,
.form-select {
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-size: 0.9rem;
    padding: 0.6rem 0.75rem;
}

.form-input:focus,
.form-select:focus {
    outline: none;
    border-color: var(--accent-color);
}

.form-input::placeholder {
    color: var(--text-muted);
}

.form-select {
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%23858585' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    padding-right: 2rem;
}

textarea.form-input {
    resize: vertical;
    min-height: 80px;
}

.form-checkbox {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    color: var(--text-secondary);
    font-size: 0.9rem;
}

.form-checkbox input[type="checkbox"] {
    width: 16px;
    height: 16px;
    accent-color: var(--accent-color);
    cursor: pointer;
}

/* Toggle Group for radio options */
.toggle-group {
    display: flex;
    gap: 1rem;
    padding: 0.6rem 0;
}

.toggle-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    color: var(--text-secondary);
    font-size: 0.9rem;
}

.toggle-option input[type="radio"] {
    accent-color: var(--accent-color);
    width: 16px;
    height: 16px;
    cursor: pointer;
}

.toggle-option input[type="radio"]:checked + span {
    color: var(--text-primary);
    font-weight: 500;
}

/* Responsive Form Styles */
@media (max-width: 768px) {
    .edit-page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.75rem;
    }

    .form-grid {
        grid-template-columns: 1fr;
    }

    .form-field.full-width {
        grid-column: 1;
    }
}

/* ==================== Tags & Tag List ==================== */
/* (No tag-* rules in Commerce.Web app.css — TagList component uses scoped CSS) */

/* ==================== Tree View ==================== */
/* Navigation Tree Styles
   Shared styles for tree-based navigation panels (Organizations, etc.) */

/* Tree View Container */
.tree-view {
    padding: 0 4px;
}

.tree-loading {
    padding: 20px;
    text-align: center;
    color: var(--text-muted);
    font-size: 12px;
}

/* Tree Node */
.tree-node {
    margin-bottom: 1px;
}

/* Button semantics share the same tree states as links and other headers.
   Zero specificity keeps hover and selection authoritative. */
:where(button.tree-node-header) {
    width: 100%;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
}

.tree-node-header:focus-visible {
    outline: 2px solid var(--accent-color);
    outline-offset: 2px;
}

.tree-node-header {
    display: flex;
    align-items: center;
    /* Vertical rhythm matches the Browse-tags rows (.section-entry, 0.35rem) so tree leaves
       (company nodes, module nav items) and tag rows read as ONE navigation menu. */
    padding: 0.35rem 8px;
    border-radius: var(--radius-xs);
    cursor: pointer;
    transition: background 0.1s;
    text-decoration: none;
}

.tree-node-header:hover {
    background: var(--bg-hover);
    text-decoration: none;
}

.tree-node.selected > .tree-node-header {
    background: var(--accent-color);
}

/* Tree Toggle Button */
.tree-toggle {
    width: 16px;
    height: 16px;
    border: none;
    background: none;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    transition: transform 0.1s;
}

.tree-toggle:hover {
    color: var(--text-primary);
}

.tree-node.expanded > .tree-node-header .tree-toggle .chevron {
    transform: rotate(90deg);
}

.tree-toggle-placeholder {
    width: 16px;
    height: 16px;
}

/* Tree Icon */
.tree-icon {
    width: 16px;
    height: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 6px;
    color: var(--text-muted);
}

.tree-node.selected > .tree-node-header .tree-icon {
    color: var(--accent-text, #ffffff);
}

/* Tree Label */
.tree-label {
    flex: 1;
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--text-secondary);
}

.tree-node.selected > .tree-node-header .tree-label {
    color: var(--accent-text, #ffffff);
}

/* Tree Children */
.tree-children {
    padding-left: 12px;
    margin-top: 1px;
}

/* Tree Divider - horizontal separator between menu groups */
.tree-divider {
    height: 1px;
    background: var(--border-color);
    margin: 6px 8px;
}

/* Tree Sections (Contacts, Feeds, Billing, etc.) */

.tree-section {
    margin-top: 8px;
    border-top: 1px solid var(--border-color);
    padding-top: 4px;
}

.tree-section-header {
    display: flex;
    align-items: center;
    padding: 6px 8px;
    gap: 6px;
    user-select: none;
    cursor: pointer;
    border-radius: var(--radius-xs);
    transition: background 0.1s;
}

.tree-section-header:hover {
    background: var(--bg-hover);
}

.section-toggle {
    width: 14px;
    height: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    transition: transform 0.15s;
}

.section-toggle.expanded .chevron {
    transform: rotate(90deg);
}

.tree-section-title {
    flex: 1;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
}

.tree-section-count {
    font-size: 10px;
    color: var(--text-muted);
    padding: 1px 6px;
    background: var(--bg-tertiary);
    border-radius: var(--radius-md);
    font-weight: 500;
}

.tree-section-optional {
    font-size: 9px;
    color: var(--text-muted);
    font-style: italic;
    text-transform: none;
}

.empty-section-hint {
    padding: 8px 16px;
    font-size: 11px;
    color: var(--text-muted);
    font-style: italic;
}

/* Contacts List (Navigation Panel) */

.contacts-list {
    padding: 0 8px;
}

.contact-item {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 6px 8px;
    border-radius: var(--radius-xs);
}

.contact-item:hover {
    background: var(--bg-hover);
}

.contact-icon {
    width: 16px;
    height: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    flex-shrink: 0;
    margin-top: 2px;
}

.contact-info {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.contact-name {
    font-size: 13px;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.contact-email {
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ==================== Cards ==================== */
/* Shared Card Component - Used across Categories, Brands, Suppliers, Products */
/* Reusable card layout for member cards, release cards, etc. */

/* Primitive card vocabulary — backfilled per ux-guidelines §2.1.
   Use .card for a generic surface, .card-grid for responsive auto-fill layouts,
   and .card-header / .card-body for internal structure. Richer card variants
   (.entity-card, .feed-card, .facet-grid, etc.) follow below. */
.card-grid {
    list-style: none;
    padding: 0 1rem;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 0.5rem;
}

.card {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--corner-radius);
    padding: 0.5rem;
    cursor: pointer;
    color: var(--text-primary);
    /* Flex column so .card-actions can be pushed to the bottom via margin-top:auto.
       Mirrors the .entity-card pattern used by Commerce.Web /organization tiles. */
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.card:hover {
    background: var(--bg-hover);
}

.card.selected {
    background: var(--bg-selected);
    border-color: var(--accent-color);
}

.card-header {
    font-weight: 600;
    margin-bottom: 0.25rem;
}

.card-body {
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* Tile body layout (Home/backlog cards): grow to fill remaining vertical space so the
   title stays at top and .tile-tags pin to the bottom via margin-top:auto. Without
   this, short titles leave a visual gap between title and tags — tags should anchor
   at the bottom-left of the card body. Scoped to `.card-grid .card` so form/list
   `.card-body` usages elsewhere are unaffected. */
.card-grid .card .card-body {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* Generic settings-form container (ux-guidelines §8 shared scope): a max-width page wrapper
   plus a bordered/padded section card, for inline create/edit/view pages built around a
   .form-grid. Organization.razor/OrganizationNew.razor/OrganizationSettings.razor still carry
   their own page-scoped .org-settings-page/.org-section duplicates (pre-existing, untouched);
   this shared pair is for every OTHER page reaching for the same "settings form card" shape
   (Project's inline create/edit/view - ux-guidelines §9 correction 2026-08-06) so it does not
   grow a third page-scoped copy. */
.settings-page {
    max-width: 900px;
}

.settings-section {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 1.25rem;
    margin-bottom: 1rem;
}

.entity-card {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    cursor: pointer;
    transition: border-color 0.15s;
    position: relative;
}

.entity-card:hover {
    border-color: var(--accent-color);
}

.entity-card.inactive,
.entity-card.rejected {
    opacity: 0.6;
}

/* Card Header - Avatar + Status indicator */
.entity-card .card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

/* Circular Avatar */
.entity-avatar {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--accent-color);
    color: var(--accent-text, #ffffff);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.9rem;
    font-weight: 600;
}

.entity-avatar.warning { background: var(--warning-color, #f59e0b); }
.entity-avatar.success { background: var(--success-color, #81ba2a); }
.entity-avatar.error { background: var(--error-color, #ef4444); }
.entity-avatar.info { background: var(--info-color, #75bcff); }

/* Status Dot */
.status-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
}

.status-dot.active,
.status-dot.published { background: var(--success-color, #81ba2a); }
.status-dot.inactive { background: var(--text-muted); }
.status-dot.warning,
.status-dot.uploading { background: var(--warning-color, #f59e0b); }
.status-dot.error,
.status-dot.rejected { background: var(--error-color, #ef4444); }
.status-dot.info,
.status-dot.uploaded { background: var(--accent-color, #6366f1); }

/* Card Body */
.entity-card .card-body {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.entity-card .card-title {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.entity-card .card-subtitle {
    font-size: 0.8rem;
    color: var(--text-muted);
}

.entity-card .card-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.25rem;
}

/* Small Badge */
.badge-sm {
    padding: 0.15rem 0.4rem;
    font-size: 0.65rem;
    font-weight: 600;
    border-radius: var(--radius-xs);
    text-transform: uppercase;
}

.badge-sm.success { background: rgba(129, 186, 42, 0.15); color: var(--success-color); }
.badge-sm.warning { background: rgba(245, 158, 11, 0.15); color: var(--warning-color); }
.badge-sm.error { background: rgba(239, 68, 68, 0.15); color: var(--error-color); }
.badge-sm.info { background: rgba(99, 102, 241, 0.15); color: var(--accent-color); }
.badge-sm.muted { background: var(--bg-hover); color: var(--text-muted); }

/* Card Footer */
.entity-card .card-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.75rem;
    color: var(--text-muted);
    padding-top: 0.5rem;
    border-top: 1px solid var(--border-color);
}

/* ==================== */
/* Release/Workflow Catalog Card                                                              */
/* Promoted from NuGetFeeds.razor.css 2026-07-20 (ux-guidelines §8: a style needed by a 2nd     */
/* surface belongs here) - the Package Feed catalog (NuGetFeeds) and the MWP Workflow catalog    */
/* (NuGetWorkflows) share this card vocabulary so both catalogs read as one system. Column       */
/* utilities feeding the two pages' LIST views (.col-product / .col-status-label / .col-budget)  */
/* live in the same section for the same reason; each page keeps its own grid-template-columns   */
/* page-scoped since the two lists have a different column count (.releases-list / .workflow-list). */
.releases-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1rem;
}

.release-card {
    background: linear-gradient(135deg, var(--bg-tertiary) 0%, var(--bg-secondary) 100%);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    cursor: pointer;
    transition: all 0.2s ease;
    position: relative;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.release-card:hover {
    border-color: var(--accent-color);
    box-shadow: 0 4px 12px rgba(99, 102, 241, 0.15);
    transform: translateY(-2px);
}

.release-card.rejected,
.release-card.uploadfailed {
    opacity: 0.6;
    background: var(--bg-tertiary);
}

.release-card .card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.release-avatar {
    width: 48px;
    height: 48px;
    border-radius: 12px;
    background: linear-gradient(135deg, var(--accent-color) 0%, #818cf8 100%);
    color: var(--accent-text, #ffffff);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
    transition: transform 0.2s ease;
}

.release-avatar img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: inherit;
}

.release-card:hover .release-avatar {
    transform: scale(1.05);
}

/* Listed/Unlisted axis - shared by NuGet V3 packages and MWP workflow packs. */
.release-avatar.listed {
    background: linear-gradient(135deg, var(--success-color, #81ba2a) 0%, #a3e635 100%);
    box-shadow: 0 2px 8px rgba(129, 186, 42, 0.3);
}

.release-avatar.unlisted {
    background: linear-gradient(135deg, var(--warning-color, #f59e0b) 0%, #fbbf24 100%);
    box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
}

.status-dot.listed {
    background: var(--success-color, #81ba2a);
}

.status-dot.unlisted {
    background: var(--warning-color, #f59e0b);
}

.release-card .card-body {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.release-card .product-name {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

.release-card .version-text {
    font-size: 0.8rem;
    color: var(--text-muted);
    font-family: 'Consolas', 'Monaco', monospace;
}

.release-card .card-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

.status-badge.listed {
    background: rgba(129, 186, 42, 0.15);
    color: var(--success-color, #81ba2a);
}

.status-badge.unlisted {
    background: rgba(245, 158, 11, 0.15);
    color: var(--warning-color, #f59e0b);
}

.release-card .card-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.75rem;
    color: var(--text-muted);
    padding-top: 0.5rem;
    border-top: 1px solid var(--border-color);
}

.release-card .card-actions {
    display: flex;
    gap: 0.5rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--border-color);
}

.action-btn {
    width: 32px;
    height: 32px;
    padding: 0;
    background: var(--input-bg);
    border: none;
    border-radius: var(--radius-xs);
    color: var(--text-muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.15s ease;
}

.action-btn:hover {
    background: var(--accent-color);
    color: var(--accent-text, #ffffff);
}

.action-btn.delete {
    margin-left: auto;
}

.action-btn.delete:hover {
    background: var(--error-color, #ef4444);
    color: white;
}

/* Shared list columns (grid containers stay page-scoped - see comment above). */
.col-product {
    font-weight: 500;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.col-status-label {
    display: flex;
    align-items: center;
}

.col-budget {
    font-size: 0.85rem;
    color: var(--text-muted);
    text-align: center;
}

@media (max-width: 768px) {
    .releases-grid {
        grid-template-columns: 1fr;
    }
}

/* Card Actions - bottom row of buttons.
   Generic .card-actions applies to any .card. Per ux-rule-tile-delete-with-confirmation:
   destructive actions go far-right via a `<div style="flex:1;"></div>` spacer.
   `margin-top: auto` sticks the row to the bottom of the card regardless of title length,
   so every tile in a .card-grid lines up visually. Commerce.Web /organization pattern. */
.card-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-top: 0.5rem;
    margin-top: auto;
    border-top: 1px solid var(--border-color);
}

/* Card needs to be positioned so absolute children (status badge) anchor correctly. */
.card {
    position: relative;
}

/* Card top row — first line inside a tile. Layout:
     [ updated-age ]    ...spacer...    [ status-badge ]
   ULID moved underneath the title (see .card-body .card-ulid below) so the
   title gets the full row width and the top row carries only the two
   primary scan anchors (age + status). */
.card-top-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

.card-top-row .card-updated-at {
    font-size: 0.65rem;
    color: var(--text-muted);
    opacity: 0.75;
    letter-spacing: 0.01em;
}

.card-top-row .status-badge {
    margin-left: auto;       /* pushes badge to the far right of the row */
    text-transform: lowercase;
    font-weight: 500;
}

/* ULID as a supporting metadata line directly under the title — reads as
   "this is the title's identifier" rather than competing for top-row space. */
.card-body .card-ulid {
    display: block;
    margin-top: 0.2rem;
    font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
    font-size: 0.7rem;
    color: var(--text-muted);
    background: transparent;
    padding: 0;
    border: none;
    letter-spacing: 0.02em;
}

/* Copied-feedback state on .card-action-btn — flashes the accent colour briefly
   when CopyUlidAsync completes. Reset by the component after ~1.5 s.  */
.card-action-btn.copied {
    color: var(--accent-color);
    border-color: var(--accent-color);
}

/* Title lives in the body (where content preview used to be) — bigger + prominent.
   Uses flex so the category-icon (if present) sits inline to the left of the title text. */
.card-title {
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.35;
    margin: 0;
    color: var(--text-primary);
    word-break: break-word;
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
}

.card-title-text {
    flex: 1;
    min-width: 0;
    /* Clamp long titles to 2 lines with trailing ellipsis. Uses -webkit-box + line-clamp
       (supported in Chrome/Edge/Safari/Firefox). Prevents a runaway title from pushing
       tags and actions off the card while keeping the first two lines readable. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Category icon — derived from the item's `category:*` tag (bug/task/feature/improvement/tech-debt).
   Replaces the old `BUG: / TASK: / FEATURE:` title prefixes (conveys the same info visually,
   no screen real-estate wasted on repeated text prefixes). Colour-coded per category. */
.category-icon {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    margin-top: 1px; /* optical alignment with the title's cap-height */
    color: var(--text-muted);
}

/* ADR uses the 🏛️ emoji instead of an SVG (per PO call 2026-05-19 — the
   classical-building glyph reads as "pillars of architecture" more directly
   than any monochrome decision-junction SVG). The system emoji is colored,
   so the .category-icon-adr color rule below doesn't apply. Just size + center. */
.adr-emoji-icon {
    font-size: 1rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.category-icon-bug,
.category-icon-regression,
.category-icon-defect       { color: var(--error-color); }
.category-icon-task         { color: var(--info-color); }
.category-icon-feature      { color: var(--accent-color); }
.category-icon-improvement  { color: var(--accent-color); }
.category-icon-tech-debt    { color: var(--warning-color); }
/* Smoke-test = operational / diagnostic artifact. Distinct from category:task
   which is actual developer work. Info-blue tint matches task visually but the
   clipboard-check glyph signals "this is a verification record, not a TODO". */
.category-icon-smoke-test   { color: var(--info-color); }
/* ux-rule = type-based fallback when a rule appears on the Home tile grid without
   a category:* tag. Palette glyph signals "governs how the UI looks" — distinct
   from category:task (work) and category:improvement (iteration). */
.category-icon-ux-rule      { color: var(--accent-color); }
/* llm-rule = formal rule-in-force doc governing how the agent (or UI) behaves.
   Accent colour because rules-in-force carry the brand — same treatment as ux-rule
   (both are authoritative policy docs, not work items). */
.category-icon-llm-rule     { color: var(--accent-color); }
/* agent-run = operational audit record of an LLM dispatch. Info-blue, matching
   smoke-test (both are verification/evidence artifacts, not TODOs). */
.category-icon-agent-run    { color: var(--info-color); }
/* user-story = work-definition artefact (story + acceptance criteria). Info-blue
   ties it to "context to consume / specification to implement against", distinct
   from task (action to do) and feature (new capability idea). */
.category-icon-user-story   { color: var(--info-color); }
/* prompt-template = reusable LLM-driven template with {{placeholders}}. Accent
   colour groups it with authoritative / reusable policy docs (ux-rule, llm-rule). */
.category-icon-prompt-template { color: var(--accent-color); }
/* stride = STRIDE security threat-model assessment. Warning tone signals the
   risk-flagging intent of a threat model (shares the colour with tech-debt, but
   the shield-lock glyph reads unmistakably as security, not a patch). */
.category-icon-stride       { color: var(--warning-color); }

/* Impediment badge — shown on tiles whose properties.status == "impediment"
   (orchestrator flips this after 3 failed LLM attempts). Small inline-flex pill
   with a person-exclamation glyph + attempt count. Mirrors the .status-badge
   structure (icon + gap + text, rgba-tinted warning colour, same vertical rhythm)
   but sits inline in the tile header alongside .status-badge rather than in a
   flex-grown row. */
.impediment-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.2rem 0.45rem;
    border-radius: var(--radius-xs);
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    background: rgba(245, 158, 11, 0.15);
    color: var(--warning-color);
    border: 1px solid rgba(245, 158, 11, 0.3);
    vertical-align: middle;
    cursor: help;
}

.impediment-badge svg {
    flex-shrink: 0;
    display: block;
}

.impediment-badge-count {
    font-variant-numeric: tabular-nums;
}

/* Tile tags — pills shown under the title. Plain chips use the info-blue meta
   look (matches the Commerce release-type-badge.patch styling); `status:*` chips
   take the dynamic .tile-tag-{variant} status tints below. Flex-wrap so a
   long tag list wraps cleanly. Spans don't capture clicks themselves; clicks
   bubble to the card's @onclick. Earlier `pointer-events:none` removed because
   it also disabled hover events, breaking the `title="@tag"` tooltip on
   .tile-tag children (which the comment below on .tile-tag advertises). */
.tile-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    /* margin-top:auto pins tags to the bottom of the flex-column .card-body. padding-top
       keeps breathing room above when the title is tall enough to push them close. */
    margin-top: auto;
    padding-top: 0.5rem;
}

.tile-tag {
    display: inline-block;
    padding: 0.125rem 0.5rem;
    font-size: 0.7rem;
    font-weight: 500;
    line-height: 1.5;
    border-radius: 10px;
    background: rgba(117, 188, 255, 0.15);
    color: var(--info-color);
    /* Transparent border keeps geometry identical between plain chips and the
       status-tinted variants below (which carry a visible border, mirroring the
       .status-badge treatment). */
    border: 1px solid transparent;
    white-space: nowrap;
    /* Defensive ellipsis for over-long tag values. Server-side guard caps new tags at
       40 chars (see BacklogCommandGuards), but legacy docs may still carry wider values;
       clamp to the tile width and elide the rest with the full tag available on hover
       via the element's `title` attribute set in Home.razor. */
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: bottom;
}

/* Status-axis tile tag — a `status:*` chip takes the SAME colour vocabulary as the
   .status-badge pill in the card's top row (one dynamic language for status
   everywhere; consumers map the status value through the same StatusBadgeCss
   mapping that drives the pill). Variant names + tints mirror the .status-badge
   modifiers above verbatim — keep the two families in sync, don't fork palettes.
   Non-status axes (subsystem/scope/feature/category) intentionally keep the plain
   .tile-tag look: the product convention is no per-axis tinting on meta chips
   (see .entry-tag-pill in Requirements.UI tag-grouped-list.css). */
.tile-tag.tile-tag-verified {
    background: rgba(34, 197, 94, 0.15);
    color: #16a34a;
    border-color: rgba(34, 197, 94, 0.3);
}

.tile-tag.tile-tag-warning {
    background: rgba(245, 158, 11, 0.15);
    color: var(--warning-color);
    border-color: rgba(245, 158, 11, 0.3);
}

.tile-tag.tile-tag-info {
    background: rgba(117, 188, 255, 0.15);
    color: var(--info-color);
    border-color: rgba(117, 188, 255, 0.3);
}

.tile-tag.tile-tag-invalid {
    background: rgba(229, 37, 32, 0.15);
    color: var(--error-color);
    border-color: rgba(229, 37, 32, 0.3);
}

.tile-tag.tile-tag-pending {
    background: rgba(107, 114, 128, 0.1);
    color: var(--text-muted);
    border-color: var(--border-color);
}

.entity-card .card-actions {
    display: flex;
    gap: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--border-color);
}

.card-action-btn {
    width: 32px;
    height: 32px;
    padding: 0;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    color: var(--text-muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.15s ease;
}

/* SVGs default to display:inline which carries a baseline / descender line-box;
   inside the flex button that pushes the visible art up. The render dimensions
   matter too: HTML width/height="14" on a viewBox="0 0 16 16" path scales the
   icon to 87.5% and pushes the circle's bottom arc into sub-pixel space where
   the SVG spec's default overflow:hidden clips the anti-alias edge — the
   promote/demote circle is the canary, its lower arc disappears.
   Three rules together: block-level removes baseline; native viewBox size (16)
   removes sub-pixel scaling; overflow:visible lets edge antialiasing paint past
   the viewBox if it still needs to. */
.card-action-btn svg {
    display: block;
    width: 16px;
    height: 16px;
    overflow: visible;
    /* Curves first: tell the rasteriser to prioritise geometric fidelity over
       speed — visible on the promote/demote arc and the bottom of the trash bin.
       (Chromium's `auto` default usually selects this for SVG anyway; declaring
       it explicitly is semantically clearer and free.) */
    shape-rendering: geometricPrecision;
}

.card-action-btn:hover {
    border-color: var(--accent-color);
    color: var(--accent-color);
}

/* Primary variant — was declared on the composer's Send button but never had a rule backing
   it, so Send rendered identically to the neutral mic/template icon buttons beside it (the
   "which one do I click" ambiguity). Matches .cmd-btn-primary's accent-fill treatment so the
   one action that actually submits reads as clearly different from the others. */
.card-action-btn-primary {
    background: var(--accent-color);
    border-color: var(--accent-color);
    color: #fff;
}

.card-action-btn-primary:hover:not(:disabled) {
    filter: brightness(0.94);
    border-color: var(--accent-color);
    color: #fff;
}

.card-action-btn-primary:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.card-action-btn.danger:hover {
    background: var(--error-color, #ef4444);
    border-color: var(--error-color, #ef4444);
    color: white;
}

/* Card Grid Container */
.entity-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1rem;
}

/* Master-Detail Layout
   Reusable two-panel layout: list on left, detail on right.
   Used by Support > Known Issues, Plan Features, Plan Limits, etc. */

.master-detail-layout {
    display: grid;
    grid-template-columns: 280px 1fr;
    gap: 1rem;
    flex: 1 1 auto;
    min-height: 500px;
}

/* List Panel (Master) */
.list-panel {
    display: flex;
    flex-direction: column;
    background: var(--bg-primary);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-color);
    overflow: hidden;
}

.list-panel-items {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
}

.list-panel-empty {
    padding: 2rem;
    text-align: center;
    color: var(--text-muted);
}

.list-panel-item {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.75rem 1rem;
    border: none;
    border-bottom: 1px solid var(--border-color);
    background: transparent;
    text-align: left;
    width: 100%;
    cursor: pointer;
    transition: background 0.15s;
}

.list-panel-item:hover {
    background: var(--bg-hover);
}

.list-panel-item.selected {
    background: var(--bg-selected, rgba(129, 186, 42, 0.1));
    border-left: 3px solid var(--accent-color);
}

.list-panel-item-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.list-panel-item-id {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
}

.list-panel-item-title {
    font-size: 0.85rem;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.list-panel-item-status {
    font-size: 0.7rem;
    padding: 0.15rem 0.5rem;
    border-radius: 10px;
    font-weight: 500;
    text-transform: uppercase;
}

.list-panel-item-status.draft {
    background: var(--warning-color-light, rgba(247, 147, 30, 0.15));
    color: var(--warning-color, #f7931e);
}

.list-panel-item-status.published {
    background: var(--success-color-light, rgba(129, 186, 42, 0.15));
    color: var(--success-color, #81ba2a);
}

/* Detail Panel */
.detail-panel {
    display: flex;
    flex-direction: column;
    background: var(--bg-primary);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-color);
    padding: 1.25rem;
    overflow-y: auto;
    min-height: 0;
}

.detail-panel-header {
    margin-bottom: 1rem;
}

.detail-panel-title-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.detail-panel-title-row h3 {
    margin: 0;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.detail-panel-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    flex: 1;
    color: var(--text-muted);
    text-align: center;
    padding: 2rem;
}

.detail-panel-empty svg {
    opacity: 0.3;
}

@media (max-width: 768px) {
    .master-detail-layout {
        grid-template-columns: 1fr;
    }
}

/* Facet Grid/Card/List Pattern
   Reusable tile/list layout for Categories, Platforms, ProductTypes, Marketing families */

.facet-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1rem;
}

.facet-card {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    transition: border-color 0.15s;
}

.facet-card:hover {
    border-color: var(--accent-color);
}

.facet-card.inactive {
    opacity: 0.6;
}

.facet-card .card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.facet-avatar {
    width: 56px;
    height: 56px;
    border-radius: 12px;
    background: var(--accent-color);
    color: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    transition: transform 0.2s ease;
}

.facet-avatar svg {
    width: 40px;
    height: 40px;
}

/* Composed icon — SVG has its own background, no wrapper styling */
.facet-avatar.composed {
    background: transparent;
    box-shadow: none;
}

.facet-avatar.composed svg {
    width: 56px;
    height: 56px;
}

/* Larger avatar in tile/card view */
.facet-card .facet-avatar {
    width: 64px;
    height: 64px;
}

.facet-card:hover .facet-avatar {
    transform: scale(1.05);
}

.facet-card .facet-avatar svg {
    width: 48px;
    height: 48px;
}

.facet-card .facet-avatar.composed svg {
    width: 64px;
    height: 64px;
}

.facet-card .card-body {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.facet-name {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
}

.facet-slug {
    font-size: 0.8rem;
    color: var(--text-muted);
    font-family: var(--font-mono);
}

.facet-stats {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-top: 0.35rem;
}

.facet-stats .stat {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.75rem;
    color: var(--text-muted);
}

.facet-stats .stat svg {
    color: var(--text-muted);
}

.facet-card .card-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--border-color);
}

.status-indicator {
    width: 10px;
    height: 10px;
    border-radius: 50%;
}

.status-indicator.active {
    background: var(--success-color);
}

.status-indicator.inactive {
    background: var(--text-muted);
}

/* Facet action buttons */
.btn-action {
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--input-bg);
    border: none;
    border-radius: var(--radius-xs);
    color: var(--text-muted);
    cursor: pointer;
    transition: all 0.15s;
}

.btn-action:hover {
    background: var(--accent-color);
    color: #ffffff;
}

.btn-action:disabled {
    opacity: 0.3;
    cursor: default;
}

.btn-action:disabled:hover {
    background: var(--input-bg);
    color: var(--text-muted);
}

.btn-action.delete {
    margin-left: auto;
}

.btn-action.delete:hover {
    background: var(--error-color);
    color: #ffffff;
}

.order-buttons {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

/* Facet List View */
.facet-list {
    display: flex;
    flex-direction: column;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.facet-list .list-header {
    display: grid;
    grid-template-columns: 40px 40px 1fr 120px 70px 60px 70px;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    background: var(--bg-tertiary);
    border-bottom: 1px solid var(--border-color);
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.facet-list .list-row {
    display: grid;
    grid-template-columns: 40px 40px 1fr 120px 70px 60px 70px;
    gap: 0.75rem;
    padding: 0.85rem 1rem;
    align-items: center;
    border-bottom: 1px solid var(--border-color);
    transition: background 0.15s;
}

.facet-list .list-row:last-child {
    border-bottom: none;
}

.facet-list .list-row:hover {
    background: var(--bg-hover);
}

.facet-list .list-row.inactive {
    opacity: 0.6;
}

/* List Row Columns
   NOTE: .col-order/.col-name/.col-slug/.col-actions are intentionally
   defined twice — once here (Facet List) and once below in the Inline
   Manage Table section (Misc Utilities). The two patterns have different
   grid specs; last-rule-wins in source order matches Commerce behavior. */
.col-status {
    display: flex;
    align-items: center;
    justify-content: center;
}

.col-status .status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

.col-status .status-dot.active {
    background: var(--success-color);
}

.col-status .status-dot.inactive {
    background: var(--text-muted);
}

.col-icon {
    display: flex;
    align-items: center;
    justify-content: center;
}

.col-name {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

.col-name .item-name {
    font-weight: 500;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.col-name .item-description {
    font-size: 0.78rem;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    display: block;
}

.col-slug {
    font-size: 0.8rem;
    color: var(--text-muted);
    font-family: var(--font-mono);
}

.col-products {
    font-size: 0.85rem;
    color: var(--text-secondary);
    text-align: center;
}

.col-order {
    display: flex;
    align-items: center;
    justify-content: center;
}

.col-order .order-buttons {
    display: flex;
    gap: 2px;
}

.col-order .btn-action {
    width: 22px;
    height: 22px;
}

.col-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
}

.col-actions .btn-action {
    width: 28px;
    height: 28px;
}

/* Facet responsive */
@media (max-width: 900px) {
    .facet-list .list-header,
    .facet-list .list-row {
        grid-template-columns: 40px 40px 1fr 60px 70px;
    }

    .col-slug,
    .col-order {
        display: none;
    }
}

@media (max-width: 768px) {
    .facet-grid {
        grid-template-columns: 1fr;
    }
}

/* ==================== Status Badges ==================== */
/* Channel & Status Badges */
.channel-badge {
    padding: 0.2rem 0.5rem;
    border-radius: var(--radius-xs);
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
}

.channel-badge.stable {
    background: rgba(129, 186, 42, 0.15);
    color: var(--success-color);
}

.channel-badge.beta {
    background: rgba(99, 102, 241, 0.15);
    color: #6366f1;
}

.channel-badge.alpha {
    background: rgba(245, 158, 11, 0.15);
    color: var(--warning-color);
}

.channel-badge.nightly {
    background: rgba(156, 39, 176, 0.15);
    color: var(--secondary-accent);
}

/* Release Type Badges */
.release-type-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 500;
    white-space: nowrap;
}

.release-type-badge.feature {
    background: rgba(129, 186, 42, 0.15);
    color: var(--accent-color);
}

.release-type-badge.patch {
    background: rgba(117, 188, 255, 0.15);
    color: var(--info-color);
}

.release-type-badge.security {
    background: rgba(247, 147, 30, 0.15);
    color: var(--warning-color);
}

.release-type-badge.hotfix {
    background: rgba(229, 37, 32, 0.15);
    color: var(--error-color);
}

/* Status Badge Pattern
   Use for status indicators in headers, command bars, and inline status displays */

/* Base badge container */
.status-badges {
    display: flex;
    gap: 0.5rem;
    margin-left: auto;
    padding-left: 1rem;
}

/* Base badge style */
.status-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.35rem 0.65rem;
    border-radius: var(--radius-xs);
    font-size: 0.75rem;
    font-weight: 500;
    white-space: nowrap;
    transition: all 0.15s;
}

.status-badge svg {
    flex-shrink: 0;
}

/* Verified state - bright green, successful validation/verification */
.status-badge.verified {
    background: rgba(34, 197, 94, 0.15);
    color: #16a34a;
    border: 1px solid rgba(34, 197, 94, 0.3);
}

.status-badge.verified svg {
    color: #22c55e;
}

/* Configured/Active state - same bright green as verified */
.status-badge.configured {
    background: rgba(34, 197, 94, 0.15);
    color: #16a34a;
    border: 1px solid rgba(34, 197, 94, 0.3);
}

.status-badge.configured svg {
    color: #22c55e;
}

/* Masked-field row for CredentialInput (password/secret input + "Saved" badge). Shared so every
   schema-driven credential field aligns identically across hosts. */
.credential-secret-group {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.credential-secret-group .form-input {
    flex: 1 1 auto;
}

/* ── Inline Badges (small, subtle, for inline status indicators) ── */

.inline-badge-success {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.5rem;
    background: rgba(34, 197, 94, 0.15);
    color: var(--success-color, #22c55e);
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 500;
}

.inline-badge-success svg {
    color: var(--success-color, #22c55e);
}

.inline-badge-warning {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.5rem;
    background: rgba(234, 179, 8, 0.15);
    color: var(--warning-color, #eab308);
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 500;
}

.inline-badge-warning svg {
    color: var(--warning-color, #eab308);
}

.inline-badge-error {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.5rem;
    background: rgba(239, 68, 68, 0.15);
    color: var(--error-color, #ef4444);
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 500;
}

.inline-badge-error svg {
    color: var(--error-color, #ef4444);
}

/* Pending state - gray, awaiting action/verification */
.status-badge.pending {
    background: rgba(107, 114, 128, 0.1);
    color: var(--text-muted);
    border: 1px solid var(--border-color);
}

/* Invalid state - red, failed validation/error */
.status-badge.invalid {
    background: rgba(229, 37, 32, 0.15);
    color: var(--error-color);
    border: 1px solid rgba(229, 37, 32, 0.3);
}

/* Warning state - orange, requires attention */
.status-badge.warning {
    background: rgba(245, 158, 11, 0.15);
    color: var(--warning-color);
    border: 1px solid rgba(245, 158, 11, 0.3);
}

/* Info state - blue, work-in-progress / informational */
.status-badge.info {
    background: rgba(117, 188, 255, 0.15);
    color: var(--info-color);
    border: 1px solid rgba(117, 188, 255, 0.3);
}

/* Neutral / draft state - white/transparent, draft / proposed / unclaimed */
.status-badge.neutral {
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-primary);
    border: 1px solid rgba(255, 255, 255, 0.25);
}

/* SBOM overview badge (SbomStatusBadge.razor, Compossa.NuGet.UI) - icon-only for a dedicated
   list-view column, or icon+"SBOM" label for a tile meta row. Mirrors Organizations.razor's
   .vat-badge / .org-stats .stat.vat-* visual language (verified=green, invalid=red) with a
   third "pending" (muted) state, since an SBOM's best-effort/not-yet-certified middle ground
   isn't a binary signal the way VAT verification is. Shared by NuGetFeeds + Packages (2+
   surfaces -> components.css per ux-guidelines §8). */
.sbom-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
}

.sbom-badge.sbom-badge-labeled {
    font-size: 0.75rem;
    font-weight: 500;
}

.sbom-badge.verified {
    color: #22c55e;
}

/* "pending" = a real SBOM exists but isn't publisher-certified (platform/CI best-effort) - a
   provisional-but-genuine state, not a neutral/empty one, so it takes the theme's tertiary
   accent instead of --text-muted (gold #EFC71C in .theme-ember; purple/violet/gold in the
   other themes - see app.css :root / .theme-* --tertiary-color). */
.sbom-badge.pending {
    color: var(--tertiary-color);
}

.sbom-badge.invalid {
    color: #ef4444;
}

/* Per-filter clear row — shown directly under a filter input (e.g. semantic search)
   ONLY when that filter has a value. Per ux-rule-filter-clear-link: each filter
   owns its own clear affordance, co-located with the input it clears.
   (TagList renders its own Clear-all internally via ShowClearAll="true" —
   see TagList.razor.css for .tag-list-clear.) */
.filter-clear-row {
    display: flex;
    justify-content: flex-start;
    padding: 0 1rem;
    /* Pull up into the filter-bar's 1.5rem bottom margin so the link reads as
       attached to the input above, not as a floating orphan strip. Leaves ~0.25rem
       of breathing room before the next block (.active-filters / pager / tags). */
    margin-top: -1.25rem;
    margin-bottom: 0.5rem;
    font-size: 0.8rem;
}

.view-all-link {
    color: var(--accent-color);
    text-decoration: none;
    font-weight: 500;
    cursor: pointer;
}

.view-all-link:hover { text-decoration: underline; }

/* Status Item Pattern
   Generic success/error status indicators for inline text (status bar, labels, etc.)
   Uses fixed semantic colors - NOT theme-dependent */

.status-item.success {
    color: #16a34a;
}

.status-item.success svg {
    color: #22c55e;
}

.status-item.error {
    color: #dc2626;
}

.status-item.error svg {
    color: #ef4444;
}

/* ==================== Pager ==================== */
/* (Pager component uses scoped CSS — no global pager rules in source) */

/* ==================== Filter Bar / Page Header ==================== */
/* Shared Filter/Search Bar - Uses theme variables */
.filter-bar,
.search-bar {
    background: var(--filter-bar-bg);
    border-radius: var(--radius-md);
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.5rem;
    border: none;
}

.filter-group-subtitle {
    font-family: 'Walkway';
    font-size: 13px;
    color: var(--text-muted);
    max-width: 300px;
    line-height: 1.4;
}

.filter-group,
.search-group {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.filter-label,
.search-label {
    color: rgba(255,255,255,0.8);
    font-size: 0.8rem;
    font-weight: 500;
}

.filter-select {
    min-width: 180px;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xs);
    font-size: 0.85rem;
    background: var(--input-bg);
    color: var(--text-secondary);
    cursor: pointer;
    transition: border-color 0.15s ease;
}

.filter-select:focus {
    outline: none;
    border-color: var(--accent-color);
}

.search-input-wrapper {
    position: relative;
}

.search-input {
    width: 100%;
    padding: 0.6rem 0.9rem;
    padding-right: 2.5rem;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xs);
    font-size: 0.85rem;
    background: var(--input-bg);
    color: var(--text-secondary);
    transition: border-color 0.15s ease;
}

.search-input:focus {
    outline: none;
    border-color: var(--accent-color);
}

.search-input::placeholder {
    color: var(--text-muted);
}

.search-icon {
    position: absolute;
    right: 0.9rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
}

/* Page Header */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 1.5rem;
    padding: 1.25rem 1.5rem;
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-color);
}

.header-content {
    flex: 1;
}

.page-title {
    margin: 0 0 0.35rem;
    font-size: 1.35rem;
    font-weight: 600;
    color: var(--text-primary);
}

.page-description {
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-muted);
    max-width: 600px;
}

.header-actions {
    display: flex;
    gap: 0.75rem;
}

/* ==================== Tabs ==================== */
.tabs-container {
    display: flex;
    gap: 0.25rem;
    margin-bottom: 1.5rem;
    border-bottom: 1px solid var(--border-color);
    padding-bottom: 0;
}

/* Borderless variant - removes bottom border for seamless content integration */
.tabs-container.tabs-borderless {
    border-bottom: none;
}

.tab {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1.25rem;
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--text-muted);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
    margin-bottom: -1px;
}

.tab:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
}

.tab.active {
    color: var(--accent-color);
    border-bottom-color: var(--accent-color);
}

.tab-badge {
    padding: 0.15rem 0.5rem;
    background: var(--bg-tertiary);
    border-radius: var(--radius-full);
    font-size: 0.7rem;
    font-weight: 600;
}

.tab.active .tab-badge {
    background: var(--accent-color);
    color: var(--accent-text, #ffffff);
}

/* Tab body layout convention (ux-guidelines §2.4.1). Opt-in classes a consumer wraps its
   tab content in so a tab acts like a mini-ContentPanel: .tab-body fills the TabPanel's
   .tab-content slot, .tab-scroll is the scrolling region, .tab-commandbar is the pinned
   bottom bar. These belong with the shared TabPanel control so EVERY host styles tabs
   identically — they previously lived only in Compossa.Requirements.Web/app.css and were
   absent in Commerce.Web, which left tab composers (e.g. the redact Conversation tab)
   falling back to display:block with the command bar mid-pane. */
.tab-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
    height: 100%;
}

.tab-scroll {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.tab-commandbar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.6rem 0.75rem;
    border-top: 1px solid var(--border-color);
    background: var(--bg-secondary);
    flex-wrap: wrap;
}

/* Height-chain roots for the pages composing tab commandbars (Workspace, DocumentEdit,
   DocumentNew — Requirements.UI pages). TabPanel renders
   .tab-content, so page-scoped *.razor.css can't reach it (::deep disallowed); the
   page-anchored fill rules therefore live HERE, the shared home — they sat only in
   Requirements.Web/app.css before, so on Commerce.Web .tab-content stayed auto-height,
   .tab-body collapsed, and the tab commandbar floated mid-pane instead of sticking.
   .doc-edit-main-pane: DocumentEdit's TabPanel moved inside the body-row/main-pane split
   when the Requirements rail landed — the old .doc-edit > child selector stopped matching
   there (DOM drifted, rule stayed); DocumentNew still has TabPanel as a direct child.
   .content-panel-body: the plain <ContentPanel><Content><TabPanel>...</TabPanel></Content>
   shape (e.g. NuGetWorkflowDetail's Dependencies tab) had the SAME gap — ContentPanel is
   the most common page wrapper in the app, so this covers every host of a tab commandbar
   that doesn't use one of the three specialized layouts above (found 2026-07-29). */
.workspace-flex .target-pane > .tabs-container,
.doc-edit > .tabs-container,
.doc-edit-main-pane > .tabs-container {
    margin-bottom: 0;
}

.workspace-flex .target-pane > .tab-content,
.doc-edit > .tab-content,
.doc-edit-main-pane > .tab-content,
.content-panel-body > .tab-content {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* A page tab's command bar is the page footer: edge to edge and flush with the bottom, like
   StickyCommandBar (/user) and .content-panel-commandbar. Without this the panel body's padding
   (20px sides, 40px bottom) leaves it floating inset with a gap below (/admin/ai-model, 2026-10-05).
   Only the outermost page tab is affected; nested tabs keep their own layout. */
.content-panel-body:has(> .tab-content > .tab-body > .tab-commandbar) {
    padding-bottom: 0;
}

.content-panel-body > .tab-content > .tab-body > .tab-commandbar {
    margin: 0 -20px;
    padding: 0.875rem 1.5rem;
    min-height: 56px;
}

/* Tabs: horizontal scroll on mobile */
@media (max-width: 768px) {
    .tabs-container {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        flex-wrap: nowrap;
    }

    .tabs-container::-webkit-scrollbar {
        display: none;
    }

    .tab {
        white-space: nowrap;
        flex-shrink: 0;
    }
}

/* ==================== Markdown Editor ==================== */
/* Markdown Editor & Code Block Styling
   Shared styles for MarkdownEditor component with Prism.js syntax highlighting */

/* Markdown Editor Container */
.markdown-editor {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.markdown-editor.read-only {
    border: none;
}

/* Tab Bar */
.markdown-tabs {
    display: flex;
    background: var(--bg-secondary);
    border-bottom: 1px solid var(--border-color);
}

.markdown-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.625rem 1rem;
    background: none;
    border: none;
    color: var(--text-secondary);
    font-size: 0.8125rem;
    font-weight: 500;
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}

.markdown-tab:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
}

.markdown-tab.active {
    color: var(--text-primary);
    background: var(--bg-primary);
    border-bottom: 2px solid var(--accent-color);
}

.markdown-tab svg {
    opacity: 0.7;
}

.markdown-tab.active svg {
    opacity: 1;
}

.markdown-tab-spacer {
    flex: 1;
}

.markdown-export-btn {
    color: var(--text-muted);
    font-size: 0.75rem;
}

.markdown-export-btn:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
}

/* Content Area */
.markdown-content {
    flex: 1;
    min-height: 0;
}

/* Textarea (Edit Mode) */
.markdown-textarea {
    width: 100%;
    min-height: 200px;
    padding: 1rem;
    background: var(--bg-primary);
    border: none;
    color: var(--text-primary);
    font-family: var(--font-mono);
    font-size: 0.875rem;
    line-height: 1.6;
    resize: vertical;
}

.markdown-textarea:focus {
    outline: none;
}

.markdown-textarea::placeholder {
    color: var(--text-muted);
}

.markdown-textarea:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* Help Text */
.markdown-help {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.625rem 1rem;
    background: var(--bg-secondary);
    border-top: 1px solid var(--border-color);
    color: var(--text-muted);
    font-size: 0.75rem;
}

.markdown-help svg {
    flex-shrink: 0;
    opacity: 0.6;
}

/* Preview Mode */
.markdown-preview {
    padding: 1rem;
    background: var(--bg-primary);
    color: var(--text-primary);
    line-height: 1.6;
    overflow-y: auto;
}

.markdown-preview.empty,
.preview-empty {
    color: var(--text-muted);
    font-style: italic;
}

.preview-error {
    color: var(--error-color);
}

/* Tracked Changes — Word reviewing-pane overlays.
   MarkdownEditor.OverlaySegments wraps proposed paragraphs in <ins class="tracked-add">
   and original paragraphs in <del class="tracked-del">. These rules render them as
   green-underline (additions) and red-strikethrough (deletions) on a glossy translucent
   background so the document body underneath remains fully readable.
   Used by /requirement/document/{id}/redact and Workspace.razor's TrackedSegments path. */
.markdown-preview ins.tracked-add,
ins.tracked-add {
    display: block;
    background: linear-gradient(180deg, rgba(129, 186, 42, 0.14), rgba(129, 186, 42, 0.08));
    border-left: 3px solid var(--success-color, #81BA2A);
    padding: 0.5em 0.75em;
    margin: 0 0 1em;
    border-radius: 0 4px 4px 0;
    text-decoration: none;
    color: inherit;
    position: relative;
}

    /* Glyph badges (+/−) removed per UX feedback — the panel-wide red/green
       gradient + colored left border already carries the add/del semantic.
       The badges duplicated the signal and stole horizontal gutter width. */

.markdown-preview del.tracked-del,
del.tracked-del {
    display: block;
    background: linear-gradient(180deg, rgba(229, 37, 32, 0.10), rgba(229, 37, 32, 0.05));
    border-left: 3px solid var(--error-color, #E52520);
    padding: 0.5em 0.75em;
    margin: 0 0 1em;
    border-radius: 0 4px 4px 0;
    text-decoration: line-through;
    color: var(--text-muted);
    opacity: 0.85;
    position: relative;
}

    /* Glyph badge (−) removed — see ins.tracked-add::before above. */

/* Adjacent Add+Del pair (Modify in track-changes) — visually link them with a
   tighter gap and a small "Modified" pill on the second one. */
.markdown-preview del.tracked-del + ins.tracked-add,
del.tracked-del + ins.tracked-add {
    margin-top: -0.5em;
}

/* Top-right transform pill — `improve` / `elevate` / etc. The data attribute is
   set by ApplyOverlay on .tracked-add when MarkdownOverlaySegment.LastTransform
   is non-null; we surface it as a small absolute-positioned chip so the proposed
   text body stays free of bracketed inline tags (compares cleanly against the
   original in the popover). The ::before glyph already pinned at left:-1.6em
   stays untouched. */
ins.tracked-add[data-segment-transform]::after,
.markdown-preview ins.tracked-add[data-segment-transform]::after {
    content: attr(data-segment-transform);
    position: absolute;
    top: 0.35em;
    right: 0.5em;
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0.1em 0.45em;
    background: rgba(129, 186, 42, 0.22);
    color: var(--success-color, #81BA2A);
    border: 1px solid rgba(129, 186, 42, 0.45);
    border-radius: 3px;
    line-height: 1.4;
    pointer-events: none;
}

/* Per-section comment affordance — Notion / Google Docs idiom but bound to a
   thin vertical "comment gutter" on the right edge of the body. The gutter rail
   gives the buttons a visual home (each button straddles the line) and signals
   "this column is for commenting" even when no buttons are visible.
   Discoverability flow:
     idle              opacity 0
     section hover     opacity 0.35  (dimmed — user notices it on proximity)
     button hover      opacity 1, scale 1.15, accent tint
*/
.markdown-preview {
    position: relative;
    /* Generous padding so content doesn't run under the bottom scrollbar and the
       balloon-button gutter has breathing room beside the prose. The right padding
       holds the gutter rail (centered at right: 24px) plus enough room for the
       button to scale on hover without bumping the text. */
    padding: 1.5rem 48px 2.5rem 1.5rem;
}

/* Paragraph anchors emitted by C# PrepareWithAnchors. Pure metadata for the
   per-section affordance JS — invisible in the DOM but kept in the layout flow
   so first-level child iteration still walks them and reads the data-attr. */
.markdown-preview .paragraph-anchor {
    display: block;
    height: 0;
    margin: 0;
    padding: 0;
    overflow: hidden;
}

/* Gutter-rail ::after pseudo-element removed 2026-05-17 — it was added as a
   visual marker for a future right-edge menu, but bled through into print
   (pseudo-elements ignore the splitter's @media print { display: none })
   and read as content on screen rather than scaffolding. Restore from git
   history if the right-edge menu lands and the marker is wanted again. */

.section-comment-host {
    /* Wrapper applied to each block-level child of the preview. */
    position: relative;
}

/* Affordance pills — comment + refresh share the same SHAPE (pill, icon + label,
   top-right slot, brand-green hover) but differ in DEFAULT visibility:
   - Comment lives on every block. Hover-revealed (opacity 0 → 0.6 on host hover)
     to keep the reading surface quiet.
   - Refresh lives on rare generated blocks only. Always-visible (opacity 0.8)
     because the user needs to discover the affordance without hovering. */
.section-comment-add,
.refresh-affordance-btn {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.1));
    border-radius: 999px;
    background: var(--bg-secondary, #2a2a2a);
    color: var(--text-muted, #a0a0a0);
    font-size: 0.72rem;
    line-height: 1;
    cursor: pointer;
    transition: opacity 120ms ease, color 120ms ease, border-color 120ms ease, background 120ms ease;
    z-index: 4; /* above tracked-change halos, below the modal/lightbox layer */
}

.section-comment-add > svg,
.refresh-affordance-btn > svg {
    flex-shrink: 0;
}

/* Comment: hover-revealed. */
.section-comment-add { opacity: 0; }
.section-comment-host:hover > .section-comment-add { opacity: 0.6; }

/* Refresh: always-visible (discoverability — generated blocks are rare). */
.refresh-affordance-btn { opacity: 0.8; }

/* Self hover / focus: full opacity, brand-green tint. */
.section-comment-add:hover,
.section-comment-add:focus-visible,
.refresh-affordance-btn:hover,
.refresh-affordance-btn:focus-visible {
    opacity: 1;
    color: var(--accent-color, #81BA2A);
    border-color: var(--accent-color, #81BA2A);
    background: var(--bg-hover, #333);
    outline: none;
}

/* No stacking rule needed: the JS comment decorator skips generated blocks
   so a single block never carries both pills. Refresh wins the slot when the
   block is generated; Comment wins it on every other block. */

/* Brief highlight pulse used by compossaScrollToSegment (comment-tile click).
   Yellow halo ring fades in then out so the user's eye lands on the segment
   they navigated to. Removed by JS after 1.4s. */
.tracked-pulse {
    animation: tracked-pulse 1.4s ease-out;
}

@keyframes tracked-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(247, 147, 30, 0.55); }
    40%  { box-shadow: 0 0 0 6px rgba(247, 147, 30, 0.35); }
    100% { box-shadow: 0 0 0 12px rgba(247, 147, 30, 0); }
}

/* The legacy click-to-popover affordance (cursor:pointer + brightness hover on
   tracked-add/del + the .tracked-popover floating panel) was retired alongside
   the per-content-type fine-grained diff (backlog 01KRVYS0JKSZXM332JR1HFCX3H).
   The new composite emitters (tracked-table-diff / tracked-prose-diff /
   tracked-code-diff) render the actual change inline; the popover ran on
   data-segment-original / data-segment-proposed attributes that the composite
   emitters did not set, so it surfaced "(empty)" on every click. Per-segment
   Accept / Reject / Comment is still available via the kebab (⋮) menu. */

/* Refresh affordance host — visual rules for the pill itself live with
   .section-comment-add above so the two share one source of truth. */
.refresh-affordance-host {
    position: relative;
}

/* Tracked-change segment selection — applied by JS overlay-menu when the user
   opens the kebab menu on a segment. Brand-green halo so the user sees which
   segment the menu will act on. Cleared when the menu closes. */
.markdown-preview ins.tracked-add.selected-segment,
.markdown-preview del.tracked-del.selected-segment,
ins.tracked-add.selected-segment,
del.tracked-del.selected-segment {
    box-shadow: 0 0 0 4px rgba(129, 186, 42, 0.30);
}

/* Per-section actions kebab (⋯) — Comment + Remove on every plain block. Same round
   button as the tracked-change overlay kebab below, hover-revealed on the section host;
   opens the shared .overlay-menu-flyout. (JS: compossaAttachSectionMenu.) */
.section-menu-btn {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    width: 24px;
    height: 24px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--text-muted, #a0a0a0);
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease, color 100ms ease, background 100ms ease;
}

.section-comment-host:hover > .section-menu-btn { opacity: 0.85; }

.section-menu-btn:hover,
.section-menu-btn:focus-visible {
    opacity: 1;
    color: var(--accent-color, #81BA2A);
    background: var(--bg-hover, #333);
    outline: none;
}

.section-menu-btn > svg { flex-shrink: 0; }

/* Kebab (⋮) trigger button — top-right of each tracked-change segment.
   Single icon, no label, so it doesn't overlap segment prose the way the
   earlier Accept | Reject pills did. Hover-revealed (default 0.5 opacity). */
.overlay-menu-btn {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    width: 24px;
    height: 24px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--text-muted, #a0a0a0);
    cursor: pointer;
    opacity: 0.5;
    transition: opacity 120ms ease, color 120ms ease, background 120ms ease;
    z-index: 4;
}

.tracked-add:hover > .overlay-menu-btn,
.tracked-del:hover > .overlay-menu-btn {
    opacity: 0.85;
}

.overlay-menu-btn:hover,
.overlay-menu-btn:focus-visible {
    opacity: 1;
    color: var(--accent-color, #81BA2A);
    background: var(--bg-hover, #333);
    outline: none;
}

/* Flyout — body-mounted, absolutely positioned by JS. Vertical list of
   action items. YouTube / Gmail idiom. */
.overlay-menu-flyout {
    position: fixed;
    min-width: 180px;
    background: var(--bg-secondary, #2a2a2a);
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.1));
    border-radius: 8px;
    padding: 0.35rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
    z-index: 9000;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.overlay-menu-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.45rem 0.7rem;
    border: 0;
    border-radius: 4px;
    background: transparent;
    color: var(--text-primary, #e0e0e0);
    font-size: 0.82rem;
    line-height: 1;
    cursor: pointer;
    text-align: left;
    transition: background 100ms ease, color 100ms ease;
}

.overlay-menu-item > svg { flex-shrink: 0; }

.overlay-menu-item:hover,
.overlay-menu-item:focus-visible {
    background: var(--bg-hover, #333);
    outline: none;
}

.overlay-menu-item-accept:hover { color: var(--success-color, #81BA2A); }
.overlay-menu-item-reject:hover { color: var(--error-color, #E52520); }
.overlay-menu-item-comment:hover { color: var(--info-color, #75BCFF); }

/* TableGridEditor — grid-style editor for markdown pipe-tables, used inside the
   propose-edit dialog when the active paragraph is a table. Visual goal: the grid
   reads as a table at a glance (rendered cell borders) with input fields that
   stay visually subtle until focused. Toolbar lives above the grid; row controls
   sit in a narrow gutter on the left so the column count isn't pushed off-screen. */
.table-grid-editor {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    height: 100%;
    min-height: 0;
}

.table-grid-toolbar {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.25rem 0;
}

.table-grid-toolbar-spacer {
    flex: 1 1 auto;
}

.table-grid-scroll {
    flex: 1 1 auto;
    overflow: auto;
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.08));
    border-radius: 4px;
    background: var(--bg-secondary, #1f1f1f);
}

.table-grid {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
}

.table-grid th,
.table-grid td {
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.08));
    padding: 0;
    vertical-align: top;
    background: var(--bg-secondary, #1f1f1f);
}

.table-grid th {
    background: var(--bg-tertiary, #262626);
}

.table-grid .table-grid-rownum {
    width: 44px;
    background: var(--bg-tertiary, #262626);
    text-align: center;
    padding: 0.2rem 0;
    border-right: 1px solid var(--border-color, rgba(255, 255, 255, 0.12));
}

.table-grid-rownum-controls {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
}

.table-grid-row-btn {
    width: 18px;
    height: 18px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--text-muted, #a0a0a0);
    cursor: pointer;
    font-size: 0.72rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: color 100ms ease, background 100ms ease;
}

.table-grid-row-btn:hover {
    color: var(--accent-color, #81BA2A);
    background: var(--bg-hover, #333);
}

.table-grid-row-btn.table-grid-row-btn-danger:hover {
    color: var(--error-color, #E52520);
}

.table-grid-cell-input {
    width: 100%;
    border: 0;
    background: transparent;
    padding: 0.4rem 0.55rem;
    color: var(--text-primary, #e0e0e0);
    font-family: inherit;
    font-size: inherit;
    outline: none;
}

.table-grid-cell-input:focus {
    background: var(--bg-hover, #2c2c2c);
    box-shadow: inset 0 0 0 2px var(--accent-color, #81BA2A);
}

.table-grid-cell-input.table-grid-header {
    font-weight: 600;
}

.table-grid-cell-input[readonly] {
    color: var(--text-muted, #a0a0a0);
    cursor: default;
}

.table-grid-empty {
    padding: 0.75rem;
    font-style: italic;
    text-align: center;
}

/* Compact mode for redaction timeline previews — used in tile thumbnails. */
.tracked-compact ins.tracked-add,
.tracked-compact del.tracked-del {
    padding: 0.2em 0.5em;
    margin: 0 0 0.4em;
    font-size: 0.85em;
}

    .tracked-compact ins.tracked-add::before,
    .tracked-compact del.tracked-del::before {
        display: none; /* hide the +/− glyph in compact mode to save horizontal space */
    }

/* ─── Fine-grained tracked-change diffs ──────────────────────────────────────
   Backlog 01KRVYS0JKSZXM332JR1HFCX3H — Word-style per-content-type diff.
   The renderer (MarkdownEditor.PrepareWithAnchors) detects table / fenced code /
   prose paragraphs and emits a composite element instead of the wholesale
   <del>+<ins> pair that strikes through the entire chunk. Each kind gets its
   own container class so the inline tracked-add / tracked-del styling above
   can be scoped (the table needs cell-level styling, code needs gutter
   colouring, prose just needs un-blocking the default block-display). */

/* TABLE row-level diff — composite <table> with per-<tr> tracked-del/add. */
.markdown-preview .tracked-table-diff,
.tracked-table-diff {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 1em;
    font-size: 0.9em;
}

.markdown-preview .tracked-table-diff thead th,
.tracked-table-diff thead th {
    text-align: left;
    padding: 0.4em 0.6em;
    border-bottom: 2px solid var(--border-color, #535D62);
    font-weight: 600;
}

.markdown-preview .tracked-table-diff tbody td,
.tracked-table-diff tbody td {
    padding: 0.35em 0.6em;
    border-bottom: 1px solid rgba(83, 93, 98, 0.25);
    vertical-align: top;
}

/* Removed row — translucent danger tint + line-through cells. Border-left
   echo of the existing del.tracked-del block convention so the cue is
   recognisable to anyone already trained on the wholesale style. */
.markdown-preview .tracked-table-diff tr.tracked-del td,
.tracked-table-diff tr.tracked-del td {
    background: rgba(229, 37, 32, 0.10);
    text-decoration: line-through;
    text-decoration-color: rgba(229, 37, 32, 0.55);
    color: rgba(229, 37, 32, 0.85);
}

    .markdown-preview .tracked-table-diff tr.tracked-del td:first-child,
    .tracked-table-diff tr.tracked-del td:first-child {
        border-left: 3px solid var(--error-color, #E52520);
    }

/* Added row — translucent success tint, no line-through. */
.markdown-preview .tracked-table-diff tr.tracked-add td,
.tracked-table-diff tr.tracked-add td {
    background: rgba(129, 186, 42, 0.14);
    color: var(--success-text-color, #2c5e0e);
    font-weight: 500;
}

    .markdown-preview .tracked-table-diff tr.tracked-add td:first-child,
    .tracked-table-diff tr.tracked-add td:first-child {
        border-left: 3px solid var(--success-color, #81BA2A);
    }

/* PROSE word-level diff — inline ins/del inside a single <p>. The default
   block-display tracked-add / tracked-del rules above turn each into a
   margin-bottom block, which is the wrong shape inside flowing prose. Inside
   a tracked-prose-diff container we shed that and revert to inline tints. */
.markdown-preview .tracked-prose-diff,
.tracked-prose-diff {
    margin: 0 0 1em;
    line-height: 1.6;
}

.markdown-preview .tracked-prose-diff ins.tracked-add,
.tracked-prose-diff ins.tracked-add {
    display: inline;
    background: rgba(129, 186, 42, 0.22);
    border-left: none;
    padding: 0 0.15em;
    margin: 0;
    text-decoration: none;
    color: inherit;
}

.markdown-preview .tracked-prose-diff del.tracked-del,
.tracked-prose-diff del.tracked-del {
    display: inline;
    background: rgba(229, 37, 32, 0.16);
    border-left: none;
    padding: 0 0.15em;
    margin: 0;
    text-decoration: line-through;
    text-decoration-color: rgba(229, 37, 32, 0.65);
    color: inherit;
    opacity: 0.85;
}

/* Suppress the ::before glyph (+/−) inside prose diffs — it visually intrudes
   between words. The block-style overlays elsewhere still get it. */
.markdown-preview .tracked-prose-diff ins.tracked-add::before,
.markdown-preview .tracked-prose-diff del.tracked-del::before,
.tracked-prose-diff ins.tracked-add::before,
.tracked-prose-diff del.tracked-del::before {
    display: none;
}

/* CODE line-level diff — git-style unified diff inside <pre><code>. Each
   line is wrapped in a <span> when changed, plain text when unchanged. We
   already prepend a "+ " / "- " gutter in the renderer; the span colours
   the whole line and the gutter alike. */
.markdown-preview .tracked-code-diff,
.tracked-code-diff {
    margin: 0 0 1em;
}

.markdown-preview .tracked-code-diff pre,
.tracked-code-diff pre {
    background: var(--bg-code, #1e2226);
    color: var(--text-code, #e6e6e6);
    padding: 0.75em 1em;
    border-radius: 4px;
    overflow-x: auto;
    margin: 0;
    line-height: 1.45;
}

.markdown-preview .tracked-code-diff pre code,
.tracked-code-diff pre code {
    font-family: ui-monospace, 'Cascadia Mono', 'Consolas', monospace;
    font-size: 0.85em;
    white-space: pre;
}

.markdown-preview .tracked-code-diff pre code span.tracked-add,
.tracked-code-diff pre code span.tracked-add {
    display: inline-block;
    width: 100%;
    background: rgba(129, 186, 42, 0.22);
    color: var(--success-text-code, #b9e07a);
    border-left: none;
    padding: 0;
    margin: 0;
    text-decoration: none;
}

.markdown-preview .tracked-code-diff pre code span.tracked-del,
.tracked-code-diff pre code span.tracked-del {
    display: inline-block;
    width: 100%;
    background: rgba(229, 37, 32, 0.20);
    color: var(--error-text-code, #ff8a86);
    border-left: none;
    padding: 0;
    margin: 0;
    text-decoration: none;
    opacity: 0.95;
}

.markdown-preview .tracked-code-diff pre code span.tracked-add::before,
.markdown-preview .tracked-code-diff pre code span.tracked-del::before,
.tracked-code-diff pre code span.tracked-add::before,
.tracked-code-diff pre code span.tracked-del::before {
    display: none;
}

/* Markdown Preview Typography */
.markdown-preview h1,
.markdown-preview h2,
.markdown-preview h3,
.markdown-preview h4,
.markdown-preview h5,
.markdown-preview h6 {
    margin: 1.25em 0 0.5em;
    font-weight: 600;
    line-height: 1.3;
    color: var(--text-primary);
}

.markdown-preview h1:first-child,
.markdown-preview h2:first-child,
.markdown-preview h3:first-child {
    margin-top: 0;
}

.markdown-preview h1 { font-size: 1.75rem; }
.markdown-preview h2 { font-size: 1.5rem; }
.markdown-preview h3 { font-size: 1.25rem; }
.markdown-preview h4 { font-size: 1.1rem; }

.markdown-preview p {
    margin: 0 0 1em;
}

.markdown-preview p:last-child {
    margin-bottom: 0;
}

.markdown-preview a {
    color: var(--link-color);
    text-decoration: none;
}

.markdown-preview a:hover {
    text-decoration: underline;
}

.markdown-preview strong {
    font-weight: 600;
    color: var(--text-primary);
}

.markdown-preview em {
    font-style: italic;
}

/* Lists */
.markdown-preview ul,
.markdown-preview ol {
    margin: 0 0 1em;
    padding-left: 1.5em;
}

.markdown-preview li {
    margin-bottom: 0.25em;
}

.markdown-preview li > ul,
.markdown-preview li > ol {
    margin-top: 0.25em;
    margin-bottom: 0;
}

/* Blockquotes */
.markdown-preview blockquote {
    margin: 0 0 1em;
    padding: 0.5em 1em;
    border-left: 3px solid var(--accent-color);
    background: var(--bg-secondary);
    color: var(--text-secondary);
}

.markdown-preview blockquote p:last-child {
    margin-bottom: 0;
}

/* Horizontal Rule */
.markdown-preview hr {
    border: none;
    border-top: 1px solid var(--border-color);
    margin: 1.5em 0;
}

/* Tables */
.markdown-preview table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 1em;
}

.markdown-preview th,
.markdown-preview td {
    padding: 0.5em 0.75em;
    border: 1px solid var(--border-color);
    text-align: left;
}

.markdown-preview th {
    background: var(--bg-secondary);
    font-weight: 600;
}

/* Two-column key/value tables (Metadata / Value, Parameter / Description, etc.)
   look cramped when the key column eats ~40% of the table width — the value
   column ends up wrapping long paths and prose mid-word while the key column
   sits half-empty. The width: 1% + nowrap trick shrinks the first column to
   its content's natural width and lets the remaining width flow to the value
   columns. Standard CSS idiom; works for wider tables too (first column is
   usually the short "name" axis). */
.markdown-preview table td:first-child,
.markdown-preview table th:first-child {
    width: 1%;
    white-space: nowrap;
}

.markdown-preview tr:nth-child(even) {
    background: var(--bg-secondary);
}

/* Inline Code - minimal styling, Prism handles language-specific */
.markdown-preview code:not([class*="language-"]) {
    padding: 0.2em 0.4em;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 3px;
    font-family: var(--font-mono);
    font-size: 0.875em;
}

/* Code Blocks - minimal layout, Prism theme handles colors */
.markdown-preview pre {
    margin: 0 0 1em;
    border-radius: 6px;
    overflow-x: auto;
}

.markdown-preview pre code {
    display: block;
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    line-height: 1.6;
}

/* Remove text-shadow from Prism (can look blurry on some screens) */
.markdown-preview code[class*="language-"],
.markdown-preview pre[class*="language-"] {
    text-shadow: none;
}

/* Prism toolbar (copy button) - always visible */
.markdown-preview div.code-toolbar > .toolbar {
    opacity: 1;
}

.markdown-preview div.code-toolbar > .toolbar > .toolbar-item > button,
.markdown-preview div.code-toolbar > .toolbar > .toolbar-item > span {
    background: rgba(255, 255, 255, 0.1);
    color: #9a9a9a;
    border-radius: 4px;
    padding: 4px 8px;
    font-size: 0.75rem;
    border: none;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.markdown-preview div.code-toolbar > .toolbar > .toolbar-item > button:hover {
    background: rgba(255, 255, 255, 0.2);
    color: #fff;
}

.markdown-preview div.code-toolbar > .toolbar > .toolbar-item > button:focus {
    outline: none;
}

/* Mermaid.js diagrams */
.markdown-preview .mermaid-diagram,
.markdown-preview .kroki-diagram {
    margin: 1em 0;
    padding: 1em;
    background: rgba(255, 255, 255, 0.03);
    border-radius: 6px;
    /* Scroll only as a last resort — the svg below scales to fit, so the bar
       normally never appears. No fixed height cap; the svg's max-height governs. */
    overflow: auto;
    text-align: center;
}

.markdown-preview .mermaid-diagram svg,
.markdown-preview .kroki-diagram svg {
    display: inline-block;
    margin: 0 auto;
    /* Scale the diagram DOWN to fit the tab on BOTH axes, aspect preserved:
       max-width keeps it within the pane (no horizontal scroll for normal diagrams),
       max-height caps a very tall chart to ~viewport so it fits the tab rather than
       forcing a long in-content scroll. Only a genuinely huge diagram still scrolls
       (container overflow:auto). Full-size viewing is the lightbox's job (below). */
    max-width: 100%;
    max-height: 65vh;
    width: auto;
    height: auto;
}

.markdown-preview .kroki-error {
    color: var(--error-color);
    background: var(--info-bg);
    border: 1px solid var(--error-color);
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.75rem;
    margin: 0.5rem 0;
    font-family: var(--font-mono);
    font-size: 0.85rem;
}

.markdown-preview .mermaid-error {
    color: var(--error-color);
    font-size: 0.875rem;
    padding: 0.5rem;
    margin-bottom: 0.5rem;
    text-align: left;
}

/* Inside the full-screen lightbox, diagrams render at full size — the lightbox body
   (.lightbox-body, overflow-y:auto) owns the scroll. The 600px cap used for inline
   in-page previews would otherwise clip a tall C4 / sequence / flowchart mid-render
   (e.g. the bottom container of a C4 diagram falling below the 600px line). Applies to
   both mermaid and Kroki output. Higher specificity than the .markdown-preview rule,
   so it wins inside the lightbox without a host-side override. */
.lightbox-body .mermaid-diagram,
.lightbox-body .kroki-diagram {
    max-height: none;
    overflow: visible;
}

/* In the lightbox the diagram is shown at NATURAL size (the lightbox-body scrolls) —
   override the inline fit-to-tab svg rule above, which would otherwise shrink it to
   65vh. Same specificity as the inline rule but declared later + the lightbox element
   carries both .markdown-preview and .lightbox-body, so this wins for full-screen. */
.lightbox-body .mermaid-diagram svg,
.lightbox-body .kroki-diagram svg {
    max-width: none;
    max-height: none;
    width: auto;
    height: auto;
    min-width: min-content;
}

/* Images */
.markdown-preview img {
    max-width: 100%;
    height: auto;
    border-radius: var(--radius-xs);
}

/* Linked artifact thumbnails stay inline so a sequence wraps naturally. */
.markdown-preview a[href^="/artifact/"]:has(> img[src*="/thumbnail"]) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    vertical-align: middle;
    padding: 4px;
    margin: 4px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xs);
    background: var(--bg-secondary);
}

.markdown-preview a[href^="/artifact/"]:has(> img[src*="/thumbnail"]):focus-visible {
    outline: 2px solid var(--accent-color);
    outline-offset: 2px;
}

/* ==================== Misc Utilities ==================== */
/* Text helpers — backfilled per ux-guidelines §2.9. */
.text-muted { color: var(--text-muted); }
.text-success { color: var(--accent-color); }
.text-danger { color: var(--error-color); }

/* Grid Splitter
   WPF-style resizable splitter for split-view layouts */
.grid-splitter {
    width: 6px;
    background: var(--border-color);
    cursor: col-resize;
    flex-shrink: 0;
    position: relative;
    transition: background 0.15s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.grid-splitter:hover {
    background: var(--accent-color);
}

.grid-splitter.resizing {
    background: var(--accent-color);
}

.splitter-grip {
    width: 4px;
    height: 40px;
    background: linear-gradient(
        180deg,
        transparent 0%,
        var(--text-muted) 20%,
        var(--text-muted) 80%,
        transparent 100%
    );
    opacity: 0.3;
    border-radius: 2px;
    transition: opacity 0.15s ease, height 0.15s ease;
}

.grid-splitter:hover .splitter-grip {
    opacity: 0.6;
    height: 50px;
}

.grid-splitter.resizing .splitter-grip {
    opacity: 0.8;
}

/* Print — the splitter is page chrome, not document content. Hide on every
   page that uses GridSplitter (Redact / DocumentEdit / Whiteboard / Hrm
   patterns / NuGet explorer / etc.). Without this, the 6px vertical bar
   leaks into every printed page as a stark grey/black line. */
@media print {
    .grid-splitter {
        display: none !important;
    }
}

/* Stats Panel */
.stats-panel {
    background: var(--filter-bar-bg);
    border-radius: var(--radius-md);
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.5rem;
}

.stats-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1rem;
}

@media (max-width: 1200px) {
    .stats-row {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 600px) {
    .stats-row {
        grid-template-columns: 1fr;
    }
}

.stat-card {
    position: relative;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem 1.25rem;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: border-color 0.15s;
}

.stat-card:hover {
    border-color: var(--accent-color);
}

.stat-card.active {
    border-color: var(--accent-color);
    background: var(--bg-tertiary);
}

.stat-icon {
    width: 48px;
    height: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    flex-shrink: 0;
}

.stat-icon.primary { background: rgba(129, 186, 42, 0.15); color: var(--success-color); }
.stat-icon.info { background: rgba(117, 188, 255, 0.15); color: var(--info-color); }
.stat-icon.warning { background: rgba(247, 147, 30, 0.15); color: var(--warning-color); }
.stat-icon.secondary { background: rgba(156, 39, 176, 0.15); color: var(--secondary-accent); }

.stat-content {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.stat-value {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.2;
}

.stat-label {
    font-size: 0.75rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

/* Empty State */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 3rem 2rem;
    text-align: center;
    color: var(--text-muted);
}

.empty-state svg {
    opacity: 0.3;
    margin-bottom: 1rem;
}

.empty-state h3 {
    margin: 0 0 0.5rem;
    color: var(--text-secondary);
    font-size: 1rem;
}

.empty-state p {
    margin: 0;
    font-size: 0.85rem;
}

/* Empty State Pattern (extended)
   Use for pages/sections with no data to display */
.empty-state,
.loading-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 3rem 2rem;
    text-align: center;
    color: var(--text-muted);
}

.empty-state > svg,
.loading-state > svg {
    opacity: 0.4;
    margin-bottom: 1rem;
}

/* Fallback simple spinner for legacy use */
.loading-state .spinner {
    width: 24px;
    height: 24px;
    border: 3px solid var(--border-color);
    border-top-color: var(--accent-color);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
    margin-bottom: 1rem;
}

.empty-state h3 {
    margin: 0 0 0.5rem;
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text-secondary);
}

.empty-state p {
    margin: 0;
    font-size: 0.875rem;
    max-width: 400px;
}

.empty-state .btn {
    margin-top: 1rem;
}

/* Ensure button icons inside empty state are visible and centered */
.empty-state .btn svg {
    opacity: 1;
    margin-bottom: 0;
}

/* Hidden file input - visually hidden but still functional for accessibility */
/* Parent containers that hold hidden-input need position:relative */
.add-files-btn,
.drop-zone,
.drop-zone-large,
.file-input-wrapper {
    position: relative;
}

.hidden-input {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

/* Page back-link variant (Commerce edit-page header).
   Companion .cmd-back-link lives in Content Panel & Command Bar section. */
.back-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--text-muted);
    text-decoration: none;
    font-size: 0.85rem;
}

.back-link:hover {
    color: var(--link-color);
}

/* Inline Manage Table
   Compact inline-editable table for managing items (categories, types, platforms).
   NOTE: .col-order/.col-name/.col-slug/.col-actions defined here intentionally
   shadow the Facet List versions above (different grid specs). Last-rule-wins
   behavior matches Commerce. */

.category-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.category-table thead {
    border-bottom: 1px solid var(--border-color);
}

.category-table th {
    padding: 8px 12px;
    text-align: left;
    font-weight: 600;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.category-table td {
    padding: 6px 12px;
    color: var(--text-primary);
    border-bottom: 1px solid var(--border-color-subtle, rgba(128, 128, 128, 0.1));
}

.category-table tbody tr:hover {
    background: var(--bg-hover);
}

.col-order {
    width: 48px;
}

.col-name {
    min-width: 160px;
}

.col-slug {
    min-width: 120px;
}

.col-count {
    width: 80px;
    text-align: center;
}

.col-actions {
    width: 80px;
    text-align: right;
    display: flex;
    gap: 4px;
    justify-content: flex-end;
}

.empty-row {
    text-align: center;
    color: var(--text-muted);
    padding: 1.5rem 12px !important;
    font-style: italic;
}

.order-buttons {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.order-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 14px;
    border: none;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    border-radius: 2px;
    padding: 0;
}

.order-btn:hover:not(:disabled) {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.order-btn:disabled {
    opacity: 0.25;
    cursor: default;
}

.action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border: none;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    border-radius: var(--radius-sm);
    padding: 0;
}

.action-btn:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.action-btn.action-delete:hover {
    color: var(--error-color);
}

.action-btn.action-save {
    color: var(--accent-color);
}

.action-btn.action-cancel {
    color: var(--text-muted);
}

.category-edit-input {
    padding: 3px 8px !important;
    font-size: 0.8rem !important;
    height: 28px;
}

.category-add-row {
    display: flex;
    gap: 8px;
    padding: 10px 0;
    align-items: center;
    margin-top: 0.75rem;
}

.category-add-input {
    flex: 1;
    padding: 5px 10px !important;
    font-size: 0.8rem !important;
    height: 30px;
}

.category-add-btn {
    padding: 5px 16px !important;
    font-size: 0.8rem !important;
    height: 30px;
    white-space: nowrap;
}

/* ==================== BusyIndicator ==================== */

/* Unscoped so every host that consumes <BusyIndicator> from this RCL gets the spinner
   and message layout automatically via _content/Compossa.UI.Blazor/components.css
   (ADR-101). The scoped `.razor.css` in Components/Interactivity/BusyIndicator.razor.css
   kept the animation from reaching RCL consumers whose scoped-CSS bundling differed
   from Commerce.Web — symptom reported: "only the text 'Loading document...' shows, no
   spinner". Keep the component markup stable; class names mirror the scoped file so
   either stylesheet renders the same shape. */
.busy-indicator {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1rem;
}

.busy-indicator:not(.visible) {
    display: none;
}

.busy-indicator .busy-message {
    color: var(--text-muted);
    font-size: 0.875rem;
}

/* Arc loader — three concentric stroked circles that spin at different rates and
   directions, stroke-dasharray carving each into an arc. Telerik Animation2-style.
   SVG viewBox is 64x64; the outer wrapper div controls actual pixel size so a caller
   can pass `Size="32"` for an inline spinner or `Size="80"` for a page-loading one. */
.busy-indicator .arc-loader {
    position: relative;
}

.busy-indicator .arc-loader svg {
    width: 100%;
    height: 100%;
    overflow: visible;
}

.busy-indicator .arc-loader .arc {
    fill: none;
    stroke: var(--accent-color, #7cb342);
    stroke-width: 2;
    stroke-linecap: round;
    transform-box: fill-box;
    transform-origin: center center;
}

.busy-indicator .arc-loader .arc-inner {
    stroke-dasharray: 20 100;
    animation: busyArcSpinForward 1.5s linear infinite;
}

.busy-indicator .arc-loader .arc-middle {
    stroke-dasharray: 40 100;
    animation: busyArcSpinReverse 2s linear infinite;
}

.busy-indicator .arc-loader .arc-outer {
    stroke-dasharray: 60 120;
    animation: busyArcSpinForward 2.5s linear infinite;
}

/* Keyframes are namespaced (busy*) so they don't collide with any host-defined
   spin animation reusing the generic `arcSpin*` names. */
@keyframes busyArcSpinForward {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

@keyframes busyArcSpinReverse {
    from { transform: rotate(360deg); }
    to   { transform: rotate(0deg); }
}

/* ── LongRunningBusyIndicator — multi-arc spinner for long-running operation tiles ──
   Sibling visual to BusyIndicator. The BusyIndicator rotates each arc at a different
   constant speed (static dasharray, three rotation animations). The lr-spinner instead
   rotates the WHOLE SVG once per 1.5s while each arc independently pulses its visible
   segment via stroke-dashoffset on an ease-in-out — producing the "feed → download →
   restore" cycling motion that reads as multi-phase work in flight. First seen on
   Compossa.Commerce.Web's NuGet package recovery from FTP tile; promoted here so the
   operations flyout (Compossa.Runtime.Blazor) and any future long-op host share one
   canonical visual. Class names `lr-*` so they cannot shadow BusyIndicator's `.arc-*`. */
.lr-spinner {
    animation: lrSpinnerRotate 1.5s linear infinite;
    transform-origin: center center;
    overflow: visible;
}

.lr-spinner .lr-arc {
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
}

.lr-spinner .lr-arc-outer {
    stroke: var(--accent-color, #81BA2A);
    stroke-dasharray: 60 120;
    animation: lrArcOuter 1.5s ease-in-out infinite;
}

.lr-spinner .lr-arc-middle {
    stroke: var(--info-color, #75BCFF);
    stroke-dasharray: 40 80;
    animation: lrArcMiddle 1.5s ease-in-out infinite;
}

.lr-spinner .lr-arc-inner {
    stroke: var(--accent-color, #81BA2A);
    stroke-dasharray: 20 60;
    animation: lrArcInner 1.5s ease-in-out infinite;
}

@keyframes lrSpinnerRotate {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

@keyframes lrArcOuter {
    0%, 100% { stroke-dashoffset: 0; }
    50%      { stroke-dashoffset: -90; }
}

@keyframes lrArcMiddle {
    0%, 100% { stroke-dashoffset: 0; }
    50%      { stroke-dashoffset: 60; }
}

@keyframes lrArcInner {
    0%, 100% { stroke-dashoffset: 0; }
    50%      { stroke-dashoffset: -40; }
}

/* Optional wrapper used by the LongRunningBusyIndicator component when a label or
   sub-message accompanies the spinner. Compact-tile consumers (operations flyout's
   icon column) render the bare svg.lr-spinner without this wrapper. */
.long-running-busy-indicator {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
}

.long-running-busy-indicator .lr-message {
    font-size: 0.78rem;
    color: var(--text-muted);
    text-align: center;
}

/* ─────────────────────────────────────────────────────────────────────────────
   Tile kebab menu — top-right ⋮ on document tiles. Governed by UX-R1.3
   (docs/Requirements/ux-requirements.md). The button itself uses a calm
   border/hover treatment so it doesn't compete with the status badge it sits
   next to; the flyout reuses the overlay-menu-* styles already defined above
   for the tracked-change kebab pattern (DRY — see overlay-menu-flyout above).
   ───────────────────────────────────────────────────────────────────────── */
.tile-kebab-wrap {
    position: relative;
    display: inline-flex;
}

/* ─────────────────────────────────────────────────────────────────────────────
   Effective-dated rate journal table — shared by VAT rates (FiscalPeriodEdit)
   and AI model rates (LlmModels' Pricing tab). Promoted here per §8 the moment
   a second surface needed it, instead of duplicating into a second page.
   ───────────────────────────────────────────────────────────────────────── */
.empty-tab-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 3rem 2rem;
    color: var(--text-muted);
}

.empty-tab-state p {
    margin: 0 0 1rem;
}

.vat-rates-table {
    overflow-x: auto;
}

.vat-rates-table table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.vat-rates-table th {
    text-align: left;
    padding: 8px 12px;
    background: var(--bg-tertiary);
    color: var(--text-muted);
    font-weight: 500;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    border-bottom: 1px solid var(--border-color);
}

.vat-rates-table td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--border-color);
    color: var(--text-primary);
}

.vat-rates-table tr:hover {
    background: var(--bg-hover);
}

.country-cell {
    font-weight: 600;
}

.rate-cell {
    font-weight: 600;
    font-family: var(--font-mono, monospace);
}

.source-cell {
    white-space: nowrap;
}

.actions-cell {
    width: 40px;
    text-align: center;
}

.source-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    font-size: 10px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.source-badge.vatlayer {
    background: color-mix(in srgb, var(--info-color) 20%, transparent);
    color: var(--info-color);
}

.source-badge.manual {
    background: color-mix(in srgb, var(--warning-color) 20%, transparent);
    color: var(--warning-color);
}

.source-badge.import {
    background: color-mix(in srgb, var(--accent-color) 20%, transparent);
    color: var(--accent-color);
}

.tile-kebab-btn {
    background: none;
    border: 1px solid transparent;
    color: var(--text-muted, #9aa);
    padding: 0.15rem 0.25rem;
    border-radius: 0.25rem;
    cursor: pointer;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.tile-kebab-btn:hover,
.tile-kebab-btn:focus-visible {
    color: var(--text, #e5e5e5);
    background: var(--bg-hover, rgba(255, 255, 255, 0.05));
    border-color: var(--border, rgba(255, 255, 255, 0.1));
    outline: none;
}

/* Invisible full-viewport catcher — closes any open tile kebab when the user
   clicks elsewhere. Mirrors the .dialog-overlay convention. Z-index sits BELOW
   the flyout (overlay-menu-flyout uses ~100) so menu-item clicks reach the
   items first; everywhere else the catcher swallows the click + closes. */
.tile-kebab-catcher {
    position: fixed;
    inset: 0;
    z-index: 50;
    background: transparent;
}

/* Icon slot inside overlay menu items — keeps the icon from squishing when the
   caption is long. The overlay-menu-item rule above already lays out the row;
   this just pins the icon size. Reuses the existing .overlay-menu-item-reject
   for destructive items (Delete / Reject) — no new "destructive" class needed,
   per UX-R5 (reuse, do not invent). */
.overlay-menu-item-icon {
    flex-shrink: 0;
}

/* Tile-kebab flyout anchoring. The base .overlay-menu-flyout is position:fixed
   with no top/left — the existing tracked-change use case (markdownHelpers.js
   compossaAttachOverlayMenu) computes coordinates from the trigger's bounding
   rect at runtime. The tile kebab is pure Blazor (no JS interop) so we override
   to absolute-position the flyout relative to its .tile-kebab-wrap parent
   (which is position:relative). Right-aligned to the trigger so the menu opens
   under-and-leftward from the ⋮ button, never clipping off the right edge of
   tiles near the grid's right column. */
.tile-kebab-wrap > .overlay-menu-flyout {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 100;
}

/* Session-history ⋯ menu (CanvasSessionMenu) — pinned to the TOP-RIGHT corner of the
   conversation canvas (not the command bar), so its flyout opens DOWNWARD, right-aligned
   to the trigger (standard tile-kebab direction). Reuses the overlay-menu-* / tile-kebab-*
   families; this block only anchors the trigger + flyout and lays each row out as
   title-over-relative-time. */
.conversation-widget {
    position: relative;
}

.conversation-session-menu {
    position: absolute;
    top: 0.4rem;
    right: 0.5rem;
    z-index: 5;
}

.session-menu-wrap {
    position: relative;
    display: inline-flex;
}

.session-menu-wrap > .session-menu-flyout {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 100;
    max-width: 320px;
    max-height: 320px;
    overflow-y: auto;
}

.session-menu-item {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.15rem;
    width: 100%;
}

.session-menu-item.selected {
    background: var(--bg-selected, rgba(129, 186, 42, 0.12));
}

.session-menu-item-title {
    display: block;
    max-width: 290px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.session-menu-item-time {
    font-size: 0.72rem;
    color: var(--text-muted, #9aa);
}

.session-menu-empty {
    padding: 0.45rem 0.7rem;
    font-size: 0.82rem;
    color: var(--text-muted, #9aa);
}

/* ───────────────────────── Conversation / chat primitives ─────────────────────────
   Shared chat presentation for any conversational surface (Requirements Conversation,
   Sales Pricing Composer, Redact Conversation tab). Promoted here from
   Compossa.Requirements.UI/wwwroot/conversation.css so every host's composer reads ONE
   source. Feature-rich chrome (markdown body, per-turn metrics, conversation meter,
   prompt-template picker) stays page-local in that file.

   The in-progress / live "AI is working" state uses var(--tertiary-color) — the distinct,
   theme-dynamic *activity* accent also used by the usage heatmaps — so a working tool
   reads apart from the primary action accent. Succeeded = success, failed = error. */

/* AI chat message bubbles. User bubbles right-align with subtle bg, assistant bubbles
   left-align with an accent tint. */
.chat-msg {
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    font-size: 0.9rem;
    line-height: 1.45;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    overflow-wrap: anywhere;
}

.chat-msg > span {
    white-space: pre-wrap;
}

.chat-msg > strong {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}

.chat-msg-user {
    background: var(--bg-subtle, #f3f4f6);
    align-self: flex-end;
    max-width: 85%;
}

/* Turn divider — a user message always starts a new turn, so a subtle top border before
   the second-onward user bubble separates exchange N from N+1. The first is excluded. */
.chat-msg-user:not(:first-child) {
    margin-top: 0.55rem;
    padding-top: 0.85rem;
    border-top: 1px solid var(--border-subtle, rgba(0, 0, 0, 0.08));
}

.chat-msg-assistant {
    background: var(--bg-accent-subtle, #eef6ff);
    align-self: flex-start;
    max-width: 85%;
    position: relative;
}

/* Per-assistant-message action row — fades in on hover/focus-within only. */
.chat-msg-actions {
    display: flex;
    gap: 0.15rem;
    position: absolute;
    bottom: 0.25rem;
    right: 0.35rem;
    opacity: 0;
    transition: opacity 0.15s ease-out;
}

.chat-msg-assistant:hover .chat-msg-actions,
.chat-msg-assistant:focus-within .chat-msg-actions {
    opacity: 1;
}

.chat-msg-action {
    background: transparent;
    border: none;
    padding: 0.2rem 0.4rem;
    border-radius: 4px;
    cursor: pointer;
    line-height: 1;
    color: var(--text-muted, #6b7280);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.chat-msg-action svg {
    display: block;
    width: 14px;
    height: 14px;
}

.chat-msg-action:hover {
    background: rgba(0, 0, 0, 0.06);
    color: var(--text-primary, #1f2937);
}

/* a11y helper — visually hidden but read by screen readers (icon-only buttons). */
.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;
}

/* In-flight assistant bubble while SSE is streaming tokens — subtle pulsing border
   signals "still arriving" (stays the primary accent; this is text arrival, not a tool). */
.chat-msg-streaming {
    border-left: 2px solid var(--accent-color, #81BA2A);
    animation: chat-msg-streaming-pulse 1.4s ease-in-out infinite;
}

@keyframes chat-msg-streaming-pulse {
    0%, 100% { border-left-color: var(--accent-color, #81BA2A); }
    50%      { border-left-color: rgba(129, 186, 42, 0.35); }
}

/* AI tool-use progress chips — surface what the AI is doing during the silent pause
   between text deltas. One container per turn renders the in-flight chips; a per-message
   container persists them in the audit trail. */
.chat-tool-chips {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    margin: 0.1rem 0 0.1rem 0.5rem;
}

.chat-tool-chip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.55rem;
    border-radius: 4px;
    font-size: 0.78rem;
    line-height: 1.3;
    background: var(--bg-subtle, #f3f4f6);
    border-left: 2px solid var(--text-muted, #6b7280);
    color: var(--text-muted, #6b7280);
    max-width: 85%;
    align-self: flex-start;
    /* Do NOT put overflow-wrap here — it inherits to the label and breaks short words
       like "Searching" mid-letter. Wrap is scoped to .chat-tool-chip-detail only. */
}

.chat-tool-chip-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    color: var(--text-muted, #6b7280);
}

.chat-tool-chip-icon svg {
    display: block;
    width: 14px;
    height: 14px;
}

.chat-tool-chip-label {
    flex: 0 1 auto;
    min-width: 0;
    font-weight: 600;
    color: var(--text-primary, #1f2937);
}

.chat-tool-chip-detail {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
    font-size: 0.72rem;
    opacity: 0.85;
}

.chat-tool-chip-detail-source {
    flex: 1 1 16rem;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow-wrap: normal;
}

/* Lifecycle states: chip carries a Status enum (InProgress / Succeeded / Failed / Cancelled)
   written as a lowercase modifier class. In-progress uses the golden activity accent. */
.chat-tool-chip-inprogress {
    border-left-color: var(--tertiary-color, #C8A21A);
    animation: chat-tool-chip-pulse 1.4s ease-in-out infinite;
}

@keyframes chat-tool-chip-pulse {
    0%, 100% { opacity: 0.85; }
    50%      { opacity: 1.0; }
}

.chat-tool-chip-succeeded {
    border-left-color: var(--success-color, #5BA635);
    opacity: 0.85;
}

.chat-tool-chip-failed {
    border-left-color: var(--error-color, #E52520);
    background: rgba(229, 37, 32, 0.06);
    color: var(--text-primary, #1f2937);
}

.chat-tool-chip-failure {
    flex: 1 1 100%;
    margin-top: 0.15rem;
    font-size: 0.72rem;
    color: var(--error-color, #E52520);
    font-weight: 500;
}

/* Cancelled — the user paused/cancelled the run while this tool was mid-flight. Neutral
   (not red): the tool didn't error, the run was stopped out from under it. */
.chat-tool-chip-cancelled {
    border-left-color: var(--text-muted, #6b7280);
    opacity: 0.7;
}

/* Status glyph (✓/✗) at the right of a terminal-state chip — the primary outcome signal. */
.chat-tool-chip-status {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    margin-left: 0.25rem;
}
.chat-tool-chip-status svg {
    display: block;
    width: 13px;
    height: 13px;
    stroke-width: 2.5;
}
.chat-tool-chip-status-ok        { color: var(--success-color, #5BA635); }
.chat-tool-chip-status-fail      { color: var(--error-color, #E52520); }
.chat-tool-chip-status-cancelled { color: var(--text-muted, #6b7280); }

/* Live chip rows: identical SIZE/POSITION to historical. Only IN-PROGRESS live chips tint to
   the golden activity accent + animate the icon; once a chip reaches a terminal state its
   status border (green/red) + ✓/✗ glyph take over, so a completed tool reads as done mid-run. */
.chat-tool-chips-live .chat-tool-chip-inprogress {
    border-left-color: var(--tertiary-color, #C8A21A);
}

.chat-tool-chips-historical .chat-tool-chip {
    opacity: 0.85;
}

/* While LIVE AND in-progress, swap the static icon for the cycling braille-dot spinner (same
   glyph the in-flight bubble uses). CSS-only: SVG stays for sizing but visibility-hidden, and
   the ::before pseudo overlays the spinner in its place. Once a live chip reaches a terminal
   state the spinner is dropped so its static icon + ✓/✗ glyph show — completed stays completed. */
.chat-tool-chips-live .chat-tool-chip-icon {
    position: relative;
    width: 14px;
    height: 14px;
}

.chat-tool-chips-live .chat-tool-chip-inprogress .chat-tool-chip-icon svg {
    visibility: hidden;
}

.chat-tool-chips-live .chat-tool-chip-inprogress .chat-tool-chip-icon::before {
    content: '⠋';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--tertiary-color, #C8A21A);
    font-weight: 600;
    font-size: 14px;
    line-height: 1;
    animation: chat-busy-spin 0.95s steps(1, end) infinite;
}

/* Busy indicator for the in-flight chat bubble: braille-dot spinner + cycling verb. The
   spinner shares the golden activity accent so "AI is working" reads consistently. */
.chat-busy {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--text-muted, #6b7280);
    font-style: italic;
}

.chat-busy-spinner {
    display: inline-block;
    width: 1ch;
    font-style: normal;
    color: var(--tertiary-color, #C8A21A);
    font-weight: 600;
}

.chat-busy-spinner::before {
    content: '⠋';
    animation: chat-busy-spin 0.95s steps(1, end) infinite;
}

@keyframes chat-busy-spin {
      0% { content: '⠋'; }
     10% { content: '⠙'; }
     20% { content: '⠹'; }
     30% { content: '⠸'; }
     40% { content: '⠼'; }
     50% { content: '⠴'; }
     60% { content: '⠦'; }
     70% { content: '⠧'; }
     80% { content: '⠇'; }
     90% { content: '⠏'; }
    100% { content: '⠋'; }
}

.chat-busy-verb {
    animation: chat-busy-verb-fade 0.35s ease-out;
}

@keyframes chat-busy-verb-fade {
    from { opacity: 0.45; }
    to   { opacity: 1; }
}

/* ============================================================
   Inline icon-remove button — the platform idiom for removing a row/item
   from an editable table (Properties tabs, rule/definition editors).
   Promoted from DocumentEdit/DocumentNew page-scoped CSS per ux-guidelines §8
   (second surface needed it). Ghost by default, danger on hover.
   ============================================================ */
.btn-icon-remove {
    background: transparent;
    border: 1px solid var(--border-color, #e5e7eb);
    border-radius: 4px;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0.1rem 0.45rem;
}

.btn-icon-remove:hover {
    background: var(--bg-hover);
    color: var(--error-color, #E52520);
    border-color: var(--error-color, #E52520);
}

/* ==================== Hero page identity (ux-guidelines §9, task #51) ====================
   Shell-rendered from NavigationContext: icon + title inline, one summary paragraph.
   Message bars render ABOVE; quick actions pin top-right. Pages never hand-roll this. */
.content-hero {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin: 0 0 0.75rem;
}

/* Breadcrumb trail (ux-guidelines §9): sits above the hero title on 2nd-level pages so the user
   sees the navigation depth. Ancestors are links; the current (leaf) crumb is muted plain text. */
.content-hero-breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    margin: 0 0 0.35rem;
    font-size: 0.8rem;
    line-height: 1.2;
}

.content-hero-breadcrumb-link {
    color: var(--text-muted);
    text-decoration: none;
}

.content-hero-breadcrumb-link:hover {
    color: var(--link-color);
    text-decoration: underline;
}

.content-hero-breadcrumb-sep {
    color: var(--text-muted);
    opacity: 0.6;
}

.content-hero-breadcrumb-current {
    color: var(--text-secondary);
    font-weight: 500;
}

.content-hero-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
    font-size: 1.35rem;
}

.content-hero-icon {
    display: inline-flex;
    align-items: center;
    color: var(--accent-color);
}

.content-hero-badge {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border: 1px solid var(--accent-color);
    color: var(--accent-color);
    border-radius: 3px;
    padding: 0.1rem 0.4rem;
}

.content-hero-summary {
    margin: 0.25rem 0 0;
    color: var(--text-muted);
    max-width: 72ch;
}

.content-hero-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex: 0 0 auto;
}

/* Compact page shortcuts, sharing the shell's circular action treatment. */
.hero-quick-action {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 28px;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--accent-color);
    color: var(--accent-text, #fff);
    cursor: pointer;
}

.hero-quick-action:hover:not(:disabled) { opacity: 0.85; }
.hero-quick-action:focus-visible {
    outline: 2px solid var(--text-primary);
    outline-offset: 3px;
}
.hero-quick-action:disabled { opacity: 0.45; cursor: not-allowed; }
.hero-quick-action-count {
    position: absolute;
    inset-block-start: -0.5rem;
    inset-inline-end: -0.5rem;
    padding: 0.05rem 0.25rem;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-full);
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-size: 0.65rem;
    line-height: 1.2;
}

/* ============================================================
   Wizard dialog (steps indicator + content + footer)
   Shared home per ux-guidelines §8 - promoted from the page-scoped
   copies in Packages/Releases (migrate those on touch).
   ============================================================ */
.wizard-steps {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.25rem 1.5rem;
    background: var(--bg-tertiary);
    border-bottom: 1px solid var(--border-color);
    gap: 0.5rem;
}

.wizard-step {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.wizard-step.active {
    color: var(--accent-color);
}

.wizard-step.completed {
    color: var(--success-color);
}

.wizard-step-number {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--bg-secondary);
    border: 2px solid currentColor;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    font-weight: 600;
}

.wizard-step.active .wizard-step-number {
    background: var(--accent-color);
    border-color: var(--accent-color);
    color: var(--accent-text, #ffffff);
}

.wizard-step.completed .wizard-step-number {
    background: var(--success-color);
    border-color: var(--success-color);
    color: #ffffff;
}

.wizard-step-connector {
    width: 40px;
    height: 2px;
    background: var(--border-color);
}

.wizard-step-connector.completed {
    background: var(--success-color);
}

.wizard-content {
    padding: 1.5rem;
    overflow-y: auto;
    max-height: 50vh;
}

.wizard-step-title {
    margin: 0 0 1rem;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary);
}

.wizard-footer {
    display: flex;
    justify-content: space-between;
    padding: 1rem 1.5rem;
    border-top: 1px solid var(--border-color);
    background: var(--bg-tertiary);
}

.wizard-footer-left,
.wizard-footer-right {
    display: flex;
    gap: 0.75rem;
}

/* Wizard-size escape hatch: dialogs that opt into .modal-xl span near the full viewport
   (component-picker idiom: big intention tiles + summary rail). The base .modal rule above
   pins max-width:480px with !important, so this MUST also carry !important and win on
   specificity (.modal.modal-xl). */
.modal-overlay > .modal.modal-xl {
    width: min(1400px, calc(100vw - 3rem)) !important;
    max-width: min(1400px, calc(100vw - 3rem)) !important;
    height: calc(100vh - 3rem) !important;
    max-height: calc(100vh - 3rem) !important;
}

/* Dialog close glyph: one consistent ghost-x across every modal header (no default
   button chrome). Mirrors the lightbox-close voice at dialog scale. */
.modal-header .modal-close {
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    border-radius: var(--radius-xs, 4px);
    color: var(--text-muted);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}

.modal-header .modal-close:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
}

/* ============================================================
   Composer kiosk inline affordances (MWP canvas dance increment 1)
   TemplateInputAffordance / SelectAffordance / ComparisonAffordance / ConfirmationAffordance /
   HandoffAffordance (Compossa.Composer.UI/Affordances) all render this shared card shape - the
   structural equivalent of the retired slice-1 .composer-step. Global here rather than page-scoped:
   these components render via <DynamicComponent> inside ComposerCanvas, so their root elements
   never carry that page's CSS-isolation scope attribute - reaching them from a page-scoped
   .razor.css would require ::deep, which Global Constraints forbid introducing anew. components.css
   is already linked in Commerce.Web's App.razor head (ux-guidelines §8), so no ::deep is needed.
   ============================================================ */
.affordance-card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* One-line agent-produced note under a ComparisonAffordance tile - optional, only rendered when a
   caller supplies FitNoteFor. The Eijerkamp seed leaves it unpopulated and instead narrates the
   same fit reasoning as a normal chat bubble (the post-advance acknowledgment turn). */
.comparison-fit-note {
    font-size: 0.8rem;
    color: var(--text-muted);
    margin: 0;
}

/* Persistent warning banner (promoted from NuGetApiTokens' .token-warning - a second consumer,
   NuGetMcpServers' zero-active-token banner, moved it here per ux-guidelines §8 rule 1). Icon +
   message row, orange-on-tint, for a standing condition the user should notice without it reading
   as an error toast. */
.warning-banner {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.9rem 1.25rem;
    background: rgba(247, 147, 30, 0.08);
    border: 1px solid var(--warning-color);
    border-radius: 8px;
    color: var(--warning-color);
    width: 100%;
}

.warning-banner svg {
    flex-shrink: 0;
}

/* ==================== Swipe-to-reveal (generic) ====================
   Generalized from Compossa.Requirements.UI's LinkedArtifactsList swipe-to-
   unlink gesture (artifact-tile.css) once a second consumer (Organization
   Integrations cards, enable/disable) needed the same interaction -
   ux-guidelines §8 rule 1: the moment a second surface needs it, it moves
   here instead of being duplicated per host. JS: js/swipeReveal.js
   (attachSwipeReveal/detachSwipeReveal). Unlike the single fixed "Unlink"
   action the original shipped with, this version supports any action label +
   colour variant via the host's own button markup/class inside
   .swipe-reveal-action. */

.swipe-tile-wrap {
    position: relative;
    display: block;
    border-radius: 6px;
    overflow: hidden;
}

/* Reveal panel that sits behind the tile and becomes visible as the tile
   translates left. pointer-events:none until fully open so a mid-swipe
   accidental release doesn't fire the action. Host supplies the label/icon
   and picks a colour variant (.danger / .success below); the panel itself
   only owns position/sizing. */
.swipe-reveal-action {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 96px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    background: var(--bg-secondary);
    border-radius: 0 6px 6px 0;
    pointer-events: none;
    cursor: pointer;
    z-index: 0;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-family: inherit;
}

.swipe-reveal-action.danger {
    color: var(--error-color, #E52520);
    border: 1px solid var(--error-color, #E52520);
}

.swipe-reveal-action.success {
    color: var(--success-color, #81BA2A);
    border: 1px solid var(--success-color, #81BA2A);
}

/* Interactive only when the wrap is fully open. */
.swipe-tile-wrap.swipe-open .swipe-reveal-action {
    pointer-events: auto;
}

.swipe-reveal-action.danger:hover,
.swipe-reveal-action.danger:focus-visible {
    background: var(--error-color, #E52520);
    color: #fff;
    outline: none;
}

.swipe-reveal-action.success:hover,
.swipe-reveal-action.success:focus-visible {
    background: var(--success-color, #81BA2A);
    color: #fff;
    outline: none;
}

/* The slider — whatever is normally visible (a card, a row, …). touch-action:
   pan-y tells the browser "vertical scrolls are yours, horizontal is ours". */
.swipe-tile-wrap[data-swipe] > .swipe-tile {
    position: relative;
    z-index: 1;
    touch-action: pan-y;
}

/* Drag-active state, set by the JS gesture handler once it locks to the X
   axis — disables the tile's normal transition so JS's transform doesn't
   fight a competing animation. */
.swipe-tile-wrap.swiping > .swipe-tile {
    transition: none;
    cursor: grabbing;
}

/* Open state — tile sits at -96px (panel width), reveal action interactive.
   Suppress hover styles so the tile doesn't shift further while the user
   reaches for the action button. */
.swipe-tile-wrap.swipe-open > .swipe-tile {
    background: var(--bg-secondary);
    border-color: var(--border-color);
}

.warning-banner a {
    color: inherit;
    text-decoration: underline;
}

/* Gridview (bug 01M0605WVPBGXDCVECZ3K6AQVX): a genuine SHARED row-list pattern, not another
   page-scoped lookalike. Organizations/PackageExplorer/Contacts/etc. each hand-roll their own
   .list-header/.list-row in a page-scoped .razor.css with hardcoded per-page column widths —
   copy-pasted, not reused, and itself a §8 CSS-scope violation (2+ surfaces ⇒ belongs here).
   This pattern is left in place (out of scope for this fix — no regression risk to touch it);
   .gridview-* is the one new consumers should reach for instead of copying .list-row again.
   Column widths are supplied per-consumer via --gridview-columns so the same rule fits any
   column count/shape, from single-line fixed columns to a wrapping multi-line value column.
   NO overflow: hidden on the container (same fix PackageExplorer.razor.css already carries,
   see its "Lists - NO overflow: hidden" comment, and the same bug .organizations-list still
   has latently — Organizations just rarely has enough rows to expose it): a large page size
   (e.g. 100 items) needs ContentPanel's OWN outer scroll container (the height-chain,
   ux-guidelines §2.4.1) to scroll the overflow, which overflow: hidden here would clip instead.
   Rounded corners go on the header/last-row children directly rather than the container. */
.gridview {
    display: flex;
    flex-direction: column;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
}

.gridview-header {
    display: grid;
    grid-template-columns: var(--gridview-columns, 1fr);
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    background: var(--bg-tertiary);
    border-bottom: 1px solid var(--border-color);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* Each row is its OWN grid (not joined to the header via display:contents) — alignment with
   the header comes from sharing the same --gridview-columns, exactly like Organizations/
   PackageExplorer already do. A row's height is then free to grow with its own content (e.g. a
   wrapped label or an auto-sizing textarea) without needing a table-layout workaround. */
.gridview-row {
    display: grid;
    grid-template-columns: var(--gridview-columns, 1fr);
    gap: 0.75rem;
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--border-color);
    transition: background 0.15s;
}

.gridview-row:last-child {
    border-bottom: none;
    border-radius: 0 0 var(--radius-md) var(--radius-md);
}

.gridview-row:hover {
    background: var(--bg-hover);
}

/* :focus-within (an admin editing a cell inside this row) gets the same "current row" treatment
   Organizations reserves for .selected — no selection model added, per this bug's own
   constraint (a gridview affordance, not row selection). */
.gridview-row.selected,
.gridview-row:focus-within {
    background: var(--bg-selected, rgba(129, 186, 42, 0.08));
    border-left: 3px solid var(--accent-color);
    padding-left: calc(1rem - 3px);
}

/* Capability pills: render one pill per supported capability, nothing for what's absent — used by
   AiModelCatalogPanel's model catalog and the ProjectEdit code-hosting capabilities row. Promoted here
   per CSS ownership rule 1 (second-surface duplication triggers a move to the shared lib) rather than
   adding a third page-scoped copy. */
.capability-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.capability-pill {
    border: 1px solid var(--accent-color);
    border-radius: 999px;
    padding: 0.15rem 0.45rem;
    font-size: 0.75rem;
}

/* Shared proposal toolbar buttons, including CommandButton-rendered actions. */
.propose-edit-tool-btn {
    background: transparent;
    border: 1px solid transparent;
    border-radius: 4px;
    padding: 0.25rem 0.4rem;
    cursor: pointer;
    color: var(--text-muted);
    transition: all 0.12s ease;
    line-height: 1;
}

    .propose-edit-tool-btn:hover {
        background: var(--bg-hover, rgba(129, 186, 42, 0.12));
        color: var(--accent-color, #81BA2A);
        border-color: rgba(129, 186, 42, 0.4);
    }

    .propose-edit-tool-btn:active {
        transform: scale(0.96);
    }

    .propose-edit-tool-btn:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    /* Busy state — CommandButton appends .cmd-busy when State.IsBusy. The
       platform ships a bouncing-bar style by default at the bottom of the
       button; we tint the foreground so the icon still reads even while
       greyed during the Anthropic round-trip. */
    .propose-edit-tool-btn.cmd-busy {
        color: var(--accent-color, #81BA2A);
    }

/* ─── Settings cards ────────────────────────────────────────────────────────
   Icon + semibold title + muted summary, then label/hint rows with the control on
   the right. Shared by Settings, AppearanceChoices, the site Features tab and the document-type
   pages (Compossa.DocumentStore.UI), so it lives in the shared component stylesheet (§8). */
.settings-card {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 1.25rem;
    margin-bottom: 1rem;
}

.settings-card:last-child {
    margin-bottom: 0;
}

.settings-card .card-header {
    display: flex;
    align-items: flex-start;
    gap: 0.875rem;
    margin-bottom: 1rem;
}

.settings-card .card-header:last-child {
    margin-bottom: 0;
}

.settings-card .card-header svg {
    color: var(--accent-color);
    flex-shrink: 0;
    margin-top: 0.125rem;
}

.settings-card .card-header-action {
    margin-left: auto;
    flex-shrink: 0;
}

.settings-card .card-title-group {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.settings-card .card-title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
}

.settings-card .card-subtitle {
    font-size: 0.8rem;
    color: var(--text-muted);
}

.settings-rows {
    display: flex;
    flex-direction: column;
}

.settings-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.875rem 0;
    border-bottom: 1px solid var(--border-color);
}

.settings-row:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.settings-row:first-child {
    padding-top: 0;
}

/* Row whose control needs the full width (tag pickers): label above, control below. */
.settings-row.settings-row-stacked {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
}

.row-label-group {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.row-label {
    color: var(--text-secondary);
    font-size: 0.9rem;
}

.row-hint {
    color: var(--text-muted);
    font-size: 0.75rem;
}

.settings-select {
    appearance: none;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: 0.9rem;
    padding: 0.5rem 2rem 0.5rem 0.75rem;
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%23858585' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
}

.settings-select:hover {
    border-color: var(--text-muted);
}

.settings-select:focus {
    outline: none;
    border-color: var(--accent-color);
}

.settings-select:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Text and number inputs in a settings row: same frame as .settings-select. */
.settings-input {
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: 0.9rem;
    padding: 0.5rem 0.75rem;
    min-width: 16rem;
}

.settings-input:hover {
    border-color: var(--text-muted);
}

.settings-input:focus {
    outline: none;
    border-color: var(--accent-color);
}

@media (max-width: 768px) {
    .settings-row {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.75rem;
    }
}

/* ==================== Usage dashboard (Insights tabs, Agentic Workload) ====================
   One dashboard vocabulary for every usage/load surface: tab head (chips left, UsagePeriodPicker
   right), hero figures, iconed mini tiles, titled sections and the month strip (UsageMonthStrip).
   Promoted from Insights.razor.css when Agentic Workload became the second surface (backlog
   01M51Y8JX9TYQZBKZZJZHMTYXK); page-only parts (breakdown table, budget gauge) stay with their page. */

/* Tab header row: chips or title left, the shared period picker pinned right. */
.usage-tab-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.25rem;
}

.usage-tab-title {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--text-secondary);
}

.usage-tab-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.ai-hero {
    /* Shared left inset so every row under the bled-out .ai-hero-figures grid lines up with the
       mini tile's left edge below it, not just .ai-hero's own padding. */
    --hero-content-inset: calc(1rem + 1px);
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 1.5rem 1.75rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

/* Shares .ai-mini-row's columns (repeat(3,1fr) + same gap) so hero figures and the tiles below line
   up. The negative margin bleeds the grid to .ai-hero's border edges, cancelling its padding;
   without it columns 2 and 3 drift left of the tiles they align with. */
.ai-hero-figures {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
    margin: 0 -1.75rem;
}

/* Same inset as the tile's own edge (border + padding), not its label: matching the iconed label
   put ~70px of empty space before the value (reverted in 2026-08-18 UAT). */
.ai-hero-block { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; padding-left: var(--hero-content-inset); }
/* First and last columns pinned: a conditionally rendered middle block must not shift the last one. */
.ai-hero-cost { grid-column: 1; }
.ai-hero-tokens { grid-column: 3; }

.ai-hero-value {
    font-size: 2.6rem;
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -0.02em;
    color: var(--text-primary);
}

/* The lead figure carries the "activity" accent (--tertiary-color), the same hue as the trend chart. */
.ai-hero-cost .ai-hero-value { color: var(--tertiary-color); }

.ai-hero-label {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-secondary);
}

.ai-hero-sub { font-size: 0.85rem; color: var(--text-secondary); margin-top: 0.2rem; }
.ai-dot { color: var(--text-muted); margin: 0 0.15rem; }
.ai-hero-proj-value { color: var(--text-secondary); }
.ai-delta { font-size: 0.78rem; font-weight: 600; white-space: nowrap; }
.ai-delta.up { color: var(--warning-color); }
.ai-delta.down { color: var(--success-color); }
.ai-delta-note { color: var(--text-muted); font-weight: 400; }

.ai-hero-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    border-top: 1px solid var(--border-color);
    padding-top: 0.85rem;
}

/* In the tab head the chips are the first row: no divider above them. */
.usage-tab-head .ai-hero-meta {
    border-top: none;
    padding-top: 0;
}

.ai-chip {
    font-size: 0.74rem;
    color: var(--text-secondary);
    background: var(--bg-tertiary);
    border-radius: 999px;
    padding: 0.2rem 0.65rem;
}

.ai-chip-active { color: var(--accent-color); background: color-mix(in srgb, var(--accent-color) 12%, transparent); font-weight: 600; }

.ai-hero-info {
    font-size: 0.74rem;
    color: var(--text-muted);
    cursor: pointer;
    border-bottom: 1px dotted var(--text-muted);
}
.ai-hero-info:hover { color: var(--text-secondary); }

/* Mini tiles */
.ai-mini-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
/* Four-up tiles keep a tighter gap; they are not paired with the hero columns. */
.ai-mini-row-4 { grid-template-columns: repeat(4, 1fr); gap: 0.75rem; }

.ai-mini {
    position: relative;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 0.85rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.ai-mini-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-secondary);
}

.ai-mini-value { font-size: 1.25rem; font-weight: 600; color: var(--text-primary); }
/* Semantic status, not skin: a healthy metric reads green and a breach red in every theme. */
.ai-mini-value-good { color: var(--success-color); }
.ai-mini-value-bad { color: var(--error-color); }
.ai-mini-model { font-size: 0.95rem; font-family: var(--font-mono); }
.ai-mini-note { font-size: 0.72rem; color: var(--text-secondary); font-weight: 400; }
.ai-mini-sub { font-size: 0.72rem; color: var(--text-muted); }

/* Iconed tile: a glyph tile in the theme accent beside label and value (re-tints per .theme-* skin). */
.ai-mini-iconed { flex-direction: row; align-items: center; gap: 0.85rem; }
.ai-mini-iconed .ai-mini-body { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; flex: 1; }
.ai-mini-iconed .ai-mini-model { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ai-mini-ico {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    color: var(--accent-color);
    background: color-mix(in srgb, var(--accent-color) 14%, transparent);
}
.ai-mini-ico svg { width: 20px; height: 20px; }

/* Titled section */
.ai-section { display: flex; flex-direction: column; gap: 0.5rem; }
.ai-section-title { font-size: 0.85rem; font-weight: 600; color: var(--text-secondary); }

/* Month strip (UsageMonthStrip): zoom into one month */
.ai-month-strip { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; padding: 0.75rem 0 0.6rem; border-bottom: 1px solid var(--border-color); margin-bottom: 0; }
.ai-month-cell { display: flex; flex-direction: column; align-items: center; min-width: 62px; padding: 0.35rem 0.5rem 0.25rem; background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 4px; cursor: pointer; color: var(--text-color); gap: 0.12rem; transition: border-color 0.1s, background 0.1s; }
.ai-month-cell:hover { background: var(--bg-hover); border-color: var(--accent-color); }
.ai-month-selected { background: color-mix(in srgb, var(--accent-color) 15%, transparent); border-color: var(--accent-color) !important; }
.ai-month-label { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; }
.ai-month-cost { font-size: 0.65rem; color: var(--text-muted); }
.ai-month-selected .ai-month-cost { color: var(--accent-color); opacity: 0.85; }
.ai-month-heat { width: 100%; height: 3px; background: var(--border-color); border-radius: 2px; overflow: hidden; }
.ai-month-heat > span { display: block; height: 100%; background: var(--accent-color); border-radius: 2px; transition: width 0.2s; }
.ai-month-clear { display: inline-flex; align-items: center; gap: 0.2rem; padding: 0.25rem 0.6rem; background: color-mix(in srgb, var(--accent-color) 10%, transparent); border: 1px dashed var(--accent-color); border-radius: 12px; font-size: 0.7rem; color: var(--accent-color); cursor: pointer; align-self: center; margin-left: 0.25rem; }
.ai-month-clear:hover { background: color-mix(in srgb, var(--accent-color) 20%, transparent); }

@media (max-width: 900px) {
    .ai-mini-row, .ai-mini-row-4 { grid-template-columns: 1fr; }
    .ai-hero-figures { grid-template-columns: 1fr; margin: 0; }
    .ai-hero-value { font-size: 2.1rem; }
}
