:root {
    --app-sidebar-w: 248px;
    --app-topbar-h: 56px;
    --brand: #0d6efd;
}

html, body { height: 100%; }
body { margin: 0; }

/* ============ App shell (sidebar + topbar + main) ============ */
.app-sidebar {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: var(--app-sidebar-w);
    background: var(--bs-body-bg);
    border-right: 1px solid var(--bs-border-color);
    display: flex;
    flex-direction: column;
    z-index: 1040;
}
.app-sidebar-brand {
    display: flex;
    align-items: center;
    gap: .25rem;
    height: var(--app-topbar-h);
    padding: 0 1rem;
    border-bottom: 1px solid var(--bs-border-color);
    font-size: 1.05rem;
}
.brand-logo-wrap { font-size: 1.3rem; }
.app-nav { padding: .75rem .5rem; overflow-y: auto; }
.app-nav-link {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .55rem .75rem;
    border-radius: .5rem;
    color: var(--bs-body-color);
    text-decoration: none;
    font-weight: 500;
    margin-bottom: .15rem;
}
.app-nav-link i { width: 1.2rem; text-align: center; color: var(--bs-secondary-color); }
.app-nav-link:hover { background: var(--bs-secondary-bg); }
.app-nav-link.active { background: color-mix(in srgb, var(--brand) 14%, transparent); color: var(--brand); }
.app-nav-link.active i { color: var(--brand); }

.app-topbar {
    position: fixed;
    top: 0; right: 0; left: var(--app-sidebar-w);
    height: var(--app-topbar-h);
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: 0 1rem;
    background: var(--bs-body-bg);
    border-bottom: 1px solid var(--bs-border-color);
    z-index: 1030;
}
.app-main {
    margin-left: var(--app-sidebar-w);
    padding: calc(var(--app-topbar-h) + 1rem) 1.25rem 1.25rem;
    min-height: 100vh;
}
.app-footer {
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid var(--bs-border-color);
    color: var(--bs-secondary-color);
    font-size: .85rem;
}

@media (max-width: 991.98px) {
    .app-topbar { left: 0; }
    .app-main { margin-left: 0; }
}

/* ============ DB status glyph ============ */
.db-glyph { font-size: 1rem; }
.db-glyph.ok { color: #198754; }
.db-glyph.bad { color: #dc3545; }

/* ============ Grid ============ */
.app-grid { width: 100%; height: calc(100vh - var(--app-topbar-h) - 190px); min-height: 360px; }
/* Phone: the search toolbar wraps to several rows, so give the grid a taller share and a smaller floor. */
@media (max-width: 575.98px) {
    .app-grid { height: calc(100vh - var(--app-topbar-h) - 260px); min-height: 300px; }
}
.grid-icon-btn { cursor: pointer; padding: 0 .3rem; background: none; border: none; }
.grid-icon-btn.edit { color: var(--brand); }
.grid-icon-btn.del { color: #dc3545; }
.grid-icon-btn.report { color: var(--bs-secondary-color); }
.grid-icon-btn[disabled] { opacity: .3; cursor: not-allowed; }

/* Tom Select dropdowns sitting inside a Bootstrap floating label.
   The original (hidden) .form-select keeps the label in its floated position; we give the visible
   Tom Select control the matching height and top padding so its value sits below the floated label. */
.form-floating > .ts-wrapper { padding: 0; background: transparent; height: auto; min-height: calc(3.5rem + 2px); }
/* Force the border/box on the control so it matches the text inputs (beats the plugin's own rules). */
.form-floating > .ts-wrapper .ts-control {
    min-height: calc(3.5rem + 2px);
    padding: 1.625rem .75rem .375rem !important;
    border: 1px solid var(--bs-border-color) !important;
    border-radius: var(--bs-border-radius) !important;
    background-color: var(--bs-body-bg) !important;
    color: var(--bs-body-color);
    align-items: center;
    box-shadow: none !important;
}
.form-floating > .ts-wrapper ~ label { z-index: 3; }
.form-floating > .ts-wrapper.focus .ts-control { border-color: #86b7fe !important; box-shadow: 0 0 0 .25rem rgba(13,110,253,.25) !important; }

/* Single-select: keep the selected value AND the hidden search input on ONE line.
   Tom Select's input carries a min-width that, sitting next to a long value like
   "23 - Hector Ortolaza", can't fit and wraps to a second row — which grows the
   control taller than the plain text inputs (the stray "line" under the value).
   Force no-wrap, let the value ellipsize, and drop the input's min-width. */
.form-floating > .ts-wrapper.single .ts-control { flex-wrap: nowrap; overflow: hidden; }
.form-floating > .ts-wrapper.single .ts-control > .item {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
.form-floating > .ts-wrapper.single .ts-control > input { min-width: 0 !important; flex: 0 1 auto; }

/* Tom Select — dark mode: control + dropdown adopt the Bootstrap theme colours. */
[data-bs-theme="dark"] .ts-control { background-color: var(--bs-body-bg) !important; color: var(--bs-body-color); border-color: var(--bs-border-color) !important; }
[data-bs-theme="dark"] .ts-control .item,
[data-bs-theme="dark"] .ts-control input,
[data-bs-theme="dark"] .ts-control input::placeholder { color: var(--bs-body-color); }
[data-bs-theme="dark"] .ts-dropdown { background-color: var(--bs-body-bg); color: var(--bs-body-color); border-color: var(--bs-border-color); box-shadow: 0 .5rem 1rem rgba(0,0,0,.5); }
[data-bs-theme="dark"] .ts-dropdown .option { color: var(--bs-body-color); }
[data-bs-theme="dark"] .ts-dropdown .active { background-color: rgba(255,255,255,.10); color: var(--bs-body-color); }
[data-bs-theme="dark"] .ts-dropdown .optgroup-header,
[data-bs-theme="dark"] .ts-dropdown .no-results { background: transparent; color: var(--bs-secondary-color); }

/* Draggable modals: header is the grab handle. */
.modal-header { cursor: move; }
.modal-header .btn-close { cursor: pointer; }
/* A fullscreen (phone/tablet) modal isn't draggable — reset the grab cursor there. */
.modal-fullscreen .modal-header,
.modal-fullscreen-sm-down .modal-header,
.modal-fullscreen-lg-down .modal-header { cursor: default; }

/* Report modal: tall PDF viewer on desktop; fill the screen when fullscreen on phone. */
.report-modal-content { height: 90vh; }
@media (max-width: 575.98px) { .report-modal-content { height: 100%; } }

/* Search toolbar: on phone every control goes full-width and stacks instead of cramming
   with fixed min-widths; the "New movement" button spans the row as the last item. */
@media (max-width: 575.98px) {
    .mv-toolbar { gap: .5rem !important; }
    .mv-toolbar > div { width: 100%; }
    .mv-toolbar select,
    .mv-toolbar input,
    .mv-toolbar .form-select,
    .mv-toolbar .form-control { width: 100% !important; min-width: 0 !important; max-width: none !important; }
    .mv-toolbar > div[style*="max-width"] { max-width: none !important; }
    .mv-toolbar .d-flex { width: 100%; }
    .mv-toolbar .d-flex .btn { flex: 1 1 0; }
    .mv-toolbar .ms-auto { margin-left: 0 !important; width: 100%; }
    .mv-toolbar .ms-auto .btn { width: 100%; }
}

/* Status cell holds inline badges — never show an ellipsis when they're snug. */
.ag-theme-quartz .status-cell,
.ag-theme-quartz-dark .status-cell { text-overflow: clip; overflow: hidden; }

/* ============ Stat tiles ============ */
.stat-tile {
    border: 1px solid var(--bs-border-color);
    border-radius: .75rem;
    padding: 1rem 1.1rem;
    background: var(--bs-body-bg);
    height: 100%;
}
.stat-tile .stat-label { color: var(--bs-secondary-color); font-size: .8rem; text-transform: uppercase; letter-spacing: .03em; }
.stat-tile .stat-value { font-size: 1.7rem; font-weight: 600; line-height: 1.2; }
.stat-tile .stat-icon { font-size: 1.4rem; opacity: .5; }

.chart-card {
    border: 1px solid var(--bs-border-color);
    border-radius: .75rem;
    padding: 1rem;
    background: var(--bs-body-bg);
}
.chart-box { width: 100%; height: 320px; }

/* ============ Login ============ */
.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1rem; }
.login-card { width: 100%; max-width: 400px; }
.login-brand { font-size: 2.2rem; color: var(--brand); }

/* ===== Animated login header (brand left · cycling A→B→D scene right) ===== */
.login-head { display: flex; align-items: center; gap: 16px; }
.login-brand-col { display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 118px; }
.login-logo { font-size: 2.1rem; color: var(--brand); line-height: 1; }
.login-name { font-weight: 700; font-size: 1.15rem; text-align: center; line-height: 1.05; }
.login-sub { font-size: .8rem; color: var(--bs-secondary-color); }
.login-head-divider { width: 1px; align-self: stretch; background: var(--bs-border-color); margin: 4px 0; }
.login-status-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 8px; }

.mv-stage { position: relative; width: 100%; max-width: 160px; height: 84px; border-radius: 12px; background: #f6f8fb; overflow: hidden; }
[data-bs-theme="dark"] .mv-stage { background: rgba(255,255,255,.05); }
.mv-scene { position: absolute; inset: 0; opacity: 0; animation: mvShow 9s infinite; }
.mv-a { animation-delay: 0s; } .mv-b { animation-delay: 3s; } .mv-d { animation-delay: 6s; }
@keyframes mvShow { 0%{opacity:0} 3%{opacity:1} 30%{opacity:1} 34%{opacity:0} 100%{opacity:0} }

/* A — on the road (blue truck drives over a moving road) */
.mv-truck-a { position: absolute; bottom: 24px; left: 16px; color: var(--brand); animation: mvDrive 3s ease-in-out infinite; }
@keyframes mvDrive { 0%,100%{transform:translateX(0)} 50%{transform:translateX(74px)} }
.mv-road { position: absolute; left: 14px; right: 14px; bottom: 22px; height: 3px; border-radius: 3px; background: #cbd5e1; overflow: hidden; }
.mv-road::after { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(90deg,#94a3b8 0 8px,transparent 8px 16px); animation: mvDash 1s linear infinite; }
@keyframes mvDash { to { transform: translateX(-16px); } }

/* B — live dispatch (amber radar ping around a truck badge) */
.mv-b { display: flex; align-items: center; justify-content: center; }
.mv-badge { width: 46px; height: 46px; border-radius: 50%; background: rgba(13,110,253,.12); color: var(--brand); display: flex; align-items: center; justify-content: center; z-index: 1; }
.mv-ping { position: absolute; left: 50%; top: 50%; width: 46px; height: 46px; border-radius: 50%; border: 2px solid #f59e0b; opacity: 0; animation: mvPing 2s ease-out infinite; }
.mv-ping.d2 { animation-delay: 1s; }
@keyframes mvPing { 0%{transform:translate(-50%,-50%) scale(.5);opacity:.7} 100%{transform:translate(-50%,-50%) scale(1.5);opacity:0} }

/* D — trace the route (green route draws itself, red pin pops) */
.mv-route { fill: none; stroke: #16a34a; stroke-width: 3; stroke-linecap: round; stroke-dasharray: 150; stroke-dashoffset: 150; animation: mvDraw 3s ease-in-out infinite; }
@keyframes mvDraw { 0%{stroke-dashoffset:150} 60%{stroke-dashoffset:0} 100%{stroke-dashoffset:0} }
.mv-pin { animation: mvPin 3s ease-in-out infinite; }
@keyframes mvPin { 0%,50%{opacity:0} 65%,100%{opacity:1} }

/* cycling status caption, colored to match each scene */
.mv-status { position: relative; height: 20px; width: 100%; text-align: center; }
.mv-status span { position: absolute; left: 0; right: 0; font-size: .85rem; font-weight: 600; opacity: 0; animation: mvCyc 9s infinite; }
.mv-status span:nth-child(1) { color: var(--brand); }
.mv-status span:nth-child(2) { color: #f59e0b; animation-delay: 3s; }
.mv-status span:nth-child(3) { color: #16a34a; animation-delay: 6s; }
@keyframes mvCyc { 0%{opacity:0;transform:translateY(3px)} 3%{opacity:1;transform:none} 30%{opacity:1} 33%{opacity:0} 100%{opacity:0} }
