@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap');

/* Brand palette (Sentri Systems): #f46731 orange / #14120f near-black /
   #ffffff white / #29231a dark brown. --panel-border and --text-muted are
   the only tones not directly one of the four -- interpolated between them
   since a real UI needs a subtle separator/secondary-text tone that plain
   black/white/brown/orange can't provide on their own. --ok/--warning/
   --critical stay their original green/amber/red: those are functional
   safety-status colors (device online/offline, active-worker long-stay
   flags, flash messages), not brand decoration, and a site supervisor
   scanning a long list for a red-flagged critical row still needs that
   color signal. Reports > Analytics' stat tiles are the one place the
   client asked for text tags instead of color-coded severity -- see the
   .stat-block.stat-warning/.stat-critical ::before rules below. */
:root {
    --bg: #14120F;
    --panel: #29231A;
    --panel-border: #3D3527;
    --text-primary: #FFFFFF;
    --text-muted: #B7AC9A;
    --ok: #3DD68C;
    --warning: #F0A93C;
    --critical: #E5484D;
    --accent: #F46731;

    --font-ui: 'Inter', -apple-system, sans-serif;
    --font-mono: 'JetBrains Mono', 'Courier New', monospace;
}

/* Light theme override — activated via <html data-theme="light"> */
html[data-theme="light"] {
    --bg: #FBF7F2;
    --panel: #FFFFFF;
    --panel-border: #E5DDD0;
    --text-primary: #14120F;
    --text-muted: #7A7265;
    --ok: #1FA36A;
    --warning: #B4780E;
    --critical: #C7333B;
    --accent: #F46731;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--text-primary);
    font-family: var(--font-ui);
    font-size: 15px;
    line-height: 1.5;
    transition: background 0.15s ease, color 0.15s ease;
}

a { color: inherit; text-decoration: none; }

.layout {
    display: flex;
    min-height: 100vh;
}

/* Sidebar */
.sidebar {
    width: 220px;
    /* Pinned to the viewport while .main scrolls -- previously a normal
       flow sibling with no explicit height, so it stretched to match
       .main's full content height and scrolled away together with it on
       any page long enough to scroll (confirmed: nav links, the tunnel
       toggle, everything gone after ~800px of scroll). A first attempt
       here used align-self:flex-start to shrink the box to its own (short)
       content so sticky had somewhere to "stick" -- but that also shrank
       the panel-colored background, leaving a visibly empty gap in the
       true page background color below "Dark mode" instead of solid
       sidebar color all the way down. Fixed height:100vh keeps the sidebar
       always exactly viewport-tall (background included) while still
       pinning it at top:0 as .main scrolls past underneath. */
    height: 100vh;
    position: sticky;
    top: 0;
    overflow-y: auto;
    flex-shrink: 0;
    background: var(--panel);
    border-right: 1px solid var(--panel-border);
    padding: 24px 0;
    display: flex;
    flex-direction: column;
}

.sidebar-brand {
    padding: 0 20px 20px;
    border-bottom: 1px solid var(--panel-border);
    margin-bottom: 12px;
    white-space: nowrap;
}

.sidebar-collapse-btn,
.sidebar-expand-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    background: transparent;
    border: 1px solid var(--panel-border);
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 13px;
}

.sidebar-collapse-btn {
    /* A sibling of .sidebar-brand now (not nested inside it) so it stays
       visible at mobile widths, where .sidebar-brand is hidden below --
       needs its own spacing since it no longer inherits .sidebar-brand's
       padding. */
    margin: 0 0 12px 20px;
    flex-shrink: 0;
}

.sidebar-collapse-btn:hover,
.sidebar-expand-btn:hover {
    color: var(--text-primary);
    background: rgba(255,255,255,0.05);
}

/* Floating re-open button, only shown while the sidebar is collapsed. */
.sidebar-expand-btn {
    display: none;
    position: fixed;
    top: 20px;
    left: 20px;
    z-index: 500;
    width: 36px;
    height: 36px;
    background: var(--panel);
    box-shadow: 0 2px 8px rgba(0,0,0,0.25);
}

html[data-sidebar="collapsed"] .sidebar {
    width: 0;
    padding-left: 0;
    padding-right: 0;
    border-right: none;
}

html[data-sidebar="collapsed"] .sidebar-expand-btn {
    display: flex;
}

/* .sidebar-expand-btn is fixed at top:20px/left:20px, 36px square (bottom
   edge at y=56) -- .main's own top padding (28px normally, 20px at the
   800px breakpoint below) isn't enough clearance on its own, so the page
   title rendered under it. Confirmed live: on a narrow/mobile viewport,
   where .main's padding drops to a flat 20px, the button sat directly on
   top of the Overview page title. Push .main down only while collapsed,
   enough to clear the button on any viewport width, rather than
   permanently eating that space when the sidebar is visible (where the
   button isn't floating over content at all). */
html[data-sidebar="collapsed"] .main {
    padding-top: 76px;
}

.brand-logo {
    display: block;
    height: 40px;
    width: auto;
    margin-bottom: 10px;
}

.sidebar-brand .brand-title {
    font-weight: 700;
    font-size: 15px;
    letter-spacing: 0.02em;
}

.sidebar-brand .brand-sub {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 4px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* Site name -- small, sits right under "By Sentri Systems" so it's always
   visible (this block is inside the sticky .sidebar, unlike the page
   header's copy of the same text, which scrolls away with .main). */
.brand-site {
    display: inline-block;
    margin-top: 8px;
    padding: 5px 12px;
    background: var(--accent);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    border-radius: 20px;
    white-space: nowrap;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 20px;
    color: var(--text-muted);
    font-size: 14px;
    font-weight: 500;
    border-left: 3px solid transparent;
}

.nav-link:hover {
    color: var(--text-primary);
    background: rgba(255,255,255,0.03);
}

.nav-link.active {
    color: var(--text-primary);
    border-left-color: var(--accent);
    background: rgba(244,103,49,0.08);
}

.nav-icon {
    font-family: var(--font-mono);
    font-size: 12px;
    width: 16px;
    color: var(--text-muted);
}

/* Main content */
.main {
    flex: 1;
    min-width: 0; /* let this flex item shrink below its content's natural
        width — without this, a wide table/row (e.g. Registered Users)
        forces the whole page wider than the viewport instead of just
        scrolling internally within its own panel (see .panel below). */
    padding: 28px 36px;
    /* Previously capped at 1200px regardless of screen size, which left a
       large dead strip on the right on any kiosk display wider than
       ~1450px (1200px content + 220px sidebar) -- exactly the "empty
       space" contractors complained about. .stat-row's grid already uses
       auto-fit and reflows to use whatever width it's given, so removing
       the cap (with a generous ceiling only for genuinely ultra-wide
       monitors, purely for line-length readability) lets it actually do
       that instead of being artificially starved of width. */
    max-width: 1800px;
    /* Once the viewport is wide enough to hit that cap (confirmed: with
       the sidebar collapsed on a >1800px-wide window), flex:1 still
       reserves the full remaining row width for .main, so the leftover
       past 1800px was all dumped on the right as one lopsided gap. Centers
       it instead, so any leftover space is split evenly on both sides. */
    margin: 0 auto;
}

.page-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    margin-bottom: 24px;
    flex-wrap: wrap;
    gap: 16px;
}

.page-title {
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.page-subtitle {
    color: var(--text-muted);
    font-size: 13px;
    margin-top: 4px;
}

/* Tunnel toggle - segmented control */
.tunnel-toggle {
    display: inline-flex;
    background: var(--panel);
    border: 1px solid var(--panel-border);
    border-radius: 8px;
    padding: 3px;
    gap: 2px;
}

.tunnel-toggle a {
    padding: 7px 14px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    border-radius: 6px;
}

.tunnel-toggle a.active {
    background: var(--accent);
    color: #fff;
}

/* Stat blocks */
.stat-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 14px;
    margin-bottom: 28px;
}

.stat-block {
    position: relative;
    background: var(--panel);
    border: 1px solid var(--panel-border);
    border-left: 3px solid var(--panel-border);
    border-radius: 8px;
    padding: 16px 18px;
}

/* Severity is a text tag in the corner, not a border color -- the brand
   palette is one accent (orange) plus neutrals, so there's no second/third
   hue available to color-code "warning" vs "critical" the way the old blue
   theme did. Two tiers of the same accent (outlined vs. solid-filled)
   still reads as "more severe" without needing more hues. stat-ok/
   stat-accent tiles (nothing wrong, or just an informational count) get no
   tag at all -- only warning/critical states are worth flagging. */
.stat-block.stat-warning,
.stat-block.stat-critical {
    padding-top: 34px;
}

.stat-block.stat-warning::before,
.stat-block.stat-critical::before {
    position: absolute;
    top: 12px;
    right: 14px;
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 3px 9px;
    border-radius: 20px;
}

.stat-block.stat-warning::before {
    content: "Warning";
    background: transparent;
    color: var(--accent);
    border: 1px solid var(--accent);
}

.stat-block.stat-critical::before {
    content: "Critical";
    background: var(--accent);
    color: #fff;
    border: 1px solid var(--accent);
}

.stat-value {
    font-family: var(--font-mono);
    font-size: 28px;
    font-weight: 700;
    line-height: 1;
}

.stat-label {
    color: var(--text-muted);
    font-size: 12px;
    margin-top: 6px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

/* Second line under a tile's label for a qualifier on the big number
   ("4 likely missed scan-out", "at 11:42"). Always rendered, even when
   the qualifier is zero/empty, so the tile row doesn't change height as
   the numbers move. stat-sub-flag = the qualifier is non-zero and worth a
   glance, in the accent rather than a severity color: it's a data-quality
   note about the headline number, not an alarm. */
.stat-sub {
    color: var(--text-muted);
    font-size: 11px;
    margin-top: 8px;
    font-family: var(--font-mono);
}

.stat-sub.stat-sub-flag {
    color: var(--accent);
}

/* Panels / cards */
.panel {
    background: var(--panel);
    border: 1px solid var(--panel-border);
    border-radius: 10px;
    margin-bottom: 24px;
    overflow: hidden;
}

/* Wraps just a <table> (not the whole panel, which may also contain
   button rows below it, e.g. "Deactivate Selected") so the horizontal
   scrollbar sits directly under the visible rows. Scoping the scroll to
   the panel itself put the scrollbar at the panel's bottom edge — past
   every row and any buttons below the table — making it unreachable
   without scrolling down through the whole list first.
   max-height + overflow-y bounds long tables (e.g. 100+ registered
   users) to a fixed-size box so its horizontal scrollbar stays at a
   constant, reachable position instead of sliding further away as more
   rows render — same reason the header is pinned with position: sticky. */
.table-scroll {
    overflow: auto;
    max-height: 65vh;
}

.table-scroll table thead th {
    position: sticky;
    top: 0;
    background: var(--panel);
    z-index: 1;
}

.panel-header {
    padding: 14px 18px;
    border-bottom: 1px solid var(--panel-border);
    font-weight: 600;
    font-size: 14px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.panel-header a {
    font-size: 12px;
    color: var(--accent);
    font-weight: 500;
}

/* Worker/status rows with signature left-edge color bar */
.status-row {
    display: flex;
    align-items: center;
    padding: 12px 18px;
    border-left: 4px solid var(--panel-border);
    border-bottom: 1px solid var(--panel-border);
    gap: 16px;
}

.status-row:last-child { border-bottom: none; }
.status-row.flag-ok { border-left-color: var(--ok); }
.status-row.flag-warning { border-left-color: var(--warning); background: rgba(240,169,60,0.05); }
.status-row.flag-critical { border-left-color: var(--critical); background: rgba(229,72,77,0.06); }

.status-row .name {
    font-weight: 600;
    flex: 1;
    min-width: 120px;
}

.status-row .meta {
    color: var(--text-muted);
    font-size: 12px;
}

.status-row .tag {
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--text-muted);
    background: rgba(255,255,255,0.04);
    padding: 2px 8px;
    border-radius: 4px;
}

.status-row .duration {
    font-family: var(--font-mono);
    font-weight: 600;
    font-size: 13px;
    min-width: 70px;
    text-align: right;
}

.duration.flag-ok { color: var(--ok); }
.duration.flag-warning { color: var(--warning); }
.duration.flag-critical { color: var(--critical); }

/* Active Persons: the "likely missed scan-out" band at the bottom of each
   category list. A labelled divider rather than a separate panel, so the
   category's count in the panel header still means "everyone we count as
   inside" and the band reads as a qualifier on that list. */
.band-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 18px;
    border-top: 2px dashed var(--panel-border);
    border-bottom: 1px solid var(--panel-border);
    background: rgba(244,103,49,0.06);
    color: var(--accent);
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.band-header .band-note {
    color: var(--text-muted);
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    font-size: 11px;
}

/* Tables */
table {
    width: 100%;
    border-collapse: collapse;
}

th {
    text-align: left;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    font-weight: 600;
    padding: 10px 18px;
    border-bottom: 1px solid var(--panel-border);
}

td {
    padding: 11px 18px;
    border-bottom: 1px solid var(--panel-border);
    font-size: 13.5px;
}

tr:last-child td { border-bottom: none; }

.tag-mono {
    font-family: var(--font-mono);
    font-size: 12.5px;
    color: var(--text-muted);
}

.badge {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 20px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.badge-entry { background: rgba(61,214,140,0.15); color: var(--ok); }
.badge-exit { background: rgba(138,148,163,0.15); color: var(--text-muted); }
.badge-online { background: rgba(61,214,140,0.15); color: var(--ok); }
.badge-offline { background: rgba(229,72,77,0.15); color: var(--critical); }
.badge-unknown { background: rgba(138,148,163,0.15); color: var(--text-muted); }
/* Neutral category label (e.g. Inspection Team) -- reuses the same accent
   color as stat-accent tiles/active nav links, since red/green here would
   misleadingly read as a status ("bad"/"good") rather than a category. */
.badge-accent { background: rgba(244,103,49,0.15); color: var(--accent); }
.badge-warning { background: rgba(240,169,60,0.15); color: var(--warning); }
.badge-critical { background: rgba(229,72,77,0.15); color: var(--critical); }

/* Empty state */
.empty-state {
    padding: 40px 20px;
    text-align: center;
    color: var(--text-muted);
    font-size: 13px;
}

/* Forms */
.filter-bar {
    display: flex;
    gap: 10px;
    margin-bottom: 18px;
    flex-wrap: wrap;
}

/* Category breakdown chips (Registered Users) -- one per person_category,
   each a link that toggles that category filter. Counts reflect every
   other active filter. */
.category-summary {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin: -2px 0 18px;
}

.cat-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 7px;
    padding: 7px 13px;
    border-radius: 20px;
    background: var(--panel);
    border: 1px solid var(--panel-border);
    color: var(--text-muted);
    font-size: 12.5px;
    font-weight: 600;
    transition: border-color 0.12s ease, color 0.12s ease;
}

.cat-chip:hover {
    color: var(--text-primary);
    border-color: var(--accent);
}

.cat-chip.active {
    background: rgba(244, 103, 49, 0.14);
    border-color: var(--accent);
    color: var(--accent);
}

.cat-chip span {
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 700;
    color: var(--text-primary);
}

.cat-chip.active span { color: var(--accent); }

input[type="text"], input[type="password"], select {
    background: var(--panel);
    border: 1px solid var(--panel-border);
    color: var(--text-primary);
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 13px;
    font-family: var(--font-ui);
}

input[type="text"]:focus, input[type="password"]:focus, select:focus, a:focus, button:focus {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* Custom dropdown ("fake-select") -- replaces native <select> everywhere.
   The kiosk's Chromium (labwc/Wayland on ARM) crashes with "Aw, Snap!"
   the instant a native <select> popup is opened -- confirmed via a
   trusted CDP-dispatched click, independent of page content. This is a
   compositor bug in how labwc hands Chromium a popup surface, not
   anything fixable in page code, so every <select> is replaced with this
   plain-DOM dropdown (a button + an absolutely-positioned div), which
   never asks the browser/compositor to create a native popup surface. */
.fake-select { position: relative; display: inline-block; }
.fake-select-btn {
    background: var(--panel);
    border: 1px solid var(--panel-border);
    color: var(--text-primary);
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 13px;
    font-family: var(--font-ui);
    cursor: pointer;
    text-align: left;
}
.fake-select-menu {
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: 2px;
    z-index: 100;
    background: var(--panel);
    border: 1px solid var(--panel-border);
    border-radius: 6px;
    min-width: 100%;
    max-height: 260px;
    overflow-y: auto;
    box-shadow: 0 6px 16px rgba(0,0,0,0.35);
}
.fake-select-opt {
    padding: 8px 12px;
    cursor: pointer;
    white-space: nowrap;
    font-size: 13px;
    font-family: var(--font-ui);
    color: var(--text-primary);
}
.fake-select-opt:hover, .fake-select-opt.active { background: var(--bg); }

button, .btn {
    background: var(--accent);
    color: #fff;
    border: none;
    padding: 9px 16px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    font-family: var(--font-ui);
}

button:hover, .btn:hover { opacity: 0.9; }

.btn-danger { background: var(--critical); }
.btn-sm { padding: 6px 10px; font-size: 12px; }

.flash-stack {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 18px;
}

.flash-message {
    padding: 10px 14px;
    border-radius: 6px;
    font-size: 13px;
    border: 1px solid var(--panel-border);
    border-left: 3px solid var(--panel-border);
}

.flash-message.flash-success { border-left-color: var(--ok); color: var(--ok); background: rgba(61,214,140,0.08); }
.flash-message.flash-error { border-left-color: var(--critical); color: var(--critical); background: rgba(229,72,77,0.08); }

.report-tabs {
    display: flex;
    gap: 8px;
    margin-bottom: 20px;
}

.report-tabs a {
    padding: 8px 14px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    background: var(--panel);
    border: 1px solid var(--panel-border);
}

.report-tabs a.active {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
}

/* Reports > Analytics: alerts-per-day stacked bar chart. Pure CSS/inline-
   style bars (no charting library) -- this site runs fully offline, so a
   CDN-hosted chart lib is a non-starter and a vendored one is overkill for
   one simple stacked bar. Each .chart-seg's height is set inline per-row
   as a % of the busiest day, so the tallest bar always reaches the top of
   the fixed-height .chart-stack track. */
.chart-bars {
    display: flex;
    align-items: flex-end;
    gap: 6px;
    height: 140px;
    overflow-x: auto;
    padding-bottom: 4px;
}

.chart-col {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 22px;
    flex-shrink: 0;
}

.chart-stack {
    width: 18px;
    height: 120px;
    display: flex;
    flex-direction: column-reverse;
    border-radius: 3px 3px 0 0;
    overflow: hidden;
    background: var(--bg);
}

.chart-seg-warning { background: var(--warning); }
.chart-seg-critical { background: var(--critical); }
.chart-seg-accent { background: var(--accent); }
.chart-seg-muted { background: var(--text-muted); opacity: 0.55; }

.chart-label {
    margin-top: 6px;
    font-size: 10px;
    color: var(--text-muted);
    font-family: var(--font-mono);
    white-space: nowrap;
}

.chart-legend {
    margin-top: 12px;
    font-size: 12px;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap: 6px;
}

.legend-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    margin-left: 10px;
}
.legend-dot:first-child { margin-left: 0; }
.legend-dot.legend-warning { background: var(--warning); }
.legend-dot.legend-accent { background: var(--accent); }
.legend-dot.legend-muted { background: var(--text-muted); opacity: 0.55; }
.legend-dot.legend-critical { background: var(--critical); }

/* Theme toggle */
.theme-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    background: transparent;
    border: 1px solid var(--panel-border);
    color: var(--text-muted);
    border-radius: 6px;
    padding: 6px 10px;
    font-size: 12px;
    cursor: pointer;
}

.theme-toggle:hover { color: var(--text-primary); border-color: var(--accent); }

.sidebar-footer {
    /* Pinned to the bottom of the sidebar (standard "primary nav top,
       account/settings controls bottom" pattern). This previously caused a
       huge dead gap on tall viewports because .sidebar itself had no fixed
       height -- it stretched to match .main's full (sometimes very tall,
       scrolled) content via flex stretch, so margin-top:auto pushed this
       all the way down to match THAT height. Now that .sidebar is a fixed
       height:100vh (see .sidebar above), auto once again pins this to the
       bottom of one screen's worth of sidebar, not the bottom of the page. */
    margin-top: auto;
    padding: 16px 20px 4px;
    border-top: 1px solid var(--panel-border);
}

/* Network config page */
.config-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 24px;
}

.config-section-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-bottom: 10px;
}

.inline-edit-form {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.inline-edit-form input[type="text"] { flex: 1; min-width: 140px; }

/* Topology tree */
.topology-tree {
    padding: 20px 24px;
    font-family: var(--font-mono);
    font-size: 13px;
    overflow-x: auto;
}

.tree-site {
    font-family: var(--font-ui);
    font-weight: 700;
    font-size: 15px;
    margin-bottom: 14px;
    color: var(--text-primary);
}

.tree-tunnel {
    margin-left: 0;
    padding: 10px 0 10px 16px;
    border-left: 2px solid var(--panel-border);
}

.tree-tunnel-label {
    font-family: var(--font-ui);
    font-weight: 600;
    font-size: 13.5px;
    color: var(--accent);
    margin-bottom: 6px;
}

.tree-entrance {
    margin-left: 20px;
    padding: 6px 0 6px 16px;
    border-left: 2px dashed var(--panel-border);
}

.tree-entrance-label {
    font-family: var(--font-ui);
    font-weight: 600;
    font-size: 12.5px;
    color: var(--text-primary);
    margin-bottom: 4px;
}

.tree-device {
    margin-left: 20px;
    padding: 4px 0 4px 16px;
    border-left: 2px solid var(--panel-border);
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-muted);
}

.tree-device-icon { color: var(--text-muted); }
.tree-device-name { color: var(--text-primary); font-family: var(--font-ui); font-weight: 500; }
.tree-device-meta { font-size: 11.5px; color: var(--text-muted); }

.config-note {
    background: rgba(244,103,49,0.06);
    border: 1px solid var(--panel-border);
    border-left: 3px solid var(--accent);
    border-radius: 6px;
    padding: 14px 16px;
    font-size: 12.5px;
    color: var(--text-muted);
    line-height: 1.6;
}

.config-note code {
    font-family: var(--font-mono);
    background: rgba(255,255,255,0.06);
    padding: 1px 6px;
    border-radius: 4px;
    color: var(--text-primary);
    font-size: 11.5px;
}

.code-block {
    font-family: var(--font-mono);
    font-size: 12px;
    background: var(--bg);
    border: 1px solid var(--panel-border);
    border-radius: 6px;
    padding: 14px 16px;
    overflow-x: auto;
    white-space: pre;
    color: var(--text-primary);
    line-height: 1.6;
}

.step-list {
    margin: 0;
    padding-left: 20px;
    font-size: 13px;
    color: var(--text-primary);
    line-height: 1.9;
}

@media (max-width: 800px) {
    .layout { flex-direction: column; }
    .sidebar { width: 100%; flex-direction: row; overflow-x: auto; padding: 12px; }
    .sidebar-brand { display: none; }
    .nav-link { border-left: none; border-bottom: 3px solid transparent; white-space: nowrap; }
    .nav-link.active { border-left: none; border-bottom-color: var(--accent); }
    .main { padding: 20px; }
}

/* ---- Emergency / muster mode ---------------------------------------- */
/* The one place the dashboard is allowed to shout. Uses --critical, the
   functional red every theme keeps, never the brand accent. */
.emergency-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    background: var(--critical);
    color: #fff;
    text-decoration: none;
    border-radius: 8px;
    padding: 12px 18px;
    margin-bottom: 18px;
    font-size: 14px;
    animation: emergency-pulse 1.6s ease-in-out infinite;
}
.emergency-bar-title { font-weight: 800; letter-spacing: 0.06em; }
.emergency-bar-cta { font-weight: 600; opacity: 0.9; }
@keyframes emergency-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(229,72,77,0.55); } 50% { box-shadow: 0 0 0 8px rgba(229,72,77,0); } }

.nav-link.nav-emergency.live { color: var(--critical); font-weight: 700; }
.nav-pill {
    display: inline-block;
    min-width: 18px;
    padding: 1px 7px;
    border-radius: 12px;
    background: var(--critical);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    text-align: center;
    margin-left: 6px;
}

.muster-tiles .stat-block.muster-tile-critical {
    border-color: var(--critical);
    border-left-color: var(--critical);
    background: rgba(229,72,77,0.10);
}
.muster-tile-critical .stat-value { color: var(--critical); }
.stat-value.muster-big { font-size: 44px; }

.status-row.muster-unaccounted { border-left-color: var(--critical); background: rgba(229,72,77,0.06); }
.status-row.muster-accounted { border-left-color: var(--ok); opacity: 0.7; }
.muster-phone { font-family: var(--font-mono); }
.muster-status { font-family: var(--font-mono); font-weight: 700; font-size: 12px; min-width: 120px; text-align: right; }
.muster-status.ok { color: var(--ok); }
.muster-status.missing { color: var(--critical); letter-spacing: 0.06em; }
.btn.muster-tick { background: var(--ok); color: #14120F; border: 1px solid var(--ok); font-weight: 700; }

/* RDP minimal display in emergency mode */
.minimal-emergency {
    background: var(--critical);
    color: #fff;
    border-radius: 12px;
    padding: 28px 32px;
    margin-bottom: 28px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
    animation: emergency-pulse 1.6s ease-in-out infinite;
}
.minimal-emergency .big { font-family: var(--font-mono); font-size: 5em; font-weight: 800; line-height: 1; }
.minimal-emergency .label { font-size: 1.3em; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }

/* ---- Print / Save as PDF ------------------------------------------------
   No plugin: the browser's own print dialog (Chromium/Firefox on every Pi
   and on Kerberos, fully offline) produces the PDF or drives the CUPS
   printer. These rules turn any dashboard page into a paper report:
   light palette regardless of the on-screen theme, chrome stripped,
   scroll boxes unrolled so every row prints, table headers repeated per
   page, and a one-line print header (site / filter / generated at) that
   base.html only shows here. Triggered by the "Print / Save as PDF"
   buttons on Reports, the person profile and the emergency record, or
   plain Ctrl+P anywhere. ---------------------------------------------- */
@media print {
    @page { size: A4 landscape; margin: 12mm 12mm 14mm; }

    /* Force the light tokens whatever data-theme is set: paper is white,
       and the dark palette's greys print as mud. Same values as the
       html[data-theme="light"] block, plus greys tuned for print. Later
       declaration + equal-or-higher specificity beats both theme blocks. */
    :root, html[data-theme="light"], html[data-theme="dark"] {
        --bg: #ffffff;
        --panel: #ffffff;
        --panel-border: #b8b0a4;
        --text-primary: #111111;
        --text-muted: #555555;
        --ok: #1FA36A;
        --warning: #B4780E;
        --critical: #C7333B;
        --accent: #F46731;
    }
    html, body { background: #fff !important; color: #111; }
    body { font-size: 11px; }

    /* Chrome that means nothing on paper. */
    .sidebar, .sidebar-expand-btn, .tunnel-toggle, .report-tabs, .filter-bar,
    .btn, button, form, #alert-banner, .emergency-bar, .flash-message,
    #confirm-modal, .no-print { display: none !important; }

    .layout { display: block; min-height: 0; }
    .main { padding: 0; max-width: none; margin: 0; }
    .page-header { margin-bottom: 10px; }
    .page-title { font-size: 20px; }

    .print-header {
        display: flex !important;
        justify-content: space-between;
        gap: 12px;
        font-size: 10px;
        color: #555;
        border-bottom: 1px solid #b8b0a4;
        padding-bottom: 6px;
        margin-bottom: 10px;
        font-family: var(--font-mono);
    }

    /* Unroll every scroll box so the whole table prints; repeat headers. */
    .table-scroll, .panel > div[style*="overflow"] {
        max-height: none !important;
        overflow: visible !important;
    }
    .table-scroll table thead th { position: static; }
    thead { display: table-header-group; }
    tr, .status-row, .stat-block { break-inside: avoid; }
    .panel { box-shadow: none; border: 1px solid #b8b0a4; margin-bottom: 12px; }
    .panel-header { break-after: avoid; }
    .chart-bars { overflow: visible; }

    /* Keep the colour that carries meaning: bar segments, badges, tags. */
    .chart-seg, .badge, .stat-block::before, .band-header, .status-row,
    .muster-tile-critical, .status-row.muster-unaccounted, .btn.muster-tick {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }
    a { color: inherit; text-decoration: none; }
    a[href]::after { content: none; }
    .empty-state { padding: 8px; }
}

/* Off-screen the rest of the time; the print block above overrides. */
.print-header { display: none; }
