/* ============================================================================
   DZASS Portal — custom overrides on top of the Silicon UI Kit (Bootstrap 5).
   Kept intentionally small; Silicon provides the bulk of the styling.
   Later phases append chart-host / sentinel rules here.
   ============================================================================ */

/* --- Base font: Google Sans (applies globally via Bootstrap variable) ------ */
:root {
    --bs-body-font-family: "Google Sans", sans-serif;
}
body {
    font-family: "Google Sans", sans-serif;
    font-optical-sizing: auto;
    font-variation-settings: "GRAD" 0;
}

/* --- Authentication background (subtle) ----------------------------------- */
.authentication-bg {
    background-color: #f6f8fb;
    background-image: linear-gradient(135deg, #f6f8fb 0%, #eef2f7 100%);
}

/* Login card sits comfortably centered with a little breathing room. */
.authentication-bg .card {
    border: 0;
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.06);
}

.login-methods {
    width: 100%;
    max-width: 400px;
}

/* Google Identity generated-button styling, matching the implemented VMMPortal flow. */
.gsi-material-button {
    -moz-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    -webkit-appearance: none;
    align-items: center;
    background-color: #fff;
    background-image: none;
    border: 1px solid #747775;
    border-radius: 4px;
    box-sizing: border-box;
    color: #1f1f1f;
    cursor: pointer;
    display: block;
    font-family: "Roboto", Arial, sans-serif;
    font-size: 14px;
    height: 40px;
    letter-spacing: 0.25px;
    outline: none;
    overflow: hidden;
    padding: 0 12px;
    position: relative;
    text-align: center;
    text-decoration: none;
    transition: background-color 0.218s, border-color 0.218s, box-shadow 0.218s;
    vertical-align: middle;
    white-space: nowrap;
    width: 100%;
}

.gsi-material-button .gsi-material-button-icon {
    height: 20px;
    margin-right: 10px;
    min-width: 20px;
    width: 20px;
}

.gsi-material-button .gsi-material-button-content-wrapper {
    align-items: center;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    height: 100%;
    justify-content: space-between;
    position: relative;
    width: 100%;
}

.gsi-material-button .gsi-material-button-contents {
    flex-grow: 1;
    font-family: "Roboto", Arial, sans-serif;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: top;
}

.gsi-material-button .gsi-material-button-state {
    bottom: 0;
    left: 0;
    opacity: 0;
    position: absolute;
    right: 0;
    top: 0;
    transition: opacity 0.218s;
}

.gsi-material-button:active .gsi-material-button-state,
.gsi-material-button:focus .gsi-material-button-state {
    background-color: #303030;
    opacity: 12%;
}

.gsi-material-button:hover {
    box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3),
        0 1px 3px 1px rgba(60, 64, 67, 0.15);
    color: #1f1f1f;
    text-decoration: none;
}

.gsi-material-button:hover .gsi-material-button-state {
    background-color: #303030;
    opacity: 8%;
}

/* Left-aligned icon inside the auth inputs. */
.input-custom-icon .form-control {
    padding-left: 2.4rem;
}
.input-custom-icon .bx {
    font-size: 1.15rem;
    color: #9aa6b2;
}

/* --- Spinner utility ------------------------------------------------------ */
.spinner-portal {
    width: 3rem;
    height: 3rem;
}

/* ============================================================================
   Dashboard (Phase 6)
   ============================================================================ */

/* Device-card title: truncate long device names to one line (Alt post preview h4 pattern). */
#device-grid .card .h4 {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* List view: full-width horizontal rows. Every direct column child spans the whole grid. */
#device-grid.view-list > .col,
#device-grid.view-list > [class*="col-"] {
    flex: 0 0 100%;
    max-width: 100%;
}

/* Mobile list rows keep device identity on the left and a compact, fixed-width
   status stack on the right. The last-seen time sits below the device name. */
#device-grid.view-list .card-body {
    display: flex;
    align-items: center;
    gap: 1rem;
    min-height: 6rem;
}
#device-grid.view-list .grid-mobile-identity {
    align-self: stretch;
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
}
#device-grid.view-list .grid-cell-seen {
    margin-top: 0.35rem;
}
#device-grid.view-list .grid-mobile-status {
    display: flex;
    flex: 0 0 auto;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.35rem;
    text-align: right;
}
/* The title already falls back to the serial number when a device has no name. */
#device-grid.view-list .grid-cell-sn {
    display: none !important;
}

/* Desktop (lg+): flatten the mobile groups and keep device identity together
   in the first column, with the status directly below the name. */
@media (min-width: 992px) {
    #device-grid.view-list .card-body {
        display: grid;
        grid-template-columns: minmax(14rem, 1fr) 11rem 8rem 6rem 10rem;
        grid-template-rows: auto auto;
        align-items: center;
        column-gap: 1.25rem;
        min-height: 0;
    }
    #device-grid.view-list .grid-mobile-identity,
    #device-grid.view-list .grid-mobile-status {
        display: contents;
    }
    #device-grid.view-list .grid-cell-name {
        grid-column: 1;
        grid-row: 1;
    }
    #device-grid.view-list .grid-cell-status {
        grid-column: 1;
        grid-row: 2;
        justify-self: start;
    }
    #device-grid.view-list .grid-cell-signal {
        grid-column: 2;
        grid-row: 1 / span 2;
        align-self: center;
    }
    #device-grid.view-list .grid-cell-cameras {
        grid-column: 3;
        grid-row: 1 / span 2;
        align-self: center;
    }
    #device-grid.view-list .grid-cell-seen {
        grid-column: 4;
        grid-row: 1 / span 2;
        margin-top: 0;
        align-self: center;
    }
    #device-grid.view-list .grid-cell-sn {
        display: flex !important;
        grid-column: 5;
        grid-row: 1 / span 2;
        align-self: center;
    }
    #device-grid.view-list .grid-cell-cameras,
    #device-grid.view-list .grid-cell-sn {
        text-align: left;
    }
}

/* Name cell truncates long device names to one line (min-width:0 lets the
   first grid track shrink past its content width and the ellipsis show). */
#device-grid.view-list .grid-cell-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
#device-grid.view-list .grid-cell-name .h4 {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Status and metadata values remain compact within their list-row regions. */
#device-grid.view-list .grid-cell-signal,
#device-grid.view-list .grid-cell-cameras,
#device-grid.view-list .grid-cell-seen,
#device-grid.view-list .grid-cell-sn {
    white-space: nowrap;
}

/* Fix the stray `mb-1` that metricLine()/buildCameraLine() add: zero it in the
   list row so each cell stays vertically centered on its grid track. */
#device-grid.view-list .grid-cell-signal > .d-block,
#device-grid.view-list .grid-cell-cameras > .d-block {
    margin-bottom: 0;
}

/* --- Camera indicator dots on dashboard device cards ------------------------- */
.camera-dots {
    display: inline-flex;
    gap: 3px;
    align-items: center;
}
.camera-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

/* Filter pill count badges sit neatly to the right of the label. */
.nav-pills .nav-link .badge {
    vertical-align: middle;
}

/* ============================================================================
   Device detail (Phase 7)
   ============================================================================ */

/* Spacing between the tab bar and its panes. */
.tab-content {
    padding-top: 1.25rem;
}

/* Detail header metadata row: keep the small MAC/Router/FRP chips on one line
   on wider screens, wrapping gracefully on mobile. */
.detail-meta {
    row-gap: 0.25rem;
}

/* --- Shiki JSON code block (Overview) + live log (Raw) --------------------- */

.raw-code-wrap { max-height: 28rem; overflow: auto; }
/* Hosts are transparent containers; Shiki's <pre> provides the theme background. */
.raw-code-host, .raw-log-code {
    background: transparent; font-size: 0.8rem; line-height: 1.45;
}
.raw-code-host { padding: 0.6rem; }
.raw-log-code { padding: 0.6rem; }
/* Shiki output owns the theme background + token colors; let it fill the host. */
.raw-code-host > pre.shiki,
.raw-log-code > pre.shiki {
    margin: 0; padding: 0.9rem; border-radius: 0.4rem;
    font-size: 0.8rem; line-height: 1.45; overflow: auto;
}
.raw-code-host > pre.shiki code,
.raw-log-code > pre.shiki code { background: transparent; }
/* Plain-text fallback when the Shiki ESM CDN is blocked. */
.shiki-fallback {
    margin: 0; padding: 0.9rem; border-radius: 0.4rem; background: #f6f8fa;
    font-size: 0.8rem; line-height: 1.45; white-space: pre;
    font-family: var(--bs-font-monospace, monospace);
}
.raw-log-wrap { max-height: 34rem; overflow: auto; border: 1px solid #e9ecef; border-radius: 0.4rem; }
.raw-log { padding: 0.5rem; }
.raw-log-line { border-bottom: 1px solid #eef0f2; padding: 0.4rem 0; }
.raw-log-line:last-child { border-bottom: 0; }
.raw-log-ts {
    font-size: 0.72rem; color: #6c757d; margin-bottom: 0.25rem;
    font-family: var(--bs-font-monospace, monospace);
}

/* --- Chart cards (ECharts hosts) ------------------------------------------- */
/* Time-series chart hosts use inline height:320px (see device.js createChart). */

/* ============================================================================
   Admin UI (Phase 8) — users + audit pages
   ============================================================================ */

/* Audit "Content" cell: long JSON should wrap rather than stretch the table. */
#audit-tbody code {
    word-break: break-all;
    white-space: normal;
}

/* Users table action column: keep the icon buttons on one row. */
#users-tbody .text-nowrap {
    column-gap: 0.15rem;
}

/* IMEI table: clickable name/SN links should look natural inside the table. */
#imei-tbody a {
    color: inherit;
    text-decoration: none;
}
#imei-tbody a:hover {
    color: var(--bs-primary, #4482ff);
    text-decoration: underline;
}
#imei-tbody code a {
    color: inherit;
    text-decoration: none;
}
#imei-tbody code a:hover {
    color: var(--bs-primary, #4482ff);
    text-decoration: underline;
}


/* ============================================================================
   Alert System (Phase 6)
   ============================================================================ */

/* Alert status badges */
.badge-active { background-color: #dc3545 !important; color: #fff !important; }
.badge-dismissed-cleared { background-color: #198754 !important; color: #fff !important; }
.badge-dismissed-manual { background-color: #6c757d !important; color: #fff !important; }
.badge-dismissed-rule { background-color: #ffc107 !important; color: #212529 !important; }

/* Condition display (e.g., "≥ 80.0 °C") */
.condition-display { font-weight: 600; white-space: nowrap; }

/* ============================================================================
   Proxy Status Banner (Variant 1 — above navbar)
   ============================================================================ */

.proxy-status-banner {
    background-color: #dc3545;
    color: #fff;
    width: 100%;
    position: relative;
    z-index: 1030;
    font-size: 0.875rem;
}

.proxy-status-banner i {
    color: #fff;
}

.proxy-banner-methods {
    opacity: 0.85;
}

.proxy-banner-link {
    color: #fff;
    text-decoration: underline;
    font-weight: 500;
    white-space: nowrap;
}

.proxy-banner-link:hover {
    color: #fff;
    opacity: 0.85;
}

/* Push the navbar down when the banner is visible */
body.has-proxy-banner header.navbar {
    margin-top: 0;
}

/* ============================================================================
   Mobile navigation drawer (offcanvas) — layout glue only.
   Makes the offcanvas a full-height flex column so the profile footer anchors
   to the bottom. The footer is a direct child of .offcanvas (outside the
   scrollable .offcanvas-body), so it is always pinned and does not need
   margin-top: auto. No colors/fonts/shadows are added here.
   ============================================================================ */
.offcanvas.offcanvas-end.dzass-drawer {
    display: flex;
    flex-direction: column;
}
.dzass-drawer .offcanvas-body {
    display: flex;
    flex-direction: column;
    overflow-y: auto;
}
.dzass-drawer .dzass-drawer-footer {
    flex-shrink: 0;
}

/* ============================================================================
   Map page (full-screen MapLibre GL)
   When <body> carries .map-fullscreen it becomes a full-height flex column so
   the navbar <header> keeps its natural height while <main> (flex:1) fills the
   remaining viewport edge-to-edge. The footer is hidden for this page only and
   the #map host fills the available height. No hardcoded navbar pixel height.
    ============================================================================ */

body.map-fullscreen {
    /* Full-height flex column: header (auto) + main (fills rest) + footer. */
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

body.map-fullscreen > main {
    /* Break out of the shared .container.py-4 so the map is full-bleed and fills
       the viewport below the navbar. min-height:0 lets the flex child shrink and
       gives the absolutely-positioned #map a concrete height to fill. */
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    max-width: none;
    padding: 0 !important;
    margin: 0;
    position: relative;
}

body.map-fullscreen > footer {
    /* Footer hidden only on the map page. */
    display: none !important;
}

#map {
    /* Absolute inset within the positioned <main> resolves to real pixels even
       when the flex child has no explicit height, so the MapLibre canvas fills it. */
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    min-height: 0;
}

/* --- MapLibre canvas must fill #map (canvas is injected as a child of #map). --- */
#map .maplibregl-canvas {
    width: 100% !important;
    height: 100% !important;
}

/* --- Source switcher control (top-right) ----------------------------------
   Built inline in map.js as a <select.form-select> inside a MapLibre control
   container (which also carries .maplibregl-ctrl so clicks reach it). Uses
   Bootstrap form tokens so it tracks the active theme. Sits clear of the
   bottom-right attribution control. */
.map-source-switcher {
    background-color: #ffffff;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 0.375rem;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
    padding: 0.25rem;
}
.map-source-switcher .form-select {
    /* The select is the only child; let it own the panel width. */
    min-width: 11rem;
    border: 0;
}
.map-source-switcher .form-select:focus {
    box-shadow: none;
}

