/* ──────────────────────────────────────────────────────────────────────────
   Gladius app.css
   Phase 0 of ux-overhaul-2026.
   Tokens, base layer, animations, accessibility helpers.
   This file is loaded after the Tailwind CDN runtime config in base.html
   so anything here can override Tailwind defaults.
   ────────────────────────────────────────────────────────────────────────── */

/* ── Design tokens ───────────────────────────────────────────────────────── */
:root {
    /* Brand (kept in sync with components/brand_config.html) */
    --brand-50:  #e8eef6;
    --brand-200: #8a9ab3;
    --brand-400: #2d4f75;
    --brand-500: #13294B;
    --brand-600: #0f2240;
    --accent-500: #06b6d4;

    /* Categorical palette — used for entity colors AND chart series.
       Aligned with the .entity-* left-border colors so blue always
       means platforms, amber always means companies, etc. */
    --cat-1: #3b82f6;  /* platforms (blue) */
    --cat-2: #f59e0b;  /* companies (amber) */
    --cat-3: #10b981;  /* facilities (emerald) */
    --cat-4: #8b5cf6;  /* components (purple) */
    --cat-5: #06b6d4;  /* locations (cyan) */
    --cat-6: #ec4899;  /* spare 6th series (pink) */

    /* Sequential palette (heatmaps, time-series intensity) */
    --seq-1: #e0e7ff;
    --seq-2: #a5b4fc;
    --seq-3: #6366f1;
    --seq-4: #4338ca;
    --seq-5: #312e81;

    /* Surfaces — page sits darker so cards have somewhere to "sit on" */
    --surface-0: #f4f5f7;   /* page bg */
    --surface-1: #ffffff;   /* card / table */
    --surface-2: #ffffff;   /* popover (uses ring) */
    --surface-3: #ffffff;   /* modal (uses shadow) */
    --surface-hover: #f0fbfd;  /* brand-50 at ~50% — gives table rows a subtle cyan wash on hover instead of dead gray */

    /* Borders */
    --border-subtle:  #eceef2;
    --border-default: #e2e5ea;
    --border-strong:  #c9ced6;

    /* Shadows / rings */
    --ring-popover: 0 0 0 1px rgba(15, 23, 42, 0.06);

    /* Z-index scale (replaces ad-hoc z-9999) */
    --z-content:  10;
    --z-sticky:   20;
    --z-dropdown: 30;
    --z-modal:    50;
    --z-tooltip:  100;

    /* Motion */
    --transition-fast:   150ms;
    --transition-normal: 200ms;
    --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Color-blind safe palette (Okabe-Ito). Toggle via Settings → Accessibility.
   The body gets .cb-palette which redefines the --cat-* variables; chart
   theme JS reads them at runtime via getComputedStyle, so charts and the
   .entity-* left borders pick up the change automatically. */
body.cb-palette {
    --cat-1: #56B4E9;  /* sky blue        — platforms */
    --cat-2: #E69F00;  /* orange          — companies */
    --cat-3: #009E73;  /* bluish green    — facilities */
    --cat-4: #CC79A7;  /* reddish purple  — components */
    --cat-5: #0072B2;  /* blue            — locations */
    --cat-6: #D55E00;  /* vermillion      — spare 6th series */
}
body.cb-palette .entity-platform     { border-left-color: #56B4E9; }
body.cb-palette .entity-company      { border-left-color: #E69F00; }
body.cb-palette .entity-facility     { border-left-color: #009E73; }
body.cb-palette .entity-component    { border-left-color: #CC79A7; }
body.cb-palette .entity-subcomponent { border-left-color: #d8a3c4; }
body.cb-palette .entity-location     { border-left-color: #0072B2; }

/* ── Page background (slightly darker than current #f8f9fb) ─────────────── */
body {
    background-color: var(--surface-0);
}

/* ── Typography rhythm ──────────────────────────────────────────────────── */
/* Inter handles all weights now that Rajdhani is gone. */
h1 { line-height: 1.15; letter-spacing: -0.011em; }
h2 { line-height: 1.2;  letter-spacing: -0.005em; }
h3 { line-height: 1.25; }

h1 { font-weight: 700; }
h2, h3 { font-weight: 600; }
dt, label { font-weight: 500; }

p, dd, li, .text-sm, .text-base { line-height: 1.625; }
.prose-body { font-size: 1rem; line-height: 1.625; max-width: 65ch; }

/* ── Skip link ──────────────────────────────────────────────────────────── */
.skip-to-content {
    position: absolute;
    left: -9999px;
    top: 8px;
    z-index: var(--z-tooltip);
    background: var(--surface-1);
    color: var(--brand-500);
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
    border: 1px solid var(--border-default);
}
.skip-to-content:focus {
    left: 8px;
}

/* ── Z-index overrides for the existing sidebar tooltip / flyout ───────── */
.sidebar-tooltip,
.sidebar-fav-flyout-portal {
    z-index: var(--z-tooltip);
}

/* ── Table — Phase 1 ───────────────────────────────────────────────────── */

.data-table tbody tr {
    transition: background 100ms ease, box-shadow 100ms ease;
}
.data-table tbody tr:hover {
    background: var(--surface-hover);
    box-shadow: inset 2px 0 0 0 var(--brand-500);
}
.data-table tbody tr.row-link {
    cursor: pointer;
}
/* Whole-row click via the first-column anchor's ::before overlay.
   Real <a href> means free keyboard nav, middle-click, right-click, etc. */
.data-table tbody tr.row-link td {
    position: relative;
}
.data-table tbody tr.row-link td:first-child .row-link-anchor::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
}
.data-table tbody tr.row-link td a {
    position: relative;
    z-index: 2;
}

/* Sorted-column header gets a subtle bg so the eye finds it immediately. */
.data-table thead th.is-sorted {
    background: rgba(19, 41, 75, 0.04);
}

/* Column resize handles — 8px hit area on the right edge of each th
   with a 1px visible divider line so the user can SEE that columns
   are resizable. The line uses ::before so the actual hit area stays
   wider than the visible affordance.
   The last column's handle is hidden — resizing the last column would
   either overflow the table or shrink other columns unpredictably. */
.data-table thead th {
    position: relative;
}
.data-table thead th:last-child .col-resize-handle {
    display: none;
}
.data-table .col-resize-handle {
    position: absolute;
    top: 25%;
    right: -4px;
    width: 8px;
    height: 50%;
    cursor: col-resize;
    user-select: none;
    z-index: 5;
}
.data-table .col-resize-handle::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 0;
    width: 1px;
    margin-left: -0.5px;
    background: rgba(19, 41, 75, 0.18);
    transition: background 100ms ease, width 100ms ease, margin-left 100ms ease;
}
.data-table .col-resize-handle:hover::before,
.data-table .col-resize-handle.is-dragging::before {
    background: var(--brand-500);
    width: 2px;
    margin-left: -1px;
}
body.col-resizing,
body.col-resizing * {
    cursor: col-resize !important;
    user-select: none !important;
}

/* Sticky first column (opt-in via data-sticky-first="true").
   Used for wide tables (e.g. components). */
.data-table[data-sticky-first="true"] thead th:first-child,
.data-table[data-sticky-first="true"] tbody td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: inherit;
}
.data-table[data-sticky-first="true"] thead th:first-child {
    background: #f9fafb;
}
.data-table[data-sticky-first="true"] tbody tr:hover td:first-child {
    background: var(--surface-hover);
}

/* ── Density modes (set via [data-density] on the <table>) ─────────────── */
/* Default = compact, py-2 (~36px row). */
.data-table[data-density="comfortable"] tbody td { padding-top: 12px; padding-bottom: 12px; }
.data-table[data-density="comfortable"] thead th { padding-top: 14px; padding-bottom: 14px; }
.data-table[data-density="spacious"] tbody td { padding-top: 16px; padding-bottom: 16px; }
.data-table[data-density="spacious"] thead th { padding-top: 18px; padding-bottom: 18px; }

/* Density toolbar buttons — active state */
.density-btn[aria-pressed="true"] {
    background: var(--surface-1);
    color: var(--brand-500);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}

/* ── Skeleton (kept from base.html, moved here) ────────────────────────── */
.skeleton {
    background: linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s ease-in-out infinite;
    border-radius: 4px;
}
@keyframes shimmer {
    0%   { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

/* ── Hover lift (kept) ─────────────────────────────────────────────────── */
.hover-lift {
    transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1),
                box-shadow 200ms ease;
}
.hover-lift:hover {
    transform: translateY(-3px);
    box-shadow: 0 4px 12px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.04);
}

/* ── Page enter (kept — subtle and functional) ─────────────────────────── */
@keyframes page-enter {
    0%   { opacity: 0; transform: translateY(8px); }
    100% { opacity: 1; transform: translateY(0); }
}
.animate-page-enter { animation: page-enter 0.3s ease-out both; }

/* ── Risk dot — leading marker on list rows with a risk classification ── */
.risk-dot {
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 9999px;
    flex-shrink: 0;
}

/* ── Inline supplier tier pill ──────────────────────────────────────────
   Used in components/table_tier_pill.html. Color modifiers (bg-*-50,
   text-*-700) live on the markup; this base class just sets the
   typography + shape so every variant looks identical. */
.tier-pill {
    display: inline-block;
    padding: 0 0.4rem;
    border-radius: 0.25rem;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.05em;
    line-height: 1.4;
    text-transform: uppercase;
    flex-shrink: 0;
}

/* ── SCRA risk-level legend swatches ─────────────────────────────────────
   Used by the SCRA score table legend on platform_detail and
   component_detail. Hex colors live here so the legend, the table-row
   coloring, and any future colorblind palette can drift in lockstep
   from one place. */
.scra-swatch {
    display: inline-block;
    width: 2rem;
    height: 0.75rem;
    border-radius: 0.25rem;
}
.scra-swatch--good     { background: #10b981; }
.scra-swatch--moderate { background: #f59e0b; }
.scra-swatch--high     { background: #ef4444; }
.scra-swatch--critical { background: #991b1b; }

/* ── Confidence badges (kept) ──────────────────────────────────────────── */
.badge-high       { background: #ecfdf5; color: #047857; border: 1px solid #a7f3d0; }
.badge-medium     { background: #fffbeb; color: #b45309; border: 1px solid #fde68a; }
.badge-low        { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }
.badge-projected  { background: #eff6ff; color: #1d4ed8; border: 1px solid #bfdbfe; }
.badge-confidence {
    display: inline-flex;
    align-items: center;
    font-size: 11px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 9999px;
    text-transform: uppercase;
    letter-spacing: 0.025em;
}

/* ── Button loading state (kept) ───────────────────────────────────────── */
button:disabled,
.btn-loading {
    opacity: 0.6;
    cursor: not-allowed;
    pointer-events: none;
}
.btn-loading::after {
    content: '';
    display: inline-block;
    width: 14px;
    height: 14px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
    margin-left: 6px;
    vertical-align: middle;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Sort completion flash (kept) ──────────────────────────────────────── */
@keyframes row-flash {
    0%   { background-color: rgba(19, 41, 75, 0.06); }
    100% { background-color: transparent; }
}
.row-flash { animation: row-flash 0.6s ease-out; }

/* ── Entity-type left border accents (kept) ────────────────────────────── */
.entity-platform     { border-left: 2px solid #3b82f6; }
.entity-company      { border-left: 2px solid #f59e0b; }
.entity-facility     { border-left: 2px solid #10b981; }
.entity-component    { border-left: 2px solid #8b5cf6; }
.entity-subcomponent { border-left: 2px solid #a78bfa; }
.entity-location     { border-left: 2px solid #06b6d4; }

/* ── Sidebar surface (Phase 2: solid, not gradient) ───────────────────── */
.sidebar-surface {
    background: #0f1623;
}

/* ── Phase 7: responsive sidebar drawer (below lg / 1024px) ───────────── */
/* Above lg the sidebar is a permanent column. Below lg it's a slide-over
   drawer triggered by the header hamburger. Tables stay tables (they
   scroll horizontally inside their existing overflow-x-auto wrapper). */
.sidebar-drawer-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
    z-index: 25;
    opacity: 0;
    pointer-events: none;
    transition: opacity 180ms ease-in-out;
}
.sidebar-drawer-open .sidebar-drawer-backdrop {
    opacity: 1;
    pointer-events: auto;
}

@media (max-width: 1023px) {
    /* Sidebar slides in from the left, sits above the backdrop */
    #app-sidebar {
        transform: translateX(-100%);
        transition: transform 200ms ease-in-out;
        z-index: 30;
        width: 280px;
    }
    .sidebar-drawer-open #app-sidebar {
        transform: translateX(0);
    }
    /* Main content reclaims full width (no left margin for the column) */
    #app-main {
        margin-left: 0 !important;
    }
    /* Lock body scroll while the drawer is open so the underlying page
       doesn't scroll under the drawer */
    .sidebar-drawer-open body {
        overflow: hidden;
    }
    /* Hide the desktop "Collapse" button at the bottom of the sidebar
       since it's a no-op in drawer mode */
    #sidebar-toggle {
        display: none;
    }
    /* Drawer always shows full labels — collapsed mode is desktop-only */
    .sidebar-collapsed .sidebar-label,
    .sidebar-collapsed .sidebar-section-header,
    .sidebar-collapsed .sidebar-badge {
        opacity: 1;
        width: auto;
    }
    .sidebar-collapsed .sidebar-favorites,
    .sidebar-collapsed .sidebar-categories {
        opacity: 1;
        height: auto;
    }
}

@media (max-width: 767px) {
    /* Below md the drawer is full width */
    #app-sidebar {
        width: 100vw;
    }
}

/* ── Collapsible sidebar system ────────────────────────────────────────── */
#app-sidebar {
    width: 256px;
    transition: width 200ms ease-in-out;
    overflow-x: hidden;
}
.sidebar-collapsed #app-sidebar { width: 72px; }

#app-main {
    margin-left: 256px;
    transition: margin-left 200ms ease-in-out;
}
.sidebar-collapsed #app-main { margin-left: 72px; }

.sidebar-brand {
    padding: 24px;
    transition: padding 200ms ease-in-out;
}
.sidebar-collapsed .sidebar-brand {
    padding: 16px 8px;
    display: flex;
    justify-content: center;
}

/* Hide labels/headers/badges/groups when collapsed */
.sidebar-label,
.sidebar-section-header,
.sidebar-badge,
.sidebar-favorites,
.sidebar-categories {
    transition: opacity 150ms ease-in-out;
}
.sidebar-collapsed .sidebar-label,
.sidebar-collapsed .sidebar-section-header,
.sidebar-collapsed .sidebar-badge {
    opacity: 0;
    width: 0;
    overflow: hidden;
    white-space: nowrap;
    pointer-events: none;
}
/* Section headers must also collapse vertically — Tailwind's pt-4/pb-2
   on .sidebar-section-header would otherwise leave a 24px ghost gap
   above each section in collapsed mode. */
.sidebar-collapsed .sidebar-section-header {
    height: 0;
    padding: 0 !important;
    margin: 0 !important;
}
/* Zero out the badge's Tailwind padding too — width:0 alone leaves the
   px-1.5 padding (12px total) occupying horizontal space, which shifts
   the icon ~6px left of center on every entity nav item that has a
   badge. The Map item has no badge so it sat at true center; the others
   sat at center-6, making Map LOOK misaligned. With the padding zeroed
   the badge takes 0 layout space and every icon centers identically. */
.sidebar-collapsed .sidebar-badge {
    padding-left: 0 !important;
    padding-right: 0 !important;
    margin: 0 !important;
}
.sidebar-collapsed .sidebar-favorites,
.sidebar-collapsed .sidebar-categories {
    opacity: 0;
    height: 0;
    overflow: hidden;
    pointer-events: none;
    margin: 0;
    padding: 0;
}

/* Center nav items when collapsed.
   Zero the gap on BOTH the button itself and the inner span — some
   nav items wrap icon+label in an inner span (regular entity rows)
   and some have the icon as a direct flex child (collapsed favorites
   button). Both cases keep the icon centered identically. */
.sidebar-collapsed .sidebar-nav-item {
    justify-content: center !important;
    gap: 0 !important;
    padding-left: 0;
    padding-right: 0;
}
.sidebar-collapsed .sidebar-nav-item > span:first-child { gap: 0; }
.sidebar-collapsed .sidebar-nav-item .sidebar-icon { margin: 0; }

/* Hide nav scrollbar when collapsed */
.sidebar-collapsed #app-sidebar nav::-webkit-scrollbar { display: none; }
.sidebar-collapsed #app-sidebar nav { scrollbar-width: none; }

/* Hide left active pill when collapsed; the icon glow takes its place */
.sidebar-collapsed .sidebar-active-pill { display: none; }
.sidebar-collapsed .sidebar-nav-item.sidebar-nav-active .sidebar-icon {
    filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.4));
}

/* Toggle icon rotation */
.sidebar-collapsed #sidebar-toggle-icon { transform: rotate(180deg); }

/* Favorites: show correct version per state */
.sidebar-fav-collapsed { display: none; }
.sidebar-collapsed .sidebar-fav-collapsed { display: block; }
.sidebar-collapsed .sidebar-favorites { display: none; }

/* Sidebar tooltip (appended to body, escapes overflow) */
.sidebar-tooltip {
    position: fixed;
    background: #1e293b;
    color: #f1f5f9;
    font-size: 12px;
    font-weight: 500;
    padding: 6px 12px;
    border-radius: 8px;
    white-space: nowrap;
    pointer-events: none;
    z-index: var(--z-tooltip);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    letter-spacing: 0.01em;
    opacity: 0;
    transition: opacity 100ms ease-in-out;
}
.sidebar-tooltip.visible { opacity: 1; }
.sidebar-tooltip::before {
    content: '';
    position: absolute;
    right: 100%;
    top: 50%;
    transform: translateY(-50%);
    border: 5px solid transparent;
    border-right-color: #1e293b;
}

/* Sidebar favorites flyout (appended to body) */
/* pointer-events: none is load-bearing — without it, the invisible
   (opacity:0) flyout sits at top:0/left:0 of the viewport by default
   (because position:fixed with no top/left set) and silently captures
   clicks from elements behind it, including the sidebar brand logo.
   Symptom: clicking the logo navigates to your first favorite. Took
   forever to find. */
.sidebar-fav-flyout-portal {
    position: fixed;
    background: #1e293b;
    border-radius: 10px;
    padding: 8px 0;
    min-width: 220px;
    max-width: 280px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
    z-index: var(--z-tooltip);
    opacity: 0;
    pointer-events: none;
    transition: opacity 100ms ease-in-out;
}
.sidebar-fav-flyout-portal.visible {
    opacity: 1;
    pointer-events: auto;
}
.sidebar-fav-flyout-portal::before {
    content: '';
    position: absolute;
    right: 100%;
    top: 16px;
    border: 5px solid transparent;
    border-right-color: #1e293b;
}
.sidebar-fav-flyout-portal a {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 16px;
    font-size: 12px;
    font-weight: 500;
    color: #cbd5e1;
    text-decoration: none;
    transition: background 100ms, color 100ms;
}
.sidebar-fav-flyout-portal a:hover {
    background: rgba(255, 255, 255, 0.1);
    color: #ffffff;
}
.sidebar-fav-flyout-portal .flyout-header {
    padding: 6px 16px 8px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    margin-bottom: 4px;
}
.sidebar-fav-flyout-portal .flyout-header span {
    font-size: 10px;
    font-weight: 600;
    color: #8a9ab3;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}
.sidebar-fav-flyout-portal .flyout-empty {
    padding: 12px 16px;
    font-size: 11px;
    color: #64748b;
    font-style: italic;
}

/* ── Login page (extracted from registration/login.html) ─────────────── */
.login-hero-grid {
    background-image:
        linear-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px);
    background-size: 48px 48px;
}
@keyframes fade-up {
    0%   { opacity: 0; transform: translateY(10px); }
    100% { opacity: 1; transform: translateY(0); }
}
.animate-fade-up { animation: fade-up 0.45s ease-out forwards; }
.animate-delay-1 { animation-delay: 80ms; opacity: 0; }
.animate-delay-2 { animation-delay: 160ms; opacity: 0; }
.login-form-bg {
    background-image: radial-gradient(circle at 1px 1px, #13294B 1px, transparent 0);
    background-size: 32px 32px;
    opacity: 0.03;
}

/* ── Home page hero gradient (extracted from core/home.html) ──────────── */
.surface-hero-dark {
    background: linear-gradient(135deg,
        hsl(213, 60%, 14%) 0%,
        hsl(213, 50%, 22%) 50%,
        hsl(200, 60%, 28%) 100%);
}
.surface-hero-dark-pattern {
    background-image: radial-gradient(circle at 1px 1px, white 1px, transparent 0);
    background-size: 20px 20px;
}
.card-accent-bar {
    height: 2px;
    background: linear-gradient(90deg, #13294B, #06b6d4);
}

/* ── Detail page tab strip ─────────────────────────────────────────────── */
.tab-strip {
    border-bottom: 1px solid var(--border-default);
    margin-bottom: 24px;
    overflow-x: auto;
}
.tab-strip-list {
    display: flex;
    gap: 4px;
    padding: 0;
    margin: 0;
    list-style: none;
}
.tab-strip-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 10px 14px;
    margin-bottom: -1px;
    font-size: 13px;
    font-weight: 500;
    color: #6b7280;
    border-bottom: 2px solid transparent;
    text-decoration: none;
    transition: color 100ms ease, border-color 100ms ease;
    white-space: nowrap;
}
.tab-strip-item:hover {
    color: #111827;
}
.tab-strip-item.is-active {
    color: var(--brand-500);
    border-bottom-color: var(--brand-500);
    font-weight: 600;
}
.tab-strip-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    font-size: 10px;
    font-weight: 600;
    background: #f3f4f6;
    color: #6b7280;
}
.tab-strip-item.is-active .tab-strip-badge {
    background: var(--brand-50);
    color: var(--brand-500);
}

/* ── Command palette ───────────────────────────────────────────────────── */
#command-palette {
    z-index: var(--z-modal);
}
.command-palette-card {
    animation: cmd-palette-in 160ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes cmd-palette-in {
    0%   { opacity: 0; transform: translateY(-8px) scale(0.98); }
    100% { opacity: 1; transform: translateY(0) scale(1); }
}
.cmd-section-label {
    padding: 6px 16px 4px;
    font-size: 10px;
    font-weight: 600;
    color: #94a3b8;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.cmd-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 16px;
    cursor: pointer;
    transition: background 80ms ease;
    border-left: 2px solid transparent;
}
.cmd-item:hover {
    background: var(--surface-hover);
}
.cmd-item.is-active {
    background: var(--brand-50);
    border-left-color: var(--brand-500);
}
.cmd-item .cmd-icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    color: #64748b;
}
.cmd-item .cmd-label {
    flex: 1;
    font-size: 13px;
    font-weight: 500;
    color: #1f2937;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.cmd-item .cmd-type {
    font-size: 10px;
    font-weight: 600;
    color: #94a3b8;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    flex-shrink: 0;
}
.cmd-empty {
    padding: 32px 16px;
    text-align: center;
    color: #94a3b8;
    font-size: 13px;
}

/* Entity-color tints for command palette icons */
.cmd-icon-platform  { color: #3b82f6; }
.cmd-icon-company   { color: #f59e0b; }
.cmd-icon-component { color: #8b5cf6; }
.cmd-icon-facility  { color: #10b981; }
.cmd-icon-location  { color: #06b6d4; }
.cmd-icon-page      { color: var(--brand-500); }
.cmd-icon-home      { color: var(--brand-500); }

/* ── Hover-intent preview popover ──────────────────────────────────────── */
.preview-popover {
    position: fixed;
    z-index: var(--z-tooltip);
    width: 280px;
    max-width: calc(100vw - 24px);
    background: var(--surface-1);
    border: 1px solid var(--border-default);
    border-radius: 10px;
    box-shadow: 0 12px 28px rgba(15, 23, 42, 0.12),
                0 4px 8px rgba(15, 23, 42, 0.06);
    padding: 12px 14px;
    pointer-events: none;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 120ms ease, transform 120ms ease;
}
.preview-popover.is-visible {
    opacity: 1;
    transform: translateY(0);
}
.preview-popover-label {
    font-size: 13px;
    font-weight: 600;
    color: #111827;
    line-height: 1.3;
    margin: 0 0 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.preview-popover-subtitle {
    font-size: 11px;
    color: #6b7280;
    margin-bottom: 8px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.preview-popover-fields {
    display: grid;
    grid-template-columns: minmax(0, 80px) minmax(0, 1fr);
    gap: 4px 12px;
    border-top: 1px solid var(--border-subtle);
    padding-top: 8px;
}
.preview-popover-fields dt {
    font-size: 10px;
    font-weight: 600;
    color: #94a3b8;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.preview-popover-fields dd {
    font-size: 11px;
    color: #374151;
    margin: 0;
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.preview-popover-loading {
    font-size: 11px;
    color: #94a3b8;
    text-align: center;
    padding: 4px 0;
}

/* ── Optimistic favorite pulse ─────────────────────────────────────────── */
@keyframes fav-pulse {
    0%   { transform: scale(1);   }
    50%  { transform: scale(1.18); }
    100% { transform: scale(1);   }
}
.fav-pulse svg {
    animation: fav-pulse 250ms ease-out;
}
/* Explicit override on top of the global reduced-motion block — the
   favorite still flips visually (the SVG class swap is not animated),
   we just skip the scale-up. */
@media (prefers-reduced-motion: reduce) {
    .fav-pulse svg { animation: none; }
}

/* ── Toolbar buttons — Mr Porter style ───────────────────────────────────
   Inspired by mrporter.com's filter / sort bar: clean white buttons
   with a thin slate border, no drop shadow, subtle hover, and a
   distinctive "fused" Filter+Count button with an internal vertical
   separator. The aesthetic is luxury-minimal: confident but quiet.

   Three button shapes:

     .toolbar-btn          — base shape, used for stand-alone controls
     .toolbar-btn-fused    — Filter + result count combined into one
                              clickable surface with an internal divider
     .toolbar-btn-dropdown — adds a trailing chevron indicating that
                              clicking opens a popover

   States: rest / hover / open (popover visible). All transitions are
   subtle 150ms ease — no scale, no gradient, no inset shadow.
   ───────────────────────────────────────────────────────────────────── */
.toolbar-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 36px;
    padding: 0 16px;
    font-size: 12.5px;
    font-weight: 500;
    letter-spacing: 0.005em;
    color: #1f2937; /* slate-800-ish, near-black for confidence */
    background: #ffffff;
    border: 1px solid #e2e8f0; /* slate-200 */
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
    transition: color 150ms ease,
                background 150ms ease,
                border-color 150ms ease;
}
.toolbar-btn:hover {
    color: #0f172a; /* slate-900 */
    background: #fafbfc;
    border-color: #94a3b8; /* slate-400 */
}
.toolbar-btn[aria-expanded="true"] {
    color: var(--brand-700, #0f1f3a);
    background: #ffffff;
    border-color: var(--brand-500);
    box-shadow: 0 0 0 1px var(--brand-500);
}
.toolbar-btn:focus-visible {
    outline: none;
    border-color: var(--brand-500);
    box-shadow: 0 0 0 3px rgba(19, 41, 75, 0.15);
}

/* Icon — quiet at rest, brand on open */
.toolbar-btn-icon {
    flex-shrink: 0;
    width: 14px;
    height: 14px;
    color: #475569; /* slate-600 */
    transition: color 150ms ease;
    stroke-width: 1.75;
}
.toolbar-btn:hover .toolbar-btn-icon {
    color: #1e293b; /* slate-800 */
}
.toolbar-btn[aria-expanded="true"] .toolbar-btn-icon {
    color: var(--brand-500);
}

/* Trailing chevron for dropdown buttons */
.toolbar-btn-chevron {
    flex-shrink: 0;
    width: 12px;
    height: 12px;
    color: #94a3b8; /* slate-400 */
    margin-left: 4px;
    transition: color 150ms ease, transform 200ms ease;
    stroke-width: 2;
}
.toolbar-btn:hover .toolbar-btn-chevron {
    color: #475569;
}
.toolbar-btn[aria-expanded="true"] .toolbar-btn-chevron {
    color: var(--brand-500);
    transform: rotate(180deg);
}

/* Internal vertical separator for the fused Filter+Count button */
.toolbar-btn-fused {
    gap: 14px;
    padding: 0 18px;
}
.toolbar-btn-divider {
    display: inline-block;
    width: 1px;
    height: 18px;
    background: #cbd5e1; /* slate-300 */
    transition: background 150ms ease;
}
.toolbar-btn:hover .toolbar-btn-divider {
    background: #94a3b8; /* slate-400 */
}
.toolbar-btn[aria-expanded="true"] .toolbar-btn-divider {
    background: var(--brand-500);
    opacity: 0.5;
}

/* The right half of the fused button — count + label, slightly
   muted so the eye reads "Filter" first as the primary control. */
.toolbar-btn-count {
    color: #64748b; /* slate-500 */
    font-weight: 400;
    font-variant-numeric: tabular-nums;
    transition: color 150ms ease;
}
.toolbar-btn:hover .toolbar-btn-count {
    color: #334155; /* slate-700 */
}
.toolbar-btn[aria-expanded="true"] .toolbar-btn-count {
    color: var(--brand-700, #0f1f3a);
}
.toolbar-btn-count strong {
    color: #1f2937; /* slate-800 */
    font-weight: 500;
}
.toolbar-btn:hover .toolbar-btn-count strong {
    color: #0f172a;
}
.toolbar-btn[aria-expanded="true"] .toolbar-btn-count strong {
    color: var(--brand-700, #0f1f3a);
}

/* Optional badge for buttons that need to show a count without the
   fused-divider treatment (e.g. saved Views with N saved). Lives
   inline with the button label, distinct from the fused count. */
.toolbar-btn-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    font-size: 10px;
    font-weight: 500;
    line-height: 1;
    color: #64748b;
    background: #f1f5f9;
    border-radius: 999px;
    transition: color 150ms ease, background 150ms ease;
    font-variant-numeric: tabular-nums;
}
.toolbar-btn:hover .toolbar-btn-badge {
    color: #1e293b;
    background: #e2e8f0;
}
.toolbar-btn[aria-expanded="true"] .toolbar-btn-badge {
    color: #ffffff;
    background: var(--brand-500);
}

/* ── Columns visibility ───────────────────────────────────────────────────
   Applied by app.js applyHiddenColumns() to the <col>, <th>, and every
   <td> with a matching data-col-key when the user toggles a column off
   in the columns popover. State is per data-table-id, persisted to
   localStorage. CSS just hides anything carrying the class — the
   table-fixed layout collapses the now-empty column automatically.
   ───────────────────────────────────────────────────────────────────── */
.data-table .col-hidden {
    display: none !important;
}

/* ── Clickability affordances ─────────────────────────────────────────────
   Enterprise apps live or die on whether users know what's clickable.
   This block handles five patterns:

   1. Table rows that link to a detail page get a chevron arrow that
      appears on hover at the right edge — the universal "go" cue.
   2. Inline anchors inside table cells get a dotted underline that
      becomes solid on hover, so a manufacturer column reads as a
      separate link from the row link without competing with it.
   3. KPI cards with a `url` show a tiny ↗ arrow in the top-right
      corner so it's clear they're linked, not just stat boxes.
   4. Spec-sheet anchors (dt/dd lists on detail pages) get the same
      hover-underline treatment as inline table anchors.
   5. Every clickable element gets cursor: pointer (defensive — most
      do already, but `<button>` and `<div role="button">` sometimes
      don't depending on browser defaults).
   ───────────────────────────────────────────────────────────────────── */

/* 1. Table row "go" chevron — always visible, brighter on hover.
      A persistent right-edge chevron is the universal "this row
      navigates" cue. Hover-only would force users to discover by
      mousing around. */
.data-table tbody tr.row-link {
    position: relative;
}
.data-table tbody tr.row-link td:last-child {
    padding-right: 32px; /* room for the chevron */
}
.data-table tbody tr.row-link::after {
    content: '';
    position: absolute;
    right: 14px;
    top: 50%;
    width: 14px;
    height: 14px;
    transform: translateY(-50%);
    opacity: 0.32;
    transition: opacity 120ms ease, transform 160ms ease, right 160ms ease;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%239ca3af' stroke-width='2.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5l7 7-7 7'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    pointer-events: none;
}
.data-table tbody tr.row-link:hover::after {
    opacity: 1;
    right: 10px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2313294B' stroke-width='2.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5l7 7-7 7'/%3E%3C/svg%3E");
}

/* First-cell entity anchor — styled as a label, NOT a link at rest.
   The persistent chevron + row hover state already do all the
   "this row is clickable" work; underlining the name on top of that
   is signal redundancy that makes the table feel like a document
   rather than an app. Underline appears only on hover as a
   reinforcement. This matches how Linear / Vercel / Stripe / Govini /
   Palantir Foundry style data table rows. */
.data-table tbody td .row-link-anchor {
    text-decoration: none;
    color: var(--gray-900, #111827);
    font-weight: 600;
    transition: color 120ms ease, text-decoration-color 120ms ease;
}
.data-table tbody tr.row-link:hover .row-link-anchor {
    color: var(--brand-700, #0f1f3a);
    text-decoration: underline;
    text-decoration-color: var(--brand-500);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}

/* 2. Inline anchors inside non-first cells (manufacturer, platform, etc.)
      get the same underline treatment so users can tell they're
      separate links from the row-level navigation. */
.data-table tbody td a:not(.row-link-anchor) {
    text-decoration: underline;
    text-decoration-color: rgba(19, 41, 75, 0.22);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: text-decoration-color 100ms ease, color 100ms ease;
}
.data-table tbody td a:not(.row-link-anchor):hover {
    text-decoration-color: var(--brand-500);
}

/* 3. KPI cards with a link get a corner arrow + lift on hover */
a.kpi-card {
    position: relative;
    cursor: pointer;
}
a.kpi-card::after {
    content: '';
    position: absolute;
    top: 10px;
    right: 10px;
    width: 12px;
    height: 12px;
    opacity: 0.35;
    transition: opacity 120ms ease, transform 160ms ease;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2313294B' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M7 17L17 7M7 7h10v10'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    pointer-events: none;
}
a.kpi-card:hover::after {
    opacity: 0.85;
    transform: translate(2px, -2px);
}

/* 4. Inline text links — universal dotted underline at rest, solid on
      hover. Targets any anchor that uses the brand color (the
      established convention for text links across the app), so detail
      page spec sheets, SCRA component lists, sub-component chips, etc.
      all share one affordance vocabulary. */
a.text-brand-500 {
    text-decoration: underline;
    text-decoration-color: rgba(19, 41, 75, 0.22);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: text-decoration-color 120ms ease, color 120ms ease;
}
a.text-brand-500:hover {
    text-decoration-color: var(--brand-500);
}
/* Don't double-underline: card-style and KPI-style anchors already
   carry their own affordance (corner arrow), so suppress the inline
   text-link underline when those classes are present. */
a.text-brand-500.link-card,
a.text-brand-500.kpi-card,
a.text-brand-500.row-link-anchor {
    text-decoration: none;
}

/* 4b. Card-style entity links (e.g. company → platforms grid, location
       → children grid). Same affordance vocabulary as KPI cards: a
       small ↗ arrow in the top-right corner that brightens and shifts
       on hover, plus a subtle border lift. The cursor pointer + the
       rest-state arrow tell the user "I navigate" without requiring
       hover discovery. */
a.link-card {
    position: relative;
    cursor: pointer;
    padding-right: 36px; /* room for the corner arrow */
}
a.link-card::after {
    content: '';
    position: absolute;
    top: 12px;
    right: 12px;
    width: 12px;
    height: 12px;
    opacity: 0.35;
    transition: opacity 120ms ease, transform 160ms ease;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2313294B' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M7 17L17 7M7 7h10v10'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    pointer-events: none;
}
a.link-card:hover::after {
    opacity: 0.85;
    transform: translate(2px, -2px);
}

/* 5. Defensive cursor pointer */
button:not(:disabled),
[role="button"]:not(:disabled),
.row-link,
a.kpi-card {
    cursor: pointer;
}

/* ── KPI strip status accents ─────────────────────────────────────────────
   The kpi_strip component renders cards with an optional `status` field
   ("high" / "medium" / "low" / "ok" / "warn" / "info"). Status drives
   two visual cues: a small colored dot inline with the label, and a
   2px colored left border on the card. Both communicate the same
   thing — "this metric needs attention" — so the status reads at a
   glance without the user parsing text.
   ───────────────────────────────────────────────────────────────────── */
.kpi-status-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 999px;
    flex-shrink: 0;
}
.kpi-status-dot-high   { background: #ef4444; box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.15); }
.kpi-status-dot-medium { background: #f59e0b; box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.15); }
.kpi-status-dot-low    { background: #10b981; box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.15); }
.kpi-status-dot-ok     { background: #10b981; box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.15); }
.kpi-status-dot-warn   { background: #f59e0b; box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.15); }
.kpi-status-dot-info   { background: #3b82f6; box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15); }

.kpi-card-high   { border-left: 2px solid #ef4444; }
.kpi-card-medium { border-left: 2px solid #f59e0b; }
.kpi-card-low    { border-left: 2px solid #10b981; }
.kpi-card-ok     { border-left: 2px solid #10b981; }
.kpi-card-warn   { border-left: 2px solid #f59e0b; }
.kpi-card-info   { border-left: 2px solid #3b82f6; }

/* ── HTMX request progress bar ────────────────────────────────────────────
   1px brand-colored bar pinned to the top of the viewport. Hidden by
   default; .is-loading class (toggled in app.js from htmx:beforeRequest /
   htmx:afterRequest) drives a 250ms-delayed appearance + indeterminate
   slide animation so very fast requests don't flash a bar at all.
   ───────────────────────────────────────────────────────────────────── */
.htmx-progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    pointer-events: none;
    z-index: 100;
    opacity: 0;
    transition: opacity 120ms ease;
}
.htmx-progress::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 35%;
    background: linear-gradient(90deg, transparent, var(--brand-500, #13294B), transparent);
    transform: translateX(-100%);
}
.htmx-progress.is-loading {
    /* 250ms delay — flicker-free for sub-250ms requests */
    opacity: 1;
    transition: opacity 120ms ease 250ms;
}
.htmx-progress.is-loading::before {
    animation: htmx-progress-slide 1.1s ease-in-out infinite;
}
@keyframes htmx-progress-slide {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(400%); }
}

/* ── HTMX swap target tab cross-fade ──────────────────────────────────────
   When the swap target carries .htmx-fade, briefly drop opacity during
   the swap so the change is visible without being jarring. The
   .htmx-swapping class is added by HTMX automatically right before the
   DOM is replaced, and removed after .htmx-settling expires.
   ───────────────────────────────────────────────────────────────────── */
.htmx-fade {
    transition: opacity 120ms ease;
}
.htmx-fade.htmx-swapping,
.htmx-fade.htmx-request {
    opacity: 0.55;
}

/* ── HTMX request "pending" feedback on the triggering element ────────────
   HTMX adds .htmx-request to whatever element fired the in-flight
   request. This is independent of the global #htmx-progress bar — it
   tells the user *which* control they clicked is still working. We
   apply it to tab strip items and pagination links so the click feels
   "pressed" until the swap completes.
   ───────────────────────────────────────────────────────────────────── */
.tab-strip-item.htmx-request {
    opacity: 0.6;
    pointer-events: none;
}

/* ── Programme Contracts (May 2026 contract data) ─────────────────────────
   Buyer-category and contract-type badges + the inline split bar at the
   top of the Programme Contracts card. Six tier colors for buyer
   categories (NASA, DoD, ESA, Commercial, Govt, Self-funded + unknown
   fallback) — view-side helpers _contract_cat_class / _contract_type_class
   compute the tier slug per row. */

.contract-cat-badge,
.contract-type-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 9999px;
    font-size: 11px;
    font-weight: 500;
    border: 1px solid;
    white-space: nowrap;
}

.contract-cat-bar { display: block; }
.contract-cat-swatch {
    display: inline-block;
    height: 8px;
    width: 8px;
    border-radius: 2px;
}

/* Buyer category tiers — applied to .contract-cat-badge / .contract-cat-bar / .contract-cat-swatch */
.contract-cat-nasa.contract-cat-badge       { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }
.contract-cat-nasa.contract-cat-bar,
.contract-cat-nasa.contract-cat-swatch      { background: #3b82f6; }

.contract-cat-dod.contract-cat-badge        { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.contract-cat-dod.contract-cat-bar,
.contract-cat-dod.contract-cat-swatch       { background: #ef4444; }

.contract-cat-commercial.contract-cat-badge { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.contract-cat-commercial.contract-cat-bar,
.contract-cat-commercial.contract-cat-swatch { background: #10b981; }

.contract-cat-esa.contract-cat-badge        { background: #eef2ff; color: #4338ca; border-color: #c7d2fe; }
.contract-cat-esa.contract-cat-bar,
.contract-cat-esa.contract-cat-swatch       { background: #6366f1; }

.contract-cat-govt.contract-cat-badge       { background: #fffbeb; color: #b45309; border-color: #fde68a; }
.contract-cat-govt.contract-cat-bar,
.contract-cat-govt.contract-cat-swatch      { background: #f59e0b; }

.contract-cat-self.contract-cat-badge       { background: #f5f3ff; color: #6d28d9; border-color: #ddd6fe; }
.contract-cat-self.contract-cat-bar,
.contract-cat-self.contract-cat-swatch      { background: #8b5cf6; }

.contract-cat-unknown.contract-cat-badge    { background: #f9fafb; color: #4b5563; border-color: #e5e7eb; }
.contract-cat-unknown.contract-cat-bar,
.contract-cat-unknown.contract-cat-swatch   { background: #9ca3af; }

/* Contract type tiers — applied to .contract-type-badge */
.contract-type-ffp     { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.contract-type-cpff    { background: #fffbeb; color: #b45309; border-color: #fde68a; }
.contract-type-ota     { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }
.contract-type-idiq    { background: #f5f3ff; color: #6d28d9; border-color: #ddd6fe; }
.contract-type-private { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.contract-type-co-fund { background: #eef2ff; color: #4338ca; border-color: #c7d2fe; }
.contract-type-other   { background: #f9fafb; color: #4b5563; border-color: #e5e7eb; }

/* ── Reduced motion ────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}
