/* ============================================================
   CronySoft NVR — Design System
   Dark, dense, professional NVR aesthetic inspired by
   industrial DVR consoles. Brand: deep blue → cyan gradients.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');

:root {
    /* ---------- Brand ---------- */
    --cs-brand:          #1d59c9;
    --cs-brand-hi:       #56a3ff;
    --cs-brand-lo:       #0d2f7a;
    --cs-accent:         #00d4ff;
    --cs-accent-warm:    #ffb547;

    /* ---------- Surfaces (dark theme) ---------- */
    --cs-bg:             #0d1117;
    --cs-bg-1:           #141a23;
    --cs-bg-2:           #1a2230;
    --cs-bg-3:           #232c3d;
    --cs-elev:           #1f2937;

    /* ---------- Lines / borders ---------- */
    --cs-line:           #2a3344;
    --cs-line-soft:      #1f2734;
    --cs-line-bright:    #3b4860;

    /* ---------- Text ---------- */
    --cs-fg:             #e6ebf5;
    --cs-fg-dim:         #a5b0c3;
    --cs-fg-muted:       #6b7689;
    --cs-fg-faint:       #4a5366;

    /* ---------- Status ---------- */
    --cs-ok:             #22c55e;
    --cs-ok-dim:         #166534;
    --cs-warn:           #f59e0b;
    --cs-warn-dim:       #78350f;
    --cs-err:            #ef4444;
    --cs-err-dim:        #7f1d1d;
    --cs-pending:        #a78bfa;

    /* ---------- Geometry ---------- */
    --cs-radius:         6px;
    --cs-radius-lg:      10px;
    --cs-sidebar-w:      230px;
    --cs-header-h:       56px;
    --cs-footer-h:       30px;

    /* ---------- Shadows ---------- */
    --cs-shadow-sm:      0 1px 0 rgba(255,255,255,0.04), 0 1px 3px rgba(0,0,0,0.4);
    --cs-shadow:         0 6px 22px rgba(0,0,0,0.4);
    --cs-shadow-pop:     0 12px 38px rgba(0,0,0,0.55);
    --cs-glow-brand:     0 0 0 1px rgba(86,163,255,0.35), 0 0 20px rgba(86,163,255,0.15);
}

/* ============================================================
   Globals
   ============================================================ */
html, body {
    background: var(--cs-bg);
    color: var(--cs-fg);
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: 14px;
    -webkit-font-smoothing: antialiased;
    margin: 0;
    min-height: 100vh;
}

* { box-sizing: border-box; }

a, .btn-link {
    color: var(--cs-brand-hi);
    text-decoration: none;
    transition: color 120ms ease;
}
a:hover { color: var(--cs-accent); }

h1, h2, h3, h4, h5, h6 {
    color: var(--cs-fg);
    font-weight: 700;
    letter-spacing: -0.01em;
}
h1 { font-size: 1.75rem; }
h4 { font-size: 1.1rem; font-weight: 600; }

.text-muted { color: var(--cs-fg-muted) !important; }
.small      { font-size: 0.8125rem; }

hr { border-color: var(--cs-line); }

/* ============================================================
   App shell layout
   ============================================================ */
.cs-app {
    display: grid;
    grid-template-rows: var(--cs-header-h) 1fr var(--cs-footer-h);
    grid-template-columns: var(--cs-sidebar-w) 1fr;
    grid-template-areas:
        "header header"
        "sidebar main"
        "footer  footer";
    min-height: 100vh;
}
.cs-header { grid-area: header;  }
.cs-sidebar { grid-area: sidebar; }
.cs-main   { grid-area: main;    padding: 22px 28px; overflow: auto; }
.cs-footer { grid-area: footer;  }

/* ---------- Header ---------- */
.cs-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: linear-gradient(180deg, #0e1726 0%, #0a1220 100%);
    border-bottom: 1px solid var(--cs-line);
    padding: 0 20px;
    position: relative;
}
.cs-header::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 2px;
    background: linear-gradient(90deg,
                  transparent 0%,
                  var(--cs-brand) 20%,
                  var(--cs-accent) 50%,
                  var(--cs-brand) 80%,
                  transparent 100%);
    opacity: 0.45;
}
.cs-header .brand {
    display: flex;
    align-items: center;
    gap: 12px;
}
/* Bootstrap defaults <code> to a magenta/pink that visually reads as an
   error or warning. We use <code> a LOT for paths, IDs, mount points
   that are neutral data — override the color to a calm desaturated
   slate so it stops shouting from every table. */
code, kbd, samp {
    color: var(--cs-fg) !important;
    background: rgba(255, 255, 255, 0.05);
    padding: 1px 6px;
    border-radius: 3px;
    font-size: 0.85em;
    font-family: 'JetBrains Mono', 'DejaVu Sans Mono', ui-monospace, Consolas, monospace;
}
pre code { background: none; padding: 0; }

.cs-header .brand img {
    height: 28px;
    /* The brand PNG ships in its original color palette so the same file
       can be reused on light + dark surfaces. On the dark header we want
       it pure white — `brightness(0) invert(1)` turns ANY input image
       into a clean white silhouette without re-exporting the asset. */
    filter: brightness(0) invert(1);
    opacity: 0.95;
}
.cs-header .brand-sub {
    font-size: 0.7rem;
    letter-spacing: 0.18em;
    color: var(--cs-fg-dim);
    text-transform: uppercase;
    padding-left: 12px;
    border-left: 1px solid var(--cs-line);
}
.cs-header .head-meta {
    display: flex;
    align-items: center;
    gap: 18px;
    font-size: 0.8125rem;
    color: var(--cs-fg-dim);
}
.cs-header .head-clock {
    font-variant-numeric: tabular-nums;
    color: var(--cs-fg);
    letter-spacing: 0.04em;
}

/* ---------- Sidebar ---------- */
.cs-sidebar {
    background: linear-gradient(180deg, #111927 0%, #0c121d 100%);
    border-right: 1px solid var(--cs-line);
    padding: 16px 10px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.cs-sidebar .group-label {
    font-size: 0.65rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--cs-fg-faint);
    padding: 14px 14px 6px;
}
.cs-sidebar a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 14px;
    border-radius: var(--cs-radius);
    color: var(--cs-fg-dim);
    font-weight: 500;
    transition: background 120ms ease, color 120ms ease;
    position: relative;
}
.cs-sidebar a:hover {
    background: rgba(86, 163, 255, 0.06);
    color: var(--cs-fg);
}
.cs-sidebar a.active {
    background: linear-gradient(90deg, rgba(29, 89, 201, 0.20), rgba(29, 89, 201, 0.05));
    color: var(--cs-fg);
}
.cs-sidebar a.active::before {
    content: "";
    position: absolute;
    left: 0; top: 18%; bottom: 18%;
    width: 3px; border-radius: 0 3px 3px 0;
    background: linear-gradient(180deg, var(--cs-brand-hi), var(--cs-brand));
}
.cs-sidebar a .ico {
    width: 18px; height: 18px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--cs-fg-muted);
    flex: 0 0 18px;
}
.cs-sidebar a.active .ico, .cs-sidebar a:hover .ico { color: var(--cs-accent); }

/* ---------- Footer status strip ---------- */
.cs-footer {
    background: #0a111c;
    border-top: 1px solid var(--cs-line);
    color: var(--cs-fg-muted);
    font-size: 0.75rem;
    padding: 0 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    letter-spacing: 0.02em;
}
.cs-footer .blob {
    display: inline-block;
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--cs-ok);
    box-shadow: 0 0 8px var(--cs-ok);
    margin-right: 6px;
}

/* ============================================================
   Cards, panels, surfaces
   ============================================================ */
.card, .cs-card {
    background: var(--cs-bg-1);
    border: 1px solid var(--cs-line);
    border-radius: var(--cs-radius-lg);
    color: var(--cs-fg);
    box-shadow: var(--cs-shadow-sm);
}
.card-body, .card-header { background: transparent; }
.card-header {
    border-bottom: 1px solid var(--cs-line);
    color: var(--cs-fg);
    font-weight: 600;
}

.cs-panel {
    background: linear-gradient(180deg, rgba(255,255,255,0.015), transparent),
                var(--cs-bg-2);
    border: 1px solid var(--cs-line);
    border-radius: 8px;
    padding: 22px 24px;
}

/* ----- Dashboard KPI cards ----- */
.cs-stat {
    background: linear-gradient(180deg, rgba(255,255,255,0.02), transparent),
                var(--cs-bg-2);
    border: 1px solid var(--cs-line);
    border-radius: 8px;
    padding: 20px 22px;
    position: relative;
    overflow: hidden;
    min-height: 116px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    transition: border-color 120ms, transform 120ms;
}
.cs-stat:hover { border-color: rgba(86,163,255,0.35); }
.cs-stat .label {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    color: var(--cs-fg-muted);
    text-transform: uppercase;
    margin-bottom: 4px;
}
.cs-stat .value {
    font-size: 2.4rem;
    font-weight: 700;
    color: var(--cs-fg);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}
.cs-stat .delta {
    font-size: 0.78rem;
    color: var(--cs-fg-muted);
    margin-top: 10px;
    line-height: 1.3;
}
.cs-stat.is-ok    .value { color: #6ee7a8; }
.cs-stat.is-err   .value { color: #f87171; }
.cs-stat.is-warn  .value { color: #fbbf24; }
.cs-stat::after {
    content: "";
    position: absolute;
    right: -40px; top: -40px;
    width: 120px; height: 120px;
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(86,163,255,0.10), transparent);
    pointer-events: none;
}
.cs-stat.is-ok::after   { background: radial-gradient(closest-side, rgba(110,231,168,0.10), transparent); }
.cs-stat.is-err::after  { background: radial-gradient(closest-side, rgba(248,113,113,0.10), transparent); }
.cs-stat.is-warn::after { background: radial-gradient(closest-side, rgba(251,191,36,0.10), transparent); }

/* ============================================================
   Buttons (override Bootstrap)
   ============================================================ */
.btn { border-radius: var(--cs-radius); font-weight: 600; letter-spacing: 0.01em; }
.btn-primary {
    background: linear-gradient(180deg, #2a72e0, #1849b1);
    border: 1px solid #2a72e0;
    color: #fff;
    box-shadow: 0 1px 0 rgba(255,255,255,0.1) inset, 0 1px 0 rgba(0,0,0,0.35);
}
.btn-primary:hover, .btn-primary:focus {
    background: linear-gradient(180deg, #3380ee, #1f56c6);
    border-color: #3380ee;
    box-shadow: var(--cs-glow-brand);
}
.btn-success {
    background: linear-gradient(180deg, #2ec27e, #189563);
    border: 1px solid #2ec27e; color: #fff;
}
.btn-danger {
    background: linear-gradient(180deg, #ef4444, #b91c1c);
    border: 1px solid #ef4444; color: #fff;
}
.btn-secondary, .btn-outline-secondary {
    background: var(--cs-bg-2);
    border: 1px solid var(--cs-line-bright);
    color: var(--cs-fg);
}
.btn-secondary:hover, .btn-outline-secondary:hover {
    background: var(--cs-bg-3);
    border-color: var(--cs-line-bright);
    color: var(--cs-fg);
}
.btn-outline-primary {
    background: transparent;
    border: 1px solid rgba(86,163,255,0.4);
    color: var(--cs-brand-hi);
}
.btn-outline-primary:hover {
    background: rgba(86,163,255,0.1);
    border-color: var(--cs-brand-hi);
    color: var(--cs-fg);
}
.btn-link { background: none; border: none; padding: 0; }

/* ============================================================
   Forms
   ============================================================ */
.form-control, .form-select, .form-control:focus, .form-select:focus {
    background: var(--cs-bg-2);
    border: 1px solid var(--cs-line);
    color: var(--cs-fg);
    border-radius: var(--cs-radius);
    box-shadow: none;
}
.form-control:focus, .form-select:focus {
    border-color: var(--cs-brand-hi);
    box-shadow: 0 0 0 3px rgba(86,163,255,0.18);
}
.form-control::placeholder { color: var(--cs-fg-faint); }
.form-label { color: var(--cs-fg-dim); font-size: 0.78rem; letter-spacing: 0.04em; font-weight: 600; }
.form-check-label { color: var(--cs-fg); }
.form-check-input {
    background: var(--cs-bg-2);
    border-color: var(--cs-line-bright);
}
.form-check-input:checked {
    background: var(--cs-brand);
    border-color: var(--cs-brand-hi);
}

/* ============================================================
   Tables
   ============================================================ */
.table {
    color: var(--cs-fg);
    border-color: var(--cs-line);
    --bs-table-bg: transparent;
}
.table thead th {
    background: linear-gradient(180deg, var(--cs-bg-2), var(--cs-bg-1));
    color: var(--cs-fg-dim);
    border-bottom: 1px solid var(--cs-line-bright);
    font-weight: 600;
    font-size: 0.78rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.table tbody tr {
    border-bottom: 1px solid var(--cs-line-soft);
}
.table tbody tr:hover {
    background: rgba(86, 163, 255, 0.04);
}
.table tbody td {
    border: none;
    vertical-align: middle;
    padding: 11px 12px;
}

/* ============================================================
   Badges
   ============================================================ */
.badge {
    font-weight: 600;
    letter-spacing: 0.04em;
    border-radius: 999px;
    padding: 0.32em 0.7em;
}
.badge.bg-success    { background: var(--cs-ok)    !important; }
.badge.bg-danger     { background: var(--cs-err)   !important; }
.badge.bg-warning    { background: var(--cs-warn)  !important; color: #1c1303 !important; }
.badge.bg-secondary  { background: #344256        !important; color: var(--cs-fg-dim) !important; }
.badge.bg-info       { background: #0891b2        !important; }
.badge.bg-primary    { background: var(--cs-brand) !important; }
.badge.bg-light      { background: var(--cs-bg-3) !important; color: var(--cs-fg-dim) !important; }

/* ============================================================
   Alerts
   ============================================================ */
.alert {
    background: var(--cs-bg-1);
    border: 1px solid var(--cs-line);
    color: var(--cs-fg);
    border-radius: var(--cs-radius);
}
.alert-info     { background: rgba(8, 145, 178, 0.12);  border-color: rgba(8, 145, 178, 0.4); color: #cffafe; }
.alert-warning  { background: rgba(245, 158, 11, 0.10); border-color: rgba(245, 158, 11, 0.4); color: #fde68a; }
.alert-danger   { background: rgba(239, 68, 68, 0.10);  border-color: rgba(239, 68, 68, 0.4);  color: #fecaca; }
.alert-success  { background: rgba(34, 197, 94, 0.10);  border-color: rgba(34, 197, 94, 0.4);  color: #bbf7d0; }

/* ============================================================
   Live View NVR grid (the headline view)
   ============================================================ */
.nvr-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 10px 14px;
    background: var(--cs-bg-1);
    border: 1px solid var(--cs-line);
    border-radius: var(--cs-radius-lg);
    margin-bottom: 14px;
}
.nvr-toolbar .left,
.nvr-toolbar .right {
    display: flex; align-items: center; gap: 10px;
}
.nvr-layout {
    display: inline-flex; gap: 4px;
    background: var(--cs-bg-2);
    border: 1px solid var(--cs-line);
    border-radius: var(--cs-radius);
    padding: 3px;
}
.nvr-layout button {
    background: transparent;
    border: 0;
    color: var(--cs-fg-muted);
    width: 30px; height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 4px;
    cursor: pointer;
    transition: all 120ms;
}
.nvr-layout button:hover { color: var(--cs-fg); background: var(--cs-bg-3); }
.nvr-layout button.active {
    background: linear-gradient(180deg, #2a72e0, #1849b1);
    color: #fff;
}

/* LIVE / SNAP toggle: wider buttons so the labels breathe, and a tiny
 * separator gap so the two pills read as distinct choices instead of
 * one mashed-up "LIVESNAP" word. */
.nvr-layout.nvr-mode-toggle {
    gap: 6px;
    padding: 4px;
    margin-left: 8px;
}
.nvr-layout.nvr-mode-toggle button {
    width: auto;
    min-width: 52px;
    padding: 0 10px;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.5px;
}
.nvr-quality {
    margin-left: 8px;
    background: var(--cs-bg-2);
    border: 1px solid var(--cs-line);
    border-radius: var(--cs-radius);
    color: var(--cs-fg);
    padding: 4px 10px;
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
}

/* Inline help-glyph that surfaces a native browser tooltip on hover.
 * Used in section headers to drop a contextual "what does this do?"
 * without making the operator find docs. */
.cs-help {
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--cs-bg-3);
    border: 1px solid var(--cs-line);
    color: var(--cs-fg-muted);
    font-size: 0.72rem;
    font-weight: 700;
    margin-left: 8px;
    cursor: help;
    vertical-align: middle;
}
.cs-help:hover { color: var(--cs-fg); border-color: var(--cs-fg-muted); }

/* Recording-timeline heat shading. Each band represents one hour of
 * coverage; alpha encodes density (0..1 -> 0.20..0.75 alpha in JS).
 * Without these positioning rules, the bands fall out of layout and
 * the operator scrubs blind over a 24h column. */
.cs-tl-heat {
    position: absolute;
    left: 0; right: 0; top: 0; bottom: 0;
    pointer-events: none;
    z-index: 0;
}
.cs-tl-heat-band {
    position: absolute;
    left: 0; right: 0;
    /* top + height come inline per-band from player.js */
}

/* Legal pages — Terms & Conditions, Privacy Policy. Long-form text on
 * a reading-optimized centered column with the same dark theme as the
 * rest of the portal. */
.cs-legal-page {
    min-height: 100vh;
    padding: 32px 16px 64px;
    display: flex;
    justify-content: center;
    background: var(--cs-bg-1);
}
.cs-legal-card {
    max-width: 820px;
    width: 100%;
    background: var(--cs-bg-2);
    border: 1px solid var(--cs-line);
    border-radius: var(--cs-radius);
    padding: 36px 40px;
    color: var(--cs-fg);
    line-height: 1.55;
}
.cs-legal-card h1 { font-size: 1.6rem; margin: 0 0 4px; }
.cs-legal-card h2 { font-size: 1.15rem; margin-top: 28px; padding-bottom: 4px; border-bottom: 1px solid var(--cs-line); }
.cs-legal-card h3 { font-size: 0.98rem; margin-top: 18px; color: var(--cs-fg-muted); }
.cs-legal-card ul, .cs-legal-card ol { padding-left: 22px; }
.cs-legal-card li { margin: 4px 0; }
.cs-legal-card code { background: var(--cs-bg-3); padding: 1px 5px; border-radius: 3px; font-size: 0.9em; }

/* Account-level lifecycle banner (trial ending / expired / deletion pending). */
.cs-account-banner {
    display: flex; gap: 10px; align-items: center;
    padding: 10px 16px;
    border-radius: var(--cs-radius);
    margin-bottom: 16px;
    font-size: 0.9rem;
}
.cs-account-banner.banner-warning {
    background: rgba(255, 193, 7, 0.12);
    border: 1px solid rgba(255, 193, 7, 0.35);
    color: #ffd54f;
}
.cs-account-banner.banner-danger {
    background: rgba(220, 53, 69, 0.12);
    border: 1px solid rgba(220, 53, 69, 0.40);
    color: #ff8a95;
}
.cs-account-banner a { color: inherit; text-decoration: underline; }

/* Public /status page incident styling. */
.status-pill {
    display: inline-block;
    padding: 2px 10px; border-radius: 999px;
    font-size: 0.75rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.5px;
    margin-left: 8px;
}
.status-pill.status-operational  { background: #1f7a3e; color: #fff; }
.status-pill.status-degraded     { background: #b78104; color: #fff; }
.status-pill.status-outage       { background: #b3331f; color: #fff; }
.status-pill.status-investigating{ background: #6f42c1; color: #fff; }
.status-pill.status-resolved     { background: #444; color: #ddd; }

.status-incident {
    background: var(--cs-bg-3);
    border: 1px solid var(--cs-line);
    border-radius: var(--cs-radius);
    padding: 12px 14px;
    margin: 10px 0;
}
.status-incident-head { display: flex; align-items: center; gap: 8px; }

/* ----------------------------------------------------------------
 * Mobile / phone-portrait layout. Below ~700 px the desktop grid
 * crams every tile into a thumbnail-sized smear. Two adjustments:
 *
 *   1. Collapse every grid-cols-N to 1-column vertical stack, with
 *      a tap-to-expand interaction (already the operator's habit
 *      on a small screen — pick one camera at a time).
 *   2. Hide the sidebar by default; header gets a hamburger that
 *      slides it in. (Sidebar nav itself unchanged structurally.)
 * ---------------------------------------------------------------- */
@media (max-width: 700px) {
    .cs-app {
        grid-template-columns: 1fr;       /* drop the sidebar column */
        grid-template-rows: auto 1fr auto;
        grid-template-areas: "header" "main" "footer";
    }
    .cs-sidebar {
        position: fixed;
        top: 56px; bottom: 0; left: 0;
        width: 240px; z-index: 50;
        transform: translateX(-100%);
        transition: transform 200ms ease;
        background: var(--cs-bg-2);
        border-right: 1px solid var(--cs-line);
    }
    .cs-app.cs-sidebar-open .cs-sidebar { transform: translateX(0); }

    .cs-main { padding: 12px; }

    /* All grid sizes collapse to a single vertical column on phones.
     * Stacking is the only sane way to actually see what's on screen
     * at <360 px tile widths. */
    .nvr-grid.cols-2,
    .nvr-grid.cols-3,
    .nvr-grid.cols-4,
    .nvr-grid.cols-5 {
        grid-template-columns: 1fr;
        grid-template-rows: none;
        grid-auto-rows: 220px;
    }

    /* Header trims to logo + menu toggle. */
    .cs-header .brand-sub { display: none; }

    /* Toolbar buttons stack with wrap so LIVE/SNAP + grid icons
     * don't horizontal-scroll off the right edge. */
    .nvr-toolbar { flex-wrap: wrap; row-gap: 6px; }
}

.nvr-grid {
    display: grid;
    gap: 2px;
    background: #000;
    border: 1px solid var(--cs-line);
    border-radius: var(--cs-radius-lg);
    overflow: hidden;
    aspect-ratio: 16 / 9;
    width: 100%;
}
.nvr-grid.cols-1 { grid-template-columns: 1fr;                 grid-template-rows: 1fr; }
.nvr-grid.cols-2 { grid-template-columns: repeat(2, 1fr);      grid-template-rows: 1fr; }
.nvr-grid.cols-3 { grid-template-columns: repeat(3, 1fr);      grid-template-rows: repeat(3, 1fr); }
.nvr-grid.cols-4 { grid-template-columns: repeat(4, 1fr);      grid-template-rows: repeat(4, 1fr); }
.nvr-grid.cols-5 { grid-template-columns: repeat(5, 1fr);      grid-template-rows: repeat(5, 1fr); }

.nvr-tile {
    position: relative;
    background: #000;
    overflow: hidden;
}
.nvr-tile > iframe,
.nvr-tile > img,
.nvr-tile > video {
    width: 100%; height: 100%;
    /* `contain` (not `cover`) keeps the whole camera frame visible. With
       4:3 sub-streams in a near-square tile, `cover` was zooming in
       vertically and chopping the left/right sides — including any POS
       overlay that lives at the bottom corners. Letterbox bars are fine;
       the underlying tile background is already black. */
    object-fit: contain;
    background: #000;
    display: block;
    border: 0;
}
/* <video-stream> from go2rtc — fill the tile, hide any UI it provides */
.nvr-tile > video-stream {
    width: 100%; height: 100%;
    display: block;
    background: #000;
    position: relative;
}
.nvr-tile > video-stream video {
    width: 100% !important;
    height: 100% !important;
    /* `contain` shows the entire camera frame undistorted with black bars
       on whatever axis doesn't match the tile box. Was `cover`, which
       cropped vertically when a 4:3 sub-stream landed in a near-square
       tile — losing the POS overlay corner and visually stretching the
       face/scene. Recordings already uses `contain` for the same reason. */
    object-fit: contain !important;
    background: #000;
    pointer-events: none;            /* no click-to-pause */
}
.nvr-tile > video-stream .info,
.nvr-tile > video-stream .mode,
.nvr-tile > video-stream .status {
    display: none !important;
}

/* Bare <video> tiles (snapshot/jpeg fallback) */
.nvr-tile > video {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    background: #000;
    pointer-events: none;
}

/* Crush every variant of the native browser player chrome on WebKit/Blink */
.nvr-tile video::-webkit-media-controls,
.nvr-tile video::-webkit-media-controls-enclosure,
.nvr-tile video::-webkit-media-controls-panel,
.nvr-tile video::-webkit-media-controls-overlay-play-button,
.nvr-tile video::-webkit-media-controls-start-playback-button,
.nvr-tile video::-webkit-media-controls-play-button,
.nvr-tile video::-webkit-media-controls-timeline,
.nvr-tile video::-webkit-media-controls-current-time-display,
.nvr-tile video::-webkit-media-controls-time-remaining-display,
.nvr-tile video::-webkit-media-controls-mute-button,
.nvr-tile video::-webkit-media-controls-volume-slider,
.nvr-tile video::-webkit-media-controls-fullscreen-button {
    display: none !important;
    -webkit-appearance: none !important;
}

/* Connection state badges (driven by data-cs-status on the tile via JS) */
.nvr-tile[data-cs-status="connecting"]::after {
    content: "CONNECTING…";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--cs-fg-muted);
    font-size: 0.7rem;
    letter-spacing: 0.3em;
    pointer-events: none;
}
.nvr-tile[data-cs-status="error"]::after {
    content: "NO SIGNAL";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--cs-err);
    font-size: 0.75rem;
    letter-spacing: 0.3em;
    pointer-events: none;
    background: rgba(127, 29, 29, 0.15);
}
.nvr-tile .corner-tl,
.nvr-tile .corner-tr,
.nvr-tile .corner-bl,
.nvr-tile .corner-br {
    position: absolute;
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    padding: 4px 8px;
    background: linear-gradient(180deg, rgba(0,0,0,0.7), rgba(0,0,0,0.3));
    text-shadow: 0 1px 2px rgba(0,0,0,0.9);
    pointer-events: none;
    user-select: none;
}
.nvr-tile .corner-tl { top: 0; left: 0; border-bottom-right-radius: 6px; }
.nvr-tile .corner-tr { top: 0; right: 0; border-bottom-left-radius: 6px; }
.nvr-tile .corner-bl { bottom: 0; left: 0; border-top-right-radius: 6px; }
.nvr-tile .corner-br { bottom: 0; right: 0; border-top-left-radius: 6px; }
.nvr-tile .corner-tr {
    background: rgba(13, 47, 122, 0.7);
}
.nvr-tile .live-dot {
    display: inline-block;
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--cs-err);
    box-shadow: 0 0 6px var(--cs-err);
    margin-right: 5px;
    animation: cs-pulse 1.6s ease-in-out infinite;
}
@keyframes cs-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
}

.nvr-tile .empty {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: #2d3848;
    font-size: 11px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
}
.nvr-tile .gear {
    position: absolute;
    top: 4px; right: 4px;
    background: rgba(0,0,0,0.6);
    border: 1px solid rgba(255,255,255,0.15);
    width: 28px; height: 28px;
    border-radius: 4px;
    color: #fff;
    opacity: 0;
    transition: opacity 120ms;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
}
.nvr-tile:hover .gear { opacity: 1; }

/* Fullscreen button — sits next to the gear so both action icons hover-in
   together. Same pattern as Recordings' .rec-tile-fs. Hidden until hover
   so the corner badges (channel name, timestamp, bandwidth) stay readable. */
.nvr-tile .nvr-fs {
    position: absolute;
    top: 4px; right: 38px;          /* leave space for the gear at right: 4px */
    background: rgba(0,0,0,0.6);
    border: 1px solid rgba(255,255,255,0.15);
    width: 28px; height: 28px;
    border-radius: 4px;
    color: #fff;
    opacity: 0;
    transition: opacity 120ms, background 120ms, transform 120ms;
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    padding: 0;
    line-height: 1;
    font-size: 0.95rem;
}
.nvr-tile:hover .nvr-fs { opacity: 1; }
.nvr-tile .nvr-fs:hover { background: rgba(86,163,255,0.9); transform: scale(1.08); }
.nvr-tile:fullscreen .nvr-fs,
.nvr-tile:fullscreen .gear { opacity: 1; }   /* keep visible in fullscreen */

/* When the live tile goes fullscreen we want the video to fill the screen
   edge-to-edge with letterbox (object-fit: contain) — same as recordings.
   The tile wrapper needs to release any width/height constraints from the
   parent grid so the browser's native fullscreen layer can take over. */
.nvr-tile:fullscreen {
    width: 100vw;
    height: 100vh;
    background: #000;
}
.nvr-tile:fullscreen video,
.nvr-tile:fullscreen video-stream::part(video),
.nvr-tile:fullscreen img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #000;
}

/* ============================================================
   Login page
   ============================================================ */
.cs-auth-shell {
    min-height: 100vh;
    background:
        radial-gradient(ellipse at 20% 30%, rgba(86,163,255,0.10), transparent 50%),
        radial-gradient(ellipse at 80% 70%, rgba(0,212,255,0.08), transparent 50%),
        linear-gradient(180deg, #0a111c, #050810);
    display: flex; align-items: center; justify-content: center;
    padding: 20px;
}
.cs-auth-card {
    width: 100%;
    max-width: 420px;
    background: linear-gradient(180deg, #131b2a, #0d1320);
    border: 1px solid var(--cs-line);
    border-radius: 14px;
    padding: 32px 32px 28px;
    box-shadow: var(--cs-shadow-pop);
    position: relative;
}
.cs-auth-card::before {
    content: "";
    position: absolute;
    inset: -1px;
    border-radius: 14px;
    padding: 1px;
    background: linear-gradient(135deg, rgba(86,163,255,0.5), transparent 40%, transparent 70%, rgba(0,212,255,0.3));
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor; mask-composite: exclude;
    pointer-events: none;
}
.cs-auth-card .brand { display: flex; justify-content: center; margin-bottom: 22px; }
.cs-auth-card .brand img {
    height: 38px;
    /* Same white-out as the header. The login card uses the same dark
       glass background so the colorful original PNG would clash. */
    filter: brightness(0) invert(1);
    opacity: 0.95;
}
.cs-auth-card h3 { text-align: center; margin-bottom: 4px; }
.cs-auth-card .sub { text-align: center; color: var(--cs-fg-muted); font-size: 0.85rem; margin-bottom: 20px; }

/* ============================================================
   Modals — flatten Bootstrap defaults to match dark theme
   ============================================================ */
.modal-content {
    background: var(--cs-bg-1);
    border: 1px solid var(--cs-line-bright);
    color: var(--cs-fg);
    border-radius: var(--cs-radius-lg);
}
.modal-header, .modal-footer {
    border-color: var(--cs-line);
    background: transparent;
}
.btn-close { filter: invert(1) brightness(1.6); }

/* ============================================================
   Misc utility
   ============================================================ */
.kbd-token {
    font-family: 'JetBrains Mono', Consolas, monospace;
    background: linear-gradient(180deg, #1f2734, #131a26);
    border: 1px solid var(--cs-line-bright);
    color: var(--cs-accent);
    padding: 4px 10px;
    border-radius: 5px;
    letter-spacing: 0.15em;
    font-weight: 700;
    box-shadow: 0 0 0 1px rgba(0,212,255,0.1) inset;
}

/* Subtle scrollbar */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: var(--cs-line);
    border-radius: 5px;
    border: 2px solid var(--cs-bg);
}
::-webkit-scrollbar-thumb:hover { background: var(--cs-line-bright); }

/* ============================================================
   Global navigation progress bar.
   Driven by JavaScript in /js/nav-progress.js — when a Blazor
   anchor click starts a navigation we add `body.nav-busy` and
   pulse this bar at the top of the page. Removed when the new
   page's first DOM update lands.
   ============================================================ */
.nav-progress {
    position: fixed;
    top: 0; left: 0; right: 0;
    height: 2px;
    background: transparent;
    z-index: 9999;
    pointer-events: none;
}
.nav-progress::before {
    content: "";
    position: absolute;
    top: 0; left: -25%;
    width: 25%; height: 100%;
    background: linear-gradient(90deg,
        transparent, var(--cs-brand-hi, #56a3ff), transparent);
    box-shadow: 0 0 8px rgba(86,163,255,0.6);
    animation: nav-progress-slide 0.9s linear infinite;
    opacity: 0;
    transition: opacity 80ms;
}
body.nav-busy .nav-progress::before { opacity: 1; }
@keyframes nav-progress-slide {
    from { left: -25%; }
    to   { left: 100%; }
}

/* ============================================================
   Reusable visible loading block — anywhere that says "Loading…"
   can be wrapped in <div class="cs-loading">…</div> for a clean
   spinner + label instead of tiny easy-to-miss italics.
   ============================================================ */
.cs-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 32px 16px;
    color: var(--cs-fg-muted);
    font-size: 0.9rem;
}
.cs-loading::before {
    content: "";
    width: 18px; height: 18px;
    border: 2px solid rgba(86,163,255,0.18);
    border-top-color: var(--cs-brand-hi, #56a3ff);
    border-radius: 50%;
    animation: cs-spin 0.9s linear infinite;
}
@keyframes cs-spin { to { transform: rotate(360deg); } }

/* Hide the default page#blazor-error-ui look — restyle it */
#blazor-error-ui {
    background: var(--cs-err);
    color: #fff;
    border-top: 1px solid #f87171;
}

/* Bootstrap layout flex utilities tweak: the old "page" class from
   the template — neutralize it since we use cs-app instead. */
.page { display: contents; }
.sidebar, .top-row.ps-3.navbar { display: none; }
main { padding: 0; }

/* POS text overlay strip — STRUCTURAL rules only. Position, color, font
   size, background, padding all come from `OverlayStyleDto.ToInlineCss()`
   so this HTML overlay visually matches what the DVR's ffmpeg drawtext
   burns into recorded MP4 pixels.
   Single source of truth: `Stores.PosOverlayStyleJson`. Both this Razor
   page and the agent's overlay_style.py parse the same JSON. Changing
   colors/position below would diverge live vs recorded — keep this block
   to layout invariants only. */
.nvr-tile .pos-overlay {
    position: absolute;
    max-width: 92%;
    line-height: 1.25;
    border-radius: 4px;
    font-family: 'JetBrains Mono', 'DejaVu Sans Mono', 'Cascadia Code', ui-monospace, Consolas, monospace;
    text-shadow: 0 1px 2px rgba(0,0,0,0.8);
    pointer-events: none;
    z-index: 5;
}
.nvr-tile .pos-overlay > div { white-space: nowrap; }

.nvr-tile .pos-overlay .pos-overlay-meta {
    margin-top: 4px;
    font-size: 0.7em;            /* relative to parent inline-style font-size */
    opacity: 0.65;
    letter-spacing: 0.04em;
}


/* ---------------------------------------------------------------- */
/* Live View — custom-layout edit mode                              */
/* ---------------------------------------------------------------- */
.nvr-customize-btn {
    background: transparent;
    border: 1px solid var(--cs-line);
    color: var(--cs-fg);
    border-radius: 4px;
    padding: 4px 10px;
    font-size: 0.85rem;
    cursor: pointer;
    margin-right: 10px;
    transition: background 0.12s, border-color 0.12s;
}
.nvr-customize-btn:hover {
    border-color: var(--cs-brand-hi, #56a3ff);
    background: rgba(86, 163, 255, 0.08);
}
.nvr-customize-btn.active {
    background: var(--cs-brand-hi, #56a3ff);
    color: #fff;
    border-color: var(--cs-brand-hi, #56a3ff);
}

/* Overlay on top of the video tile when in edit mode. Translucent
   dark backdrop so the live video still shows through but the
   controls are clearly the "active" surface. */
.nvr-edit-overlay {
    position: absolute;
    top: 8px;
    left: 8px;
    right: 8px;
    z-index: 50;
    background: rgba(0, 0, 0, 0.75);
    border-radius: 6px;
    padding: 8px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    pointer-events: auto;
}
.nvr-edit-overlay-center {
    top: 50%;
    transform: translateY(-50%);
    bottom: auto;
}
.nvr-edit-pick {
    background: #fff;
    color: #000;
    font-size: 0.82rem;
}
.nvr-edit-arrows {
    display: flex;
    justify-content: space-between;
    gap: 4px;
}
.nvr-edit-arrows .btn {
    flex: 1;
    font-size: 0.75rem;
    padding: 2px 4px;
}
.nvr-tile-empty {
    background: #1a2230;
    border: 2px dashed var(--cs-line, #2a3344);
}
