/* ==========================================================================
   RISE UI — shared component layer  (docs/DESIGN_SYSTEM.md)
   --------------------------------------------------------------------------
   OPT-IN. Every component rule is scoped to `[data-ui="v2"]`, which is set on
   <body> only by pages that declare `{% block ui_version %}v2{% endblock %}`.
   A page that does not opt in renders EXACTLY as before — that is what keeps
   the sealed Review / Selector / Catalogue-Review surfaces pixel-identical
   while the rest of the platform is converted (docs/STABILITY.md).

   The colour palette is NOT redefined here. base.html owns --accent,
   --surface*, --text*, --success/--warning/--danger and their light-theme
   overrides; this file only ADDS the axes base never had (spacing, type,
   small radii, mono/tabular numerals, soft semantic fills, z-index) and then
   builds components out of them.

   RULE: no raw colour literals below the token blocks. Everything reads
   var(--*) so both themes work by construction.
   ========================================================================== */

/* ── Additive tokens — theme-independent ─────────────────────────────────── */
:root {
    /* Spacing. base.html had no scale at all; padding was hardcoded per
       component, which is why nothing lines up between pages. */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-7: 32px;
    --space-8: 48px;

    /* Type. Also absent — sizes were literal px everywhere. */
    --fs-xs: 11px;
    --fs-sm: 12px;
    --fs-base: 13px;
    --fs-md: 15px;
    --fs-lg: 18px;
    --fs-xl: 24px;
    --fs-2xl: 30px;
    --lh-tight: 1.2;
    --lh-base: 1.55;
    --tracking-label: 0.08em;

    /* Small radii. The smallest base radius is --radius-md: 14px, which is
       why every small control reads as a blob. */
    --radius-xs: 6px;
    --radius-sm: 8px;

    /* Metrics and logs must not jitter as they poll. Space Grotesk is
       proportional, so a counter ticking 9 -> 10 shifts everything after it. */
    --font-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas,
        'Liberation Mono', monospace;

    /* Control geometry */
    --control-h-sm: 28px;
    --control-h: 34px;
    --control-h-lg: 40px;

    /* Z-index — these values already existed as magic numbers in base.html;
       naming them stops the next person guessing. */
    --z-sticky: 40;
    --z-nav: 80;
    --z-dropdown: 200;
    --z-modal: 8000;
    --z-badge: 9000;
    --z-toast: 9100;

    --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* ── Additive tokens — dark (default) ────────────────────────────────────── */
:root {
    --on-accent: #ffffff;
    --accent-line: rgba(120, 214, 255, 0.34);
    --success-soft: rgba(94, 230, 160, 0.12);
    --success-line: rgba(94, 230, 160, 0.34);
    --warning-soft: rgba(255, 209, 102, 0.12);
    --warning-line: rgba(255, 209, 102, 0.34);
    --danger-soft: rgba(255, 125, 141, 0.12);
    --danger-line: rgba(255, 125, 141, 0.36);
    --neutral-soft: rgba(221, 230, 255, 0.07);
    --row-hover: rgba(120, 214, 255, 0.06);
    --console-bg: rgba(2, 5, 15, 0.55);
    --overlay: rgba(2, 5, 15, 0.62);
}

/* ── Additive tokens — light ─────────────────────────────────────────────── */
body.theme-light {
    --on-accent: #ffffff;
    --accent-line: rgba(61, 126, 255, 0.42);
    --success-soft: rgba(18, 157, 105, 0.11);
    --success-line: rgba(18, 157, 105, 0.36);
    --warning-soft: rgba(185, 129, 0, 0.12);
    --warning-line: rgba(185, 129, 0, 0.36);
    --danger-soft: rgba(214, 69, 90, 0.1);
    --danger-line: rgba(214, 69, 90, 0.36);
    --neutral-soft: rgba(20, 32, 65, 0.05);
    --row-hover: rgba(61, 126, 255, 0.05);
    --console-bg: rgba(20, 32, 65, 0.04);
    --overlay: rgba(20, 32, 65, 0.42);
}

/* ==========================================================================
   Everything below is opt-in.
   ========================================================================== */

/* The `hidden` attribute must keep working. Any component here that sets an
   explicit `display` (.panel is flex, .stat is grid, …) otherwise BEATS the UA
   `[hidden] { display: none }` rule, and a panel marked hidden renders anyway —
   which is exactly how the Search page's clip inspector showed up empty on
   every page load. */
[data-ui="v2"] [hidden] {
    display: none !important;
}

/* ── Page header ─────────────────────────────────────────────────────────── */
[data-ui="v2"] .page-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}

[data-ui="v2"] .page-header__text {
    display: grid;
    gap: var(--space-2);
    min-width: 0;
}

[data-ui="v2"] .page-header__title {
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: 700;
    line-height: var(--lh-tight);
    letter-spacing: -0.01em;
    color: var(--text);
}

[data-ui="v2"] .page-header__sub {
    margin: 0;
    font-size: var(--fs-base);
    line-height: var(--lh-base);
    color: var(--text-muted);
    max-width: 78ch;
}

[data-ui="v2"] .page-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* Sticky section header — keeps the tab bar reachable on long pages. */
[data-ui="v2"] .page-sticky {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    padding: var(--space-3) 0;
    margin-bottom: var(--space-5);
    background: linear-gradient(
        to bottom,
        var(--app-bg) 0%,
        var(--app-bg) 68%,
        transparent 100%
    );
}

/* ── Section label ───────────────────────────────────────────────────────── */
[data-ui="v2"] .label {
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-soft);
}

/* ── Panel — the workhorse card ──────────────────────────────────────────────
   Deliberately lighter than base's .surface (radius 28 + blur(16px) + a
   ::before sheen). .surface is a hero treatment; stacking eleven of them is
   what made the Vision index read as one undifferentiated wall. */
[data-ui="v2"] .panel {
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: var(--surface-soft);
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-md);
}

[data-ui="v2"] .panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--surface-border);
    /* Children inherit from here, not from body — without this a muted hint in
       __actions renders LARGER than the panel title next to it. */
    font-size: var(--fs-base);
}

[data-ui="v2"] .panel__title {
    margin: 0;
    font-size: var(--fs-md);
    font-weight: 600;
    line-height: var(--lh-tight);
    color: var(--text);
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

[data-ui="v2"] .panel__title i {
    color: var(--accent);
    font-size: 0.9em;
}

[data-ui="v2"] .panel__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    /* ⛔ A FLEX ITEM DEFAULTS TO `min-width: auto`, SO THIS BOX REFUSES TO SHRINK
       BELOW ITS CONTENT — and a `.u-truncate` child inside it therefore never
       gets a constrained width, so `text-overflow: ellipsis` never fires and the
       text escapes the panel instead of ellipsing. The utility sets
       `min-width: 0` on ITSELF, which is not enough: every flex ancestor between
       it and the constrained box has to allow the shrink too. Panel headers hold
       operator data (filenames, match names, tallies), so this is the difference
       between a tidy header and one that prints across the panel beside it. */
    min-width: 0;
}

/* A pill is a summary that GROWS — counts, tallies, "N no camera" appended to
   it later. `white-space: nowrap` is right for a two-word status and wrong the
   moment the text is data, so in a header the pill is allowed to wrap rather
   than run off the edge. */
[data-ui="v2"] .panel__actions .pill {
    white-space: normal;
    text-align: right;
}

[data-ui="v2"] .panel__body {
    padding: var(--space-5);
    display: grid;
    gap: var(--space-4);
    align-content: start;
    min-width: 0;
}

[data-ui="v2"] .panel__body > p {
    margin: 0;
    font-size: var(--fs-base);
    line-height: var(--lh-base);
    color: var(--text-muted);
}

[data-ui="v2"] .panel__foot {
    padding: var(--space-3) var(--space-5);
    border-top: 1px solid var(--surface-border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    font-size: var(--fs-sm);
    color: var(--text-soft);
}

/* .panel--flush: for a panel whose body is a table or a console — the child
   supplies its own padding and must reach the panel edge. */
[data-ui="v2"] .panel--flush .panel__body {
    padding: 0;
    gap: 0;
}

[data-ui="v2"] .panel--quiet {
    background: transparent;
}

[data-ui="v2"] .panel--accent {
    border-color: var(--accent-line);
}

/* ── Stats ───────────────────────────────────────────────────────────────── */
[data-ui="v2"] .stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--space-3);
}

[data-ui="v2"] .stat-grid--tight {
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
}

[data-ui="v2"] .stat {
    display: grid;
    gap: var(--space-2);
    align-content: start;
    padding: var(--space-4) var(--space-5);
    background: var(--surface-soft);
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-md);
    min-width: 0;
    text-decoration: none;
    color: inherit;
}

[data-ui="v2"] .stat__label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-soft);
}

[data-ui="v2"] .stat__label i {
    color: var(--accent);
    font-size: 1em;
}

[data-ui="v2"] .stat__value {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
    font-size: var(--fs-2xl);
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.02em;
    color: var(--text);
    overflow-wrap: anywhere;
}

[data-ui="v2"] .stat__meta {
    font-size: var(--fs-xs);
    line-height: var(--lh-base);
    color: var(--text-soft);
}

[data-ui="v2"] .stat__value--sm {
    font-size: var(--fs-lg);
}

/* An anchor stat gets affordance; a static one must not pretend to be one. */
[data-ui="v2"] a.stat {
    transition: border-color 0.15s var(--ease), background 0.15s var(--ease),
        transform 0.15s var(--ease);
}

[data-ui="v2"] a.stat:hover {
    border-color: var(--accent-line);
    background: var(--accent-soft);
    transform: translateY(-1px);
}

[data-ui="v2"] a.stat:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

[data-ui="v2"] .stat--ok .stat__value { color: var(--success); }
[data-ui="v2"] .stat--warn .stat__value { color: var(--warning); }
[data-ui="v2"] .stat--err .stat__value { color: var(--danger); }

/* ── Buttons ─────────────────────────────────────────────────────────────────
   Replaces four problems in base.html's button rules:
     - border-radius 999px on everything -> no visual hierarchy, all lozenges
     - min-height 44px in a dense operator dashboard
     - uppercase + .14em tracking + weight 700 on EVERY button, so a tertiary
       action shouts exactly as loudly as the primary one
     - destructive actions filled solid at primary size, which made STOP the
       loudest object on the Vision page

   One convention: .btn + .btn--<variant> + .btn--sm/lg. The legacy class
   names are aliased at the bottom of this block so existing markup keeps
   working during the rollout — no commit has to rewrite every button. */
[data-ui="v2"] .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    height: var(--control-h);
    min-height: 0;
    padding: 0 var(--space-4);
    border: 1px solid var(--surface-border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-strong);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-base);
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0;
    text-transform: none;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
        color 0.15s var(--ease), opacity 0.15s var(--ease);
}

[data-ui="v2"] .btn i {
    font-size: 0.95em;
}

[data-ui="v2"] .btn:hover {
    border-color: var(--accent-line);
    background: var(--accent-soft);
    color: var(--text);
}

[data-ui="v2"] .btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

[data-ui="v2"] .btn:disabled,
[data-ui="v2"] .btn[disabled],
[data-ui="v2"] .btn.is-disabled {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
}

/* Primary — exactly one per view. */
[data-ui="v2"] .btn--primary {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
    color: var(--on-accent);
}

[data-ui="v2"] .btn--primary:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

/* Secondary is the bare .btn. Ghost is for repeated row-level actions. */
[data-ui="v2"] .btn--ghost {
    background: transparent;
    border-color: transparent;
    color: var(--text-muted);
}

[data-ui="v2"] .btn--ghost:hover {
    background: var(--neutral-soft);
    border-color: var(--surface-border);
    color: var(--text);
}

/* Destructive: outline at rest, fills only on hover. A Stop button should be
   findable, not dominant. */
[data-ui="v2"] .btn--danger {
    background: transparent;
    border-color: var(--danger-line);
    color: var(--danger);
}

[data-ui="v2"] .btn--danger:hover {
    background: var(--danger);
    border-color: var(--danger);
    color: var(--on-accent);
}

[data-ui="v2"] .btn--success {
    background: transparent;
    border-color: var(--success-line);
    color: var(--success);
}

[data-ui="v2"] .btn--success:hover {
    background: var(--success-soft);
    border-color: var(--success);
    color: var(--success);
}

[data-ui="v2"] .btn--warning {
    background: transparent;
    border-color: var(--warning-line);
    color: var(--warning);
}

[data-ui="v2"] .btn--warning:hover {
    background: var(--warning-soft);
    border-color: var(--warning);
    color: var(--warning);
}

/* Sizes */
[data-ui="v2"] .btn--sm {
    height: var(--control-h-sm);
    padding: 0 var(--space-3);
    font-size: var(--fs-sm);
    gap: var(--space-1);
}

[data-ui="v2"] .btn--lg {
    height: var(--control-h-lg);
    padding: 0 var(--space-5);
    font-size: var(--fs-md);
}

[data-ui="v2"] .btn--icon {
    width: var(--control-h);
    padding: 0;
}

[data-ui="v2"] .btn--icon.btn--sm {
    width: var(--control-h-sm);
}

[data-ui="v2"] .btn--icon.btn--lg {
    width: var(--control-h-lg);
}

[data-ui="v2"] .btn--block {
    width: 100%;
}

/* Loading */
[data-ui="v2"] .btn.is-loading {
    pointer-events: none;
    opacity: 0.75;
}

[data-ui="v2"] .btn.is-loading::before {
    content: '';
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 2px solid currentColor;
    border-top-color: transparent;
    animation: rise-spin 0.7s linear infinite;
}

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

@media (prefers-reduced-motion: reduce) {
    [data-ui="v2"] .btn.is-loading::before { animation-duration: 2s; }
    [data-ui="v2"] a.stat:hover { transform: none; }
}

/* Button group — segmented run of related actions. */
[data-ui="v2"] .btn-group {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* --- Legacy aliases -------------------------------------------------------
   base.html's markup uses .hud-btn / .btn-primary / .page-btn / .status-btn.
   Inside the v2 layer they all resolve to the new system, so a page can opt
   in without its buttons being rewritten in the same commit. New markup
   should use .btn--* only. */
[data-ui="v2"] .hud-btn,
[data-ui="v2"] .page-btn,
[data-ui="v2"] .status-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    height: var(--control-h);
    min-height: 0;
    padding: 0 var(--space-4);
    border: 1px solid var(--surface-border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-strong);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-base);
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0;
    text-transform: none;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    box-shadow: none;
    transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
        color 0.15s var(--ease);
}

[data-ui="v2"] .hud-btn:hover,
[data-ui="v2"] .page-btn:hover,
[data-ui="v2"] .status-btn:hover {
    transform: none;
    border-color: var(--accent-line);
    background: var(--accent-soft);
}

[data-ui="v2"] .hud-btn.primary,
[data-ui="v2"] .btn-primary {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
    color: var(--on-accent);
}

[data-ui="v2"] .hud-btn.primary:hover,
[data-ui="v2"] .btn-primary:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

[data-ui="v2"] .hud-btn.danger,
[data-ui="v2"] .btn-danger {
    background: transparent;
    border-color: var(--danger-line);
    color: var(--danger);
}

[data-ui="v2"] .hud-btn.danger:hover,
[data-ui="v2"] .btn-danger:hover {
    background: var(--danger);
    border-color: var(--danger);
    color: var(--on-accent);
}

[data-ui="v2"] .hud-btn.success,
[data-ui="v2"] .btn-success {
    background: transparent;
    border-color: var(--success-line);
    color: var(--success);
}

[data-ui="v2"] .hud-btn.success:hover,
[data-ui="v2"] .btn-success:hover {
    background: var(--success-soft);
    border-color: var(--success);
}

[data-ui="v2"] .hud-btn.warning,
[data-ui="v2"] .btn-warning {
    background: transparent;
    border-color: var(--warning-line);
    color: var(--warning);
}

[data-ui="v2"] .hud-btn.warning:hover,
[data-ui="v2"] .btn-warning:hover {
    background: var(--warning-soft);
    border-color: var(--warning);
}

[data-ui="v2"] .hud-btn.ghost,
[data-ui="v2"] .hud-btn.secondary,
[data-ui="v2"] .btn-secondary {
    background: transparent;
    border-color: transparent;
    color: var(--text-muted);
}

[data-ui="v2"] .hud-btn.ghost:hover,
[data-ui="v2"] .hud-btn.secondary:hover,
[data-ui="v2"] .btn-secondary:hover {
    background: var(--neutral-soft);
    border-color: var(--surface-border);
    color: var(--text);
}

[data-ui="v2"] .hud-btn-sm {
    width: var(--control-h-sm);
    height: var(--control-h-sm);
    padding: 0;
    border-radius: var(--radius-xs);
}

/* ── Tabs ────────────────────────────────────────────────────────────────────
   One implementation, replacing the hand-copied .vision-tabs and .det-tabs.
   Supports labelled groups so a section subnav can separate "the pages of
   this thing" from "the modules that hang off it". */
[data-ui="v2"] .rise-tabs {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    flex-wrap: wrap;
    padding: var(--space-1);
    background: var(--surface-soft);
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-md);
}

[data-ui="v2"] .rise-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    height: var(--control-h);
    padding: 0 var(--space-3);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-muted);
    font-size: var(--fs-base);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.15s var(--ease), color 0.15s var(--ease),
        border-color 0.15s var(--ease);
}

[data-ui="v2"] .rise-tab i {
    font-size: 0.9em;
    opacity: 0.85;
}

[data-ui="v2"] .rise-tab:hover {
    background: var(--neutral-soft);
    color: var(--text);
}

[data-ui="v2"] .rise-tab.is-active {
    background: var(--accent-soft);
    border-color: var(--accent-line);
    color: var(--accent);
}

[data-ui="v2"] .rise-tab:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

[data-ui="v2"] .rise-tabs__label {
    padding: 0 var(--space-2) 0 var(--space-3);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-soft);
    user-select: none;
}

[data-ui="v2"] .rise-tabs__divider {
    width: 1px;
    align-self: stretch;
    margin: var(--space-1) var(--space-2);
    background: var(--surface-border-strong);
}

@media (max-width: 720px) {
    [data-ui="v2"] .rise-tabs__divider { display: none; }
    [data-ui="v2"] .rise-tabs__label { width: 100%; padding: var(--space-2) var(--space-3) 0; }
}

/* ── Toolbar — the filter/action strip above a table or console ──────────── */
[data-ui="v2"] .toolbar {
    display: flex;
    align-items: flex-end;
    gap: var(--space-4);
    flex-wrap: wrap;
    padding: var(--space-4) var(--space-5);
    background: var(--surface-soft);
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-md);
}

[data-ui="v2"] .toolbar__group {
    display: flex;
    align-items: flex-end;
    gap: var(--space-3);
    flex-wrap: wrap;
}

[data-ui="v2"] .toolbar__spacer {
    flex: 1 1 var(--space-4);
}

[data-ui="v2"] .toolbar__rule {
    width: 1px;
    align-self: stretch;
    background: var(--surface-border);
}

/* ── Form fields ─────────────────────────────────────────────────────────────
   base.html ships only .form-field (label/input/select). Everything else —
   textarea, checkbox, radio, switch, help text, error text — was reinvented
   per page. */
[data-ui="v2"] .field {
    display: grid;
    gap: var(--space-2);
    align-content: start;
    min-width: 0;
}

[data-ui="v2"] .field > label,
[data-ui="v2"] .field__label {
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-soft);
}

[data-ui="v2"] .field input[type="text"],
[data-ui="v2"] .field input[type="search"],
[data-ui="v2"] .field input[type="number"],
[data-ui="v2"] .field input[type="password"],
[data-ui="v2"] .field input[type="email"],
[data-ui="v2"] .field select,
[data-ui="v2"] .field textarea,
[data-ui="v2"] .input {
    width: 100%;
    min-height: var(--control-h);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--surface-border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-strong);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-base);
    line-height: var(--lh-base);
    transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

[data-ui="v2"] .field textarea {
    min-height: calc(var(--control-h) * 2.4);
    resize: vertical;
    line-height: var(--lh-base);
}

[data-ui="v2"] .field input::placeholder,
[data-ui="v2"] .field textarea::placeholder,
[data-ui="v2"] .input::placeholder {
    color: var(--text-soft);
    opacity: 0.75;
}

[data-ui="v2"] .field input:focus,
[data-ui="v2"] .field select:focus,
[data-ui="v2"] .field textarea:focus,
[data-ui="v2"] .input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* ── Searchable combobox ────────────────────────────────────────────────────
   Native selects hand long libraries to the OS as an unsearchable sheet.
   This component keeps the collection inside the RISE shell and composes the
   existing input, pill and empty-state vocabulary into a searchable listbox. */
[data-ui="v2"] .combobox {
    position: relative;
    min-width: 260px;
}

[data-ui="v2"] .combobox__trigger {
    width: 100%;
    height: var(--control-h);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: 0 var(--space-3);
    border: 1px solid var(--surface-border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-strong);
    color: var(--text);
    font: inherit;
    font-size: var(--fs-base);
    cursor: pointer;
    text-align: left;
}

[data-ui="v2"] .combobox__trigger:hover:not(:disabled),
[data-ui="v2"] .combobox__trigger[aria-expanded="true"] {
    border-color: var(--accent);
    background: var(--accent-soft);
}

[data-ui="v2"] .combobox__trigger:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

[data-ui="v2"] .combobox__trigger:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

[data-ui="v2"] .combobox__value {
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

[data-ui="v2"] .combobox__value > i {
    color: var(--accent);
    flex: none;
}

[data-ui="v2"] .combobox__value > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

[data-ui="v2"] .combobox__chevron {
    color: var(--text-soft);
    font-size: var(--fs-xs);
    transition: transform 0.15s var(--ease);
}

[data-ui="v2"] .combobox__trigger[aria-expanded="true"] .combobox__chevron {
    transform: rotate(180deg);
}

[data-ui="v2"] .combobox__panel {
    position: absolute;
    top: calc(100% + var(--space-1));
    left: 0;
    z-index: var(--z-dropdown);
    width: clamp(340px, 48vw, 620px);
    max-width: calc(100vw - (var(--space-4) * 2));
    overflow: hidden;
    border: 1px solid var(--surface-border-strong);
    border-radius: var(--radius-md);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
}

[data-ui="v2"] .combobox__search-wrap {
    position: relative;
    padding: var(--space-3);
    border-bottom: 1px solid var(--surface-border);
}

[data-ui="v2"] .combobox__search-wrap > i {
    position: absolute;
    top: 50%;
    left: calc(var(--space-3) * 2);
    z-index: 1;
    color: var(--text-soft);
    transform: translateY(-50%);
    pointer-events: none;
}

[data-ui="v2"] .combobox__search {
    padding-left: calc(var(--space-7) + var(--space-1));
}

[data-ui="v2"] .combobox__list {
    display: grid;
    gap: var(--space-1);
    max-height: min(420px, calc(100vh - 190px));
    overflow: auto;
    overscroll-behavior: contain;
    padding: var(--space-2);
}

[data-ui="v2"] .combobox__option {
    width: 100%;
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

[data-ui="v2"] .combobox__option:hover,
[data-ui="v2"] .combobox__option:focus-visible {
    outline: none;
    border-color: var(--accent-line);
    background: var(--row-hover);
}

[data-ui="v2"] .combobox__option.is-selected {
    border-color: var(--accent-line);
    background: var(--accent-soft);
}

[data-ui="v2"] .combobox__option-icon {
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: var(--radius-xs);
    background: var(--neutral-soft);
    color: var(--accent);
}

[data-ui="v2"] .combobox__option-copy {
    min-width: 0;
    display: grid;
    gap: 2px;
}

[data-ui="v2"] .combobox__option-copy strong,
[data-ui="v2"] .combobox__option-copy > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

[data-ui="v2"] .combobox__option-copy strong {
    font-size: var(--fs-base);
    font-weight: 600;
}

[data-ui="v2"] .combobox__option-copy > span {
    color: var(--text-soft);
    font-size: var(--fs-xs);
}

[data-ui="v2"] .combobox__empty {
    display: grid;
    justify-items: center;
    gap: var(--space-2);
    padding: var(--space-6) var(--space-4);
    color: var(--text-soft);
    text-align: center;
    font-size: var(--fs-sm);
}

[data-ui="v2"] .combobox__empty > i {
    color: var(--accent);
    font-size: var(--fs-lg);
}

[data-ui="v2"] .combobox__empty strong {
    color: var(--text);
    font-size: var(--fs-base);
}

[data-ui="v2"] .combobox__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--surface-border);
    color: var(--text-soft);
    font-size: var(--fs-xs);
}

/* The combobox order bar. Sits between the search and the list so the operator
   sets the order where they read it, not in a settings page. */
[data-ui="v2"] .combobox__sortbar {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3) var(--space-2);
    border-bottom: 1px solid var(--surface-border);
    flex-wrap: wrap;
}

[data-ui="v2"] .combobox__sortbar-label {
    color: var(--text-soft);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-right: var(--space-1);
}

[data-ui="v2"] .combobox__sort,
[data-ui="v2"] .combobox__sort-dir {
    background: transparent;
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-sm);
    color: var(--text-soft);
    font-size: var(--fs-xs);
    padding: 2px var(--space-2);
    cursor: pointer;
}

[data-ui="v2"] .combobox__sort:hover,
[data-ui="v2"] .combobox__sort-dir:hover { color: var(--text); }

[data-ui="v2"] .combobox__sort.is-active {
    color: var(--accent);
    border-color: var(--accent);
}

[data-ui="v2"] .combobox--static .combobox__panel {
    position: static;
    width: 100%;
    margin-top: var(--space-1);
    box-shadow: none;
}

@media (max-width: 640px) {
    [data-ui="v2"] .combobox { min-width: 100%; }
    [data-ui="v2"] .combobox__panel {
        width: calc(100vw - (var(--space-4) * 2));
    }
    [data-ui="v2"] .combobox__foot > span:last-child { display: none; }
}

[data-ui="v2"] .field--invalid input,
[data-ui="v2"] .field--invalid select,
[data-ui="v2"] .field--invalid textarea {
    border-color: var(--danger-line);
}

[data-ui="v2"] .field__help {
    font-size: var(--fs-xs);
    line-height: var(--lh-base);
    color: var(--text-soft);
}

[data-ui="v2"] .field__error {
    font-size: var(--fs-xs);
    line-height: var(--lh-base);
    color: var(--danger);
}

[data-ui="v2"] .field--inline {
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: var(--space-3);
}

/* Fixed-width numeric field — stops a "limit: 50" input spanning the row. */
[data-ui="v2"] .field--narrow input,
[data-ui="v2"] .field--narrow select {
    width: 110px;
}

/* Checkbox / radio */
[data-ui="v2"] .check {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--fs-base);
    color: var(--text-muted);
    cursor: pointer;
    user-select: none;
    min-height: var(--control-h-sm);
}

[data-ui="v2"] .check input[type="checkbox"],
[data-ui="v2"] .check input[type="radio"] {
    width: 15px;
    height: 15px;
    margin: 0;
    accent-color: var(--accent-strong);
    cursor: pointer;
    flex: none;
}

[data-ui="v2"] .check:hover {
    color: var(--text);
}

[data-ui="v2"] .check-row {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* Switch */
[data-ui="v2"] .switch {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    cursor: pointer;
    font-size: var(--fs-base);
    color: var(--text-muted);
}

[data-ui="v2"] .switch input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

[data-ui="v2"] .switch__track {
    position: relative;
    width: 38px;
    height: 21px;
    flex: none;
    border-radius: 999px;
    background: var(--neutral-soft);
    border: 1px solid var(--surface-border-strong);
    transition: background 0.15s var(--ease), border-color 0.15s var(--ease);
}

[data-ui="v2"] .switch__track::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: var(--text-muted);
    transition: transform 0.15s var(--ease), background 0.15s var(--ease);
}

[data-ui="v2"] .switch input:checked + .switch__track {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
}

[data-ui="v2"] .switch input:checked + .switch__track::after {
    transform: translateX(17px);
    background: var(--on-accent);
}

[data-ui="v2"] .switch input:focus-visible + .switch__track {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ── Tables ──────────────────────────────────────────────────────────────────
   base.html has no table rules at all — only .data-table-shell, a scroll
   container. Every page therefore styled its own. This replaces .fg-table /
   .fg-controls / .fg-pager and the per-page equivalents. */
[data-ui="v2"] .rise-table-wrap {
    width: 100%;
    overflow: auto;
    border-radius: var(--radius-md);
    -webkit-overflow-scrolling: touch;
}

[data-ui="v2"] .panel--flush .rise-table-wrap {
    border-radius: 0 0 var(--radius-md) var(--radius-md);
}

[data-ui="v2"] .rise-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-base);
}

[data-ui="v2"] .rise-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: var(--space-3) var(--space-4);
    background: var(--surface-strong);
    border-bottom: 1px solid var(--surface-border-strong);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-soft);
    text-align: left;
    white-space: nowrap;
}

[data-ui="v2"] .rise-table td {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--surface-border);
    color: var(--text-muted);
    vertical-align: middle;
}

[data-ui="v2"] .rise-table tbody tr:last-child td {
    border-bottom: none;
}

[data-ui="v2"] .rise-table tbody tr:hover td {
    background: var(--row-hover);
    color: var(--text);
}

[data-ui="v2"] .rise-table tbody tr.is-selected td {
    background: var(--accent-soft);
    color: var(--text);
}

/* Numeric columns: right-aligned and tabular, so digits line up and a live
   value does not reflow the column as it ticks. */
[data-ui="v2"] .rise-table .num,
[data-ui="v2"] .rise-table [data-num] {
    text-align: right;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
    white-space: nowrap;
}

[data-ui="v2"] .rise-table th.num,
[data-ui="v2"] .rise-table th[data-num] {
    text-align: right;
}

[data-ui="v2"] .rise-table .strong {
    color: var(--text);
    font-weight: 600;
}

[data-ui="v2"] .rise-table--compact th,
[data-ui="v2"] .rise-table--compact td {
    padding: var(--space-2) var(--space-3);
}

[data-ui="v2"] .rise-table caption {
    caption-side: bottom;
    padding: var(--space-3) var(--space-4);
    font-size: var(--fs-xs);
    color: var(--text-soft);
    text-align: left;
}

/* Pager */
[data-ui="v2"] .pager {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    font-size: var(--fs-sm);
    color: var(--text-soft);
}

[data-ui="v2"] .pager__count {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

/* ── Status pills ────────────────────────────────────────────────────────────
   One vocabulary, replacing four: base --success/--warning/--danger,
   live.css --ok/--wait/--warn/--bad, autolabel.css --stage-*, and the hexes
   csv_status_patch.js injects from JS. */
[data-ui="v2"] .pill {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 3px var(--space-3);
    border: 1px solid var(--surface-border-strong);
    border-radius: 999px;
    background: var(--neutral-soft);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: 0.02em;
    white-space: nowrap;
    line-height: 1.6;
}

[data-ui="v2"] .pill__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

[data-ui="v2"] .pill--ok {
    background: var(--success-soft);
    border-color: var(--success-line);
    color: var(--success);
}

[data-ui="v2"] .pill--warn {
    background: var(--warning-soft);
    border-color: var(--warning-line);
    color: var(--warning);
}

[data-ui="v2"] .pill--err {
    background: var(--danger-soft);
    border-color: var(--danger-line);
    color: var(--danger);
}

[data-ui="v2"] .pill--info {
    background: var(--accent-soft);
    border-color: var(--accent-line);
    color: var(--accent);
}

[data-ui="v2"] .pill--live .pill__dot {
    animation: rise-pulse 1.6s ease-in-out infinite;
}

@keyframes rise-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.25; }
}

@media (prefers-reduced-motion: reduce) {
    [data-ui="v2"] .pill--live .pill__dot { animation: none; }
}

/* ── Progress ────────────────────────────────────────────────────────────── */
[data-ui="v2"] .progress {
    height: 6px;
    width: 100%;
    border-radius: 999px;
    background: var(--neutral-soft);
    overflow: hidden;
}

[data-ui="v2"] .progress__fill {
    height: 100%;
    width: 0;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--accent-strong), var(--accent));
    transition: width 0.4s var(--ease);
}

[data-ui="v2"] .progress--idle .progress__fill {
    background: var(--text-soft);
}

/* ── Console / log tail ──────────────────────────────────────────────────────
   Logs are WIDE. Giving one a 1fr column beside a 340px sidebar — which is
   what the Vision index did — wastes most of every line. Consoles should own
   the full content width. */
[data-ui="v2"] .console {
    margin: 0;
    padding: var(--space-4);
    max-height: 420px;
    overflow: auto;
    background: var(--console-bg);
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-sm);
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-xs);
    line-height: 1.6;
    color: var(--text-muted);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    -webkit-overflow-scrolling: touch;
}

[data-ui="v2"] .panel--flush .console {
    border: none;
    border-radius: 0 0 var(--radius-md) var(--radius-md);
    max-height: 520px;
}

[data-ui="v2"] .console--tall { max-height: 620px; }
[data-ui="v2"] .console--short { max-height: 220px; }

[data-ui="v2"] .console__line { display: block; }
[data-ui="v2"] .console__line--ok { color: var(--success); }
[data-ui="v2"] .console__line--warn { color: var(--warning); }
[data-ui="v2"] .console__line--err { color: var(--danger); }
[data-ui="v2"] .console__line--info { color: var(--accent); }
[data-ui="v2"] .console__line--dim { color: var(--text-soft); }

/* ── Chips (tag clouds: top labels, semantic tags) ───────────────────────── */
[data-ui="v2"] .chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

[data-ui="v2"] .chip-tag {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-sm);
    background: var(--surface-soft);
    color: var(--text-muted);
    font-size: var(--fs-sm);
    text-decoration: none;
    white-space: nowrap;
}

[data-ui="v2"] a.chip-tag:hover {
    border-color: var(--accent-line);
    background: var(--accent-soft);
    color: var(--text);
}

[data-ui="v2"] .chip-tag__count {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-xs);
    color: var(--text-soft);
}

/* ── Key/value readout ───────────────────────────────────────────────────── */
[data-ui="v2"] .kv {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-2) var(--space-4);
    font-size: var(--fs-base);
    align-items: baseline;
}

[data-ui="v2"] .kv__k {
    color: var(--text-soft);
    white-space: nowrap;
}

[data-ui="v2"] .kv__v {
    color: var(--text);
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

/* ── Empty state ─────────────────────────────────────────────────────────── */
[data-ui="v2"] .empty-state {
    display: grid;
    justify-items: center;
    gap: var(--space-3);
    padding: var(--space-8) var(--space-5);
    text-align: center;
    color: var(--text-soft);
}

/* Direct child only — a bare `.empty-state i` also caught the icon inside an
   empty-state's action button and blew it up to 26px at 40% opacity. */
[data-ui="v2"] .empty-state > i {
    font-size: 26px;
    opacity: 0.4;
}

[data-ui="v2"] .empty-state__title {
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--text-muted);
}

[data-ui="v2"] .empty-state__text {
    font-size: var(--fs-base);
    line-height: var(--lh-base);
    max-width: 46ch;
}

/* ── Dashboard layout ────────────────────────────────────────────────────────
   The Vision index stacked everything into an unstyled
   <div style="display:grid;gap:14px"> beside a fixed 340px rail, so there was
   nowhere to hang responsive rules and no visual rhythm. These are the
   regions a page composes instead. */
[data-ui="v2"] .dash {
    display: grid;
    /* ⛔ AN IMPLICIT GRID TRACK IS SIZED `auto`, WHICH RESOLVES TO MAX-CONTENT.
       `min-width: 0` on the container does NOT constrain the TRACK, so one wide
       toolbar inside stretched this column past the viewport and the whole PAGE
       scrolled sideways — measured 244px over at 900px wide. `minmax(0, 1fr)` is
       the grid form of the same shrink permission `min-width: 0` gives a flex
       item; `.dash-row--*` below already declare their columns this way. */
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-5);
    align-content: start;
    min-width: 0;
}

[data-ui="v2"] .dash-row {
    display: grid;
    gap: var(--space-4);
    align-items: start;
    min-width: 0;
}

/* Primary + secondary: a live panel next to a summary. */
[data-ui="v2"] .dash-row--primary {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
}

[data-ui="v2"] .dash-row--even {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

[data-ui="v2"] .dash-row--thirds {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* Sidebar rail — for the rare page that genuinely needs one. Controls that
   drive a full-width console belong in a .toolbar instead. */
[data-ui="v2"] .dash-row--rail {
    grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
}

@media (max-width: 1180px) {
    [data-ui="v2"] .dash-row--primary,
    [data-ui="v2"] .dash-row--thirds,
    [data-ui="v2"] .dash-row--rail {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 860px) {
    [data-ui="v2"] .dash-row--even {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 640px) {
    [data-ui="v2"] .page-header {
        align-items: flex-start;
    }
    [data-ui="v2"] .page-header__actions .btn {
        flex: 1 1 auto;
    }
    [data-ui="v2"] .panel__body,
    [data-ui="v2"] .toolbar {
        padding: var(--space-4);
    }
    [data-ui="v2"] .panel__head {
        padding: var(--space-3) var(--space-4);
    }
    [data-ui="v2"] .stat {
        padding: var(--space-3) var(--space-4);
    }
    [data-ui="v2"] .stat__value {
        font-size: var(--fs-xl);
    }
}

/* ── Utilities ───────────────────────────────────────────────────────────── */
[data-ui="v2"] .u-mono {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}
[data-ui="v2"] .u-muted { color: var(--text-muted); }
[data-ui="v2"] .u-soft { color: var(--text-soft); }
[data-ui="v2"] .u-ok { color: var(--success); }
[data-ui="v2"] .u-warn { color: var(--warning); }
[data-ui="v2"] .u-err { color: var(--danger); }
[data-ui="v2"] .u-accent { color: var(--accent); }
[data-ui="v2"] .u-truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
[data-ui="v2"] .u-hidden { display: none !important; }
[data-ui="v2"] .u-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}
[data-ui="v2"] .u-stack {
    display: grid;
    gap: var(--space-3);
    align-content: start;
}
