/* ═══ Boostlogix Warehouse Operations — shared chrome ══════════════════════
   Loaded by index.html and analyse.html; each adds one page-specific sheet on
   top (css/index.css, css/analyse.css). Everything here is the part both pages
   share with each other AND with the other Boostlogix tools: the token set, the
   brand header, the theme menu, and the sidebar shell.
   ═════════════════════════════════════════════════════════════════════════ */

/* ─── Theme tokens ─────────────────────────────────────────────────────── */
/* FALLBACKS, not the brand. The brand lives in the project-root palette.json,
   which js/applyPalette.js fetches at startup and writes onto these same
   variables — see that file. These neutral grays are the graceful-degrade net:
   if palette.json 404s or is malformed the app still renders, just in gray.
   Semantic colors (danger, ok) keep their real hue since the UI leans on it.

   Variable names are co2-tool's and route_opt-demo's, deliberately: same names,
   same dark-override mechanism, so every tool reads one palette and a brand
   change is one file. */
:root {
    --brand-700: #37474f;
    --brand-600: #455a64;
    --brand-500: #546e7a;
    --brand-light: #eceff1;
    --brand-pale: #f5f7f8;

    --page: #f0f2f5;
    --surface: #ffffff;
    --line: #e0e0e0;
    --muted: #546e7a;
    --text: #333333;
    --text-soft: #555555;
    --text-faint: #666666;

    --danger: #c62828;
    --danger-bg: #fdecea;
    --ok: #2e7d32;
    --ok-bg: #e8f5e9;

    --accent-text: #0d47a1;
    --card-line: rgba(15, 40, 80, 0.1);
    --shadow-card: 0 1px 2px rgba(15, 40, 80, 0.04), 0 4px 16px rgba(15, 40, 80, 0.06);

    --chart-1: #37474f;
    --chart-2: #546e7a;
    --chart-3: #78909c;
    --chart-4: #90a4ae;
    --chart-5: #b0bec5;
    --chart-6: #cfd8dc;

    /* palette.json's `font`. Unlike co2-tool and route_opt-demo, this app DOES
       ship the webfont (the pages already loaded one from Google Fonts, so
       swapping the family cost nothing) — hence real Inter here rather than
       whichever stack entry the OS happens to have. */
    --font-sans: Inter, system-ui, "Segoe UI", Roboto, sans-serif;
    /* Monospace stays a system stack: nothing here ships a code webfont. */
    --font-mono: Consolas, Monaco, "Courier New", monospace;

    /* ── App tokens ──────────────────────────────────────────────────────────
       Not in palette.json, because they are not brand decisions — each one is
       derived from a brand token, so they track it for free. */
    /* Second surface: insets, stat boxes and sub-headers that sit off --surface. */
    --surface-2: var(--brand-pale);
    /* Row/control hover. One step further from --surface than --surface-2. */
    --hover: var(--brand-light);
    /* Text and glyphs on a --brand-600 fill. White in both themes: the brand
       blues are shared between light and dark, so what sits on them is too. */
    --on-brand: #ffffff;
    /* Modal backdrop. Dark in both themes — a light scrim over a light page
       reads as a render glitch rather than a blocked surface. */
    --scrim: rgba(15, 40, 80, 0.45);

    --sidebar-w: 272px;
}

/* Dark-mode fallbacks — mirrors palette.json's *_dark blocks so a theme flip
   repaints instantly (the pre-paint <script> in both pages sets the attribute)
   even before applyPalette's fetch resolves. Brand blues are shared with light
   and are NOT redeclared; neither are the app tokens, which are derived. */
:root[data-theme="dark"] {
    --brand-light: #24344b;
    --brand-pale: #182234;

    --page: #0f172a;
    --surface: #1b2637;
    --line: #334155;
    --muted: #94a3b8;
    --text: #e5edf5;
    --text-soft: #c3cfdd;
    --text-faint: #9aa8ba;

    --danger: #ff6b6b;
    --danger-bg: rgba(198, 40, 40, 0.22);
    --ok: #66bb6a;
    --ok-bg: rgba(67, 160, 71, 0.18);

    --accent-text: #8ec5ff;
    --card-line: rgba(255, 255, 255, 0.1);
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.35), 0 8px 24px rgba(0, 0, 0, 0.45);

    --chart-1: #5b9be8;
    --chart-2: #2fd39a;
    --chart-3: #f5c14b;
    --chart-4: #4cc94c;
    --chart-5: #8b7ce8;
    --chart-6: #f47272;

    --scrim: rgba(0, 0, 0, 0.7);
}

/* ── Legacy aliases ──────────────────────────────────────────────────────
   The app was built on its own `--bl-*` palette before it joined the shared
   token set. Rather than rename several hundred usages across both pages and
   six operations panels — every one of them a chance to miss a rule and leave a
   stranded color — the old names are kept as ALIASES onto the new tokens. They
   are a compatibility shim, not part of the palette: write new rules against the
   tokens above. The one thing they buy is that the panels' injected CSS
   (js/operations/main.js) follows the brand and the dark theme for free. */
:root {
    --bl-blauw: var(--brand-500);
    --bl-donker: var(--brand-700);
    /* Orange, red and gold have no brand token — they are semantic accents in
       the data views (warnings, congestion, selection), so they map onto the
       chart ramp, which does flip with the theme. */
    --bl-oranje: var(--chart-3);
    --bl-rood: var(--danger);
    --bl-goud: var(--chart-5);
    --bg: var(--page);
    --sidebar-bg: var(--surface);
    --border: var(--line);
    --text-muted: var(--text-soft);
    --text-light: var(--text-faint);
}

/* Match the OS/UA form-control + scrollbar rendering to the active theme. */
:root {
    color-scheme: light;
}
:root[data-theme="dark"] {
    color-scheme: dark;
}

/* ─── Reset & base ─────────────────────────────────────────────────────── */
*,
*::before,
*::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* The `hidden` attribute is a UA rule at the lowest possible specificity, so any
   class that sets `display` silently defeats it — .am-item does. */
[hidden] {
    display: none !important;
}

body {
    background: var(--page);
    color: var(--text);
    font-family: var(--font-sans);
}

button {
    font-family: inherit;
}

code,
.mono {
    font-family: var(--font-mono);
    font-size: 13px;
    background: var(--surface-2);
    padding: 1px 5px;
    border-radius: 4px;
}

/* --- Sidebar ------------------------------------------------------------ */
/* This app's brand chrome is the sidebar, not a top bar: the 3D viewport wants
   the full height, and a horizontal brand bar on top of a vertical nav would be
   two chromes. So the sidebar HEADER carries what the other tools' top bar
   carries -- the gradient, the wordmark and the page switch -- and the rest of
   the sidebar is ordinary surface below it. Same anatomy, turned 90 degrees.

   Three bands, and only the middle one scrolls: the brand header stays put at
   the top, the panel list scrolls between them, and the menu button stays put at
   the bottom. overflow is visible on the sidebar itself so the menu can open
   upward over the viewport instead of being clipped. */
#sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--sidebar-w);
    background: var(--surface);
    border-right: 1px solid var(--line);
    box-shadow: 2px 0 12px var(--card-line);
    display: flex;
    flex-direction: column;
    z-index: 100;
    overflow: visible;
}

.sidebar-header {
    /* The gradient the other Boostlogix tools wear. Everything on it is white or
       a white alpha -- no surface token reads on a saturated brand fill. */
    background: linear-gradient(120deg, var(--brand-700), var(--brand-600) 60%, var(--brand-500));
    padding: 13px 14px 11px;
    flex-shrink: 0;
}
/* The wordmark gets its own line and nothing beside it. It is 1066x141, so at
   20px tall it is already 151px wide -- a divider and a page title next to it do
   not fit in a 272px sidebar, which is what used to push text out of the bar.
   The page switch below names the page anyway. */
.sidebar-logo {
    height: 20px;
    width: auto;
    display: block;
    margin-bottom: 11px;
}

/* Page switch, on the gradient. The active page is inverted -- white surface,
   brand text -- because a filled brand button on a brand gradient has no surface
   of its own to sit on. */
.sidebar-nav {
    display: flex;
    gap: 5px;
}
.sidebar-nav a {
    flex: 1;
    text-decoration: none;
    min-width: 0;
}
.nav-btn {
    width: 100%;
    padding: 6px 4px;
    border: 1px solid rgba(255, 255, 255, 0.32);
    border-radius: 7px;
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: background 0.15s;
}
.nav-btn:hover {
    background: rgba(255, 255, 255, 0.26);
}
.nav-btn.active {
    background: #fff;
    border-color: #fff;
    color: var(--brand-700);
    font-weight: 700;
    cursor: default;
}

.sidebar-body {
    padding: 16px;
    flex: 1;
    overflow-y: auto;
}

/* Bottom band: the menu button, pinned. Ordinary surface rather than a second
   gradient -- a brand fill at both ends would frame the panel list like a page
   footer. */
.sidebar-footer {
    flex-shrink: 0;
    padding: 8px 14px;
    border-top: 1px solid var(--line);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.sidebar-footer .foot-lbl {
    font-size: 10px;
    color: var(--text-faint);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- App menu ----------------------------------------------------------- */
/* Port of co2-tool's AccountMenu, cut down to what a slim sidebar can carry: an
   icon button rather than a name pill, and only the two things this app actually
   has to offer -- the theme and the way back to default settings. The identity
   head the other tools show goes with it; there is no session here to identify.
   See js/appMenu.js.

   It opens UPWARD (bottom, not top) because the trigger sits at the bottom of
   the sidebar, and it is a little wider than its column on purpose: three theme
   segments inside 272px minus padding would be too cramped to label. */
.am-wrap {
    position: relative;
    flex-shrink: 0;
}
.am-trigger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 50%;
    color: var(--text-soft);
    cursor: pointer;
}
.am-trigger:hover,
.am-trigger.open {
    background: var(--hover);
    border-color: var(--brand-500);
    color: var(--accent-text);
}

.am-menu {
    position: absolute;
    bottom: calc(100% + 8px);
    right: 0;
    z-index: 50;
    width: 228px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 10px;
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.18);
    overflow: hidden;
    text-align: left;
}

/* Theme picker: three segments in a well, the active one raised onto the
   surface. Reflects the stored preference -- see js/appMenu.js. */
.am-theme {
    padding: 9px 11px 11px;
    border-bottom: 1px solid var(--line);
}
.am-theme-lbl {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--text-faint);
    margin-bottom: 6px;
}
.am-theme-group {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2px;
    background: var(--brand-pale);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 2px;
}
.am-theme-opt {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    padding: 6px 2px;
    cursor: pointer;
    font-family: inherit;
    font-size: 10.5px;
    font-weight: 500;
    color: var(--text-soft);
}
.am-theme-opt:hover {
    color: var(--text);
}
.am-theme-opt.active {
    background: var(--surface);
    border-color: var(--line);
    color: var(--accent-text);
    font-weight: 600;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.am-section {
    padding: 5px;
}
.am-item {
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    border-radius: 6px;
    padding: 7px 9px;
    cursor: pointer;
    font-family: inherit;
    font-size: 12px;
    color: var(--text);
    text-decoration: none;
}
.am-item:hover {
    background: var(--brand-pale);
}
/* Clearing the settings throws tuned parameters away, which is destructive
   enough to say so. */
.am-item-danger {
    color: var(--danger);
    font-weight: 500;
}
.am-item-danger:hover {
    background: var(--danger-bg);
    color: var(--danger);
}
.am-note {
    font-size: 11px;
    line-height: 1.4;
    color: var(--text-faint);
}
/* Only shown once the reset is armed, so the warning is not sitting in the menu
   as prose the other 99% of the time -- see js/appMenu.js. */
.am-confirm .am-note {
    padding: 3px 9px 6px;
}

/* ─── Sidebar controls ─────────────────────────────────────────────────── */
/* The vocabulary the sidebar is built from, shared by index.html, analyse.html
   and the operations panels that inject markup into it. */
.section-title {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: var(--text-faint);
    margin: 16px 0 8px;
}
.section-title:first-child {
    margin-top: 0;
}

.divider {
    border: none;
    border-top: 1px solid var(--line);
    margin: 16px 0 0;
}

/* Stats */
.stat-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
}
.stat-box {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 10px 12px;
}
.stat-box .val {
    font-size: 20px;
    font-weight: 800;
    color: var(--accent-text);
    line-height: 1;
}
.stat-box .lbl {
    font-size: 10px;
    color: var(--text-soft);
    margin-top: 3px;
    font-weight: 600;
}

/* Mode buttons — the sidebar's one button shape: page nav, colour modes, replay
   speeds and the operations module switches all use it. */
.mode-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
}
.mode-btn {
    padding: 9px 8px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--surface-2);
    color: var(--text-soft);
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: all 0.18s;
    text-align: center;
}
.mode-btn .badge {
    display: block;
    font-size: 9px;
    color: var(--text-faint);
    margin-top: 2px;
    font-weight: 400;
}
.mode-btn:hover {
    border-color: var(--brand-500);
    color: var(--accent-text);
    background: var(--hover);
}
.mode-btn.active {
    border-color: var(--brand-500);
    background: var(--brand-light);
    color: var(--accent-text);
    box-shadow: 0 0 0 1px var(--brand-500);
}
.mode-btn.active .badge {
    color: var(--accent-text);
}

/* Sliders */
.filter-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 6px;
    font-size: 11px;
    color: var(--text-soft);
}
.filter-row label {
    min-width: 28px;
    font-weight: 600;
}
.filter-row input[type="range"] {
    flex: 1;
    accent-color: var(--brand-600);
    height: 4px;
    cursor: pointer;
}
.filter-val {
    min-width: 24px;
    text-align: right;
    color: var(--accent-text);
    font-weight: 700;
    font-size: 12px;
}

/* Filter checkboxes */
.check-group {
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin-top: 4px;
}
.check-item {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 11px;
    color: var(--text-soft);
    cursor: pointer;
    padding: 3px 6px;
    border-radius: 5px;
    transition: background 0.15s;
    user-select: none;
}
.check-item:hover {
    background: var(--surface-2);
}
.check-item input[type="checkbox"] {
    accent-color: var(--brand-600);
    width: 13px;
    height: 13px;
    cursor: pointer;
    flex-shrink: 0;
}
.check-dot {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    flex-shrink: 0;
}
.filter-section-hdr {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin: 16px 0 6px;
}
.filter-section-hdr .section-title {
    margin: 0;
}
.wis-btn {
    font-size: 10px;
    color: var(--accent-text);
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    font-weight: 600;
    padding: 0;
}

/* Legend */
.legend-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 11px;
    color: var(--text-soft);
    margin-bottom: 5px;
    font-weight: 400;
}
.legend-dot {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    flex-shrink: 0;
}

/* Upload */
.upload-area {
    display: block;
    border: 1.5px dashed var(--line);
    border-radius: 8px;
    padding: 12px 10px;
    text-align: center;
    font-size: 11px;
    color: var(--text-soft);
    cursor: pointer;
    transition: all 0.18s;
    margin-top: 4px;
    font-family: inherit;
    font-weight: 400;
}
.upload-area:hover {
    border-color: var(--brand-500);
    color: var(--accent-text);
    background: var(--hover);
}
.upload-status {
    font-size: 11px;
    color: var(--ok);
    margin-top: 5px;
    display: none;
    font-weight: 600;
}
