/* Vehicle Map page */

/*
 * Full-height workspace. The layout wraps every page in .container.is-fluid > .section,
 * both of which pad; the map page claims the whole viewport below the fixed navbar
 * instead. The 3.25rem matches the navbar offset the layout applies to its columns.
 */
.section:has(> .vehicle-map-page) {
    padding: 0;
}

.container:has(.vehicle-map-page) {
    padding-left: 0;
    padding-right: 0;
}

/* Bulma gives .columns.is-gapless a 1.5rem bottom margin unless it is the last child, and the
   layout has script tags after it. Left alone that margin scrolls the whole page by 24px.
   The .is-gapless variant is listed to outrank Bulma's .columns.is-gapless:not(:last-child). */
.columns:has(.vehicle-map-page),
.columns.is-gapless:has(.vehicle-map-page) {
    margin-bottom: 0;
}

.vehicle-map-page {
    /* The shared page inset, same as every other page. The right side stays flush:
       the "in view" panel is meant to meet the edge, which is why this is padding-left
       rather than the symmetric padding the other pages get. */
    display: flex;
    padding-left: var(--page-inset);
    flex-direction: column;
    height: calc(100vh - 3.25rem);
    height: calc(100dvh - 3.25rem);
    overflow: hidden;
}

/* Fullscreen takes the navbar with it, so the offset above has nothing left to make room for.
   The backdrop is the page's own, not the layout's, and defaults to black without this. */
.vehicle-map-page:fullscreen {
    height: 100vh;
    height: 100dvh;
    background: var(--surface, #ffffff);
}

/*
 * The page title sits in its own row so it matches every other page instead of being
 * absorbed into the full-bleed toolbar. The padding mirrors what the layout gives
 * everyone else - .section contributes 3rem 1.5rem and .container.is-fluid another
 * 32px each side - which this page zeroes so the map can run edge to edge. The map
 * itself stays full-bleed; only the title is inset.
 *
 * No height maths is needed: .vehicle-map-workspace is flex: 1 1 auto, so the map
 * gives up exactly the space this row takes.
 */
.vehicle-map-header {
    flex: 0 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3) var(--space-5);
    /* No left padding - the page inset already places it, which is what keeps the
       heading aligned with the map below. */
    padding: var(--space-5) var(--space-4) var(--space-3) 0;
    background: var(--background);
}

.vehicle-map-header .title {
    margin-bottom: 0;
}

/* Freshness readout and refresh, sat opposite the heading. */
.vehicle-map-header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.vehicle-map-toolbar {
    flex: 0 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem 1.25rem;
    padding: 0.6rem var(--space-4) 0.6rem 0;
    background: var(--surface, #ffffff);
    /* Surfaces carry their own colour: the themes set it per component (.box, .card ...)
       rather than through body inheritance, which Bulma's own body rule wins. */
    color: var(--text-primary, #4a4a4a);
    border-bottom: 1px solid var(--border, #dbdbdb);
}

.vehicle-map-toolbar .field {
    margin-bottom: 0;
}

.vehicle-map-freshness {
    color: var(--text-secondary, #7a7a7a);
    font-size: var(--text-sm);
    white-space: nowrap;
}

.vehicle-map-freshness.is-stale {
    color: var(--warning, #ffe08a);
}

.vehicle-map-freshness.is-error {
    color: var(--danger, #f14668);
    font-weight: 600;
}

.vehicle-map-toolbar-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem 0.75rem;
}

/* The organization form is a real form among plain controls, so it needs the same
   zero-margin treatment the other fields get to sit on the row baseline. */
.vehicle-map-filters .search-form,
.vehicle-map-filters .search-form .field {
    margin-bottom: 0;
}

.vehicle-map-filters {
    flex: 0 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem var(--space-4) 0.5rem 0;
    background: var(--surface, #ffffff);
    color: var(--text-primary, #4a4a4a);
    border-bottom: 1px solid var(--border, #dbdbdb);
}

.vehicle-map-filters .control {
    min-width: 15rem;
}

.vehicle-map-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
}

.vehicle-map-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.1rem 0.3rem 0.1rem 0.55rem;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--button-info-bg, #3A9D91) 16%, transparent);
    color: var(--text-primary, #4a4a4a);
    font-size: var(--text-xs);
}

.vehicle-map-chip button {
    display: inline-flex;
    align-items: center;
    padding: 0.1rem 0.2rem;
    border: 0;
    background: transparent;
    color: inherit;
    cursor: pointer;
    font-size: var(--text-xs);
    opacity: 0.7;
}

.vehicle-map-chip button:hover,
.vehicle-map-chip button:focus-visible {
    opacity: 1;
}

.vehicle-map-clear-all {
    border: 0;
    background: transparent;
    color: var(--button-info-bg, #3A9D91);
    cursor: pointer;
    font-size: var(--text-xs);
    text-decoration: underline;
}

.vehicle-map-workspace {
    flex: 1 1 auto;
    display: flex;
    min-height: 0;
}

.vehicle-map-canvas {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
}

.vehicle-map-page #vehicle-map {
    position: absolute;
    inset: 0;
    height: 100%;
    width: 100%;
}

/* Sits above Leaflet's panes (400) and controls (800 is the control corner) without
   covering popups. */
.vehicle-map-reopen {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
    z-index: 700;
}

.vehicle-map-workspace:not(.is-panel-collapsed) .vehicle-map-reopen {
    display: none;
}

/* Side panel */

.vehicle-map-panel {
    flex: 0 0 20rem;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--surface, #ffffff);
    color: var(--text-primary, #4a4a4a);
    border-left: 1px solid var(--border, #dbdbdb);
}

.vehicle-map-workspace.is-panel-collapsed .vehicle-map-panel {
    display: none;
}

.vehicle-map-panel-header {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid var(--border, #dbdbdb);
}

.vehicle-map-panel-title {
    font-weight: 700;
    margin-right: auto;
}

.vehicle-map-panel-count {
    color: var(--text-secondary, #7a7a7a);
    font-size: var(--text-sm);
    font-weight: 400;
}

.vehicle-map-panel-list {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
    list-style: none;
    margin: 0;
    padding: 0;
}

.vehicle-map-empty {
    padding: 1rem 0.75rem;
    color: var(--text-secondary, #7a7a7a);
    font-size: var(--text-sm);
}

.vehicle-map-row {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    width: 100%;
    text-align: left;
    padding: 0.5rem 0.75rem;
    border: 0;
    border-bottom: 1px solid var(--border, #dbdbdb);
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.vehicle-map-row:hover,
.vehicle-map-row:focus-visible {
    background: var(--surface-alt, rgba(0, 0, 0, 0.04));
}

.vehicle-map-row.is-selected {
    background: color-mix(in srgb, var(--button-info-bg, #3A9D91) 14%, transparent);
    box-shadow: inset 3px 0 0 var(--button-info-bg, #3A9D91);
}

.vehicle-map-row-license {
    font-weight: 700;
}

.vehicle-map-row-meta {
    color: var(--text-secondary, #7a7a7a);
    font-size: var(--text-sm);
    margin-left: auto;
    text-align: right;
    white-space: nowrap;
}

.vehicle-map-panel-footer {
    flex: 0 0 auto;
    border-top: 1px solid var(--border, #dbdbdb);
    max-height: 40%;
    overflow-y: auto;
}

.vehicle-map-panel-footer summary {
    cursor: pointer;
    padding: 0.5rem 0.75rem;
    font-size: var(--text-sm);
    color: var(--text-secondary, #7a7a7a);
}

/*
 * Mode tones. Every mode classifies a vehicle into one of these five tones, so marker,
 * legend and list swatch colours all come from the theme rather than JS literals.
 */

/*
 * Clusters. MarkerCluster.Default.css is deliberately not loaded, so these rules are the whole
 * cluster appearance: the badge takes the worst tone among the vehicles it hides.
 */
.vehicle-cluster {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin: 3px;
    border-radius: var(--radius-circle);
    border: 3px solid #fff;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
    background-color: var(--map-tone, var(--info, #3e8ed0));
    color: #fff;
    font-size: var(--text-sm);
    font-weight: 700;
}

.vehicle-cluster.is-medium {
    width: 42px;
    height: 42px;
    font-size: var(--text-sm);
}

.vehicle-cluster.is-large {
    width: 50px;
    height: 50px;
    font-size: var(--text-base);
}

.vehicle-cluster-icon.is-highlighted .vehicle-cluster {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--button-info-bg, #3A9D91) 55%, transparent);
}

/* Depot occupancy count, shown on org unit markers in depot mode only. */
.ou-occupancy-badge {
    position: absolute;
    top: -0.45rem;
    right: -0.55rem;
    min-width: 1.05rem;
    padding: 0 0.2rem;
    border-radius: var(--radius-pill);
    background: var(--button-info-bg, #3A9D91);
    color: var(--button-info-text, #fff);
    font-size: var(--text-2xs);
    font-weight: 700;
    line-height: 1.05rem;
    text-align: center;
}

/*
 * Stale positions. Dashed rather than solid so the doubt reads even at a glance, and dimmed so
 * fresh vehicles stay dominant. Applied in every mode, not just device health.
 */
/* De-emphasised, but still legible. A dashed 3px border on a 28px circle read as a
   cog rather than a marker, and 0.38 opacity left stale vehicles nearly invisible
   against pale tiles - which matters, because a stale vehicle is still a vehicle
   somebody may be looking for. The exact age is on the tooltip either way. */
.vehicle-marker-badge.is-stale {
    opacity: 0.82;
}

.vehicle-marker-badge.is-very-stale {
    opacity: 0.62;
}

.vehicle-map-row.is-stale {
    opacity: 0.72;
}

.vehicle-map-row.is-very-stale {
    opacity: 0.55;
}

.vehicle-popup .popup-value-tone {
    color: var(--map-tone, inherit);
    font-weight: bold;
}

/* View controls */

/*
 * Bulma gives every .buttons group a bottom margin - 1rem, or -0.5rem when it is the last
 * child - and each .button inside it another 0.5rem. These rows centre their items on the
 * cross axis, where a bottom margin lifts the whole group above everything beside it. Three
 * classes so these outrank Bulma's .buttons:not(:last-child) whatever the load order.
 */
.vehicle-map-page .vehicle-map-toolbar .buttons,
.vehicle-map-page .vehicle-map-trail .buttons {
    margin-bottom: 0;
}

.vehicle-map-page .vehicle-map-toolbar .buttons .button,
.vehicle-map-page .vehicle-map-trail .buttons .button {
    margin-bottom: 0;
}

/* Mode switcher, and the trail's range picker which reads the same way. */

.vehicle-map-modes .button.is-active,
.vehicle-map-trail-ranges .button.is-active {
    background-color: var(--button-info-bg, #3A9D91);
    border-color: var(--button-info-bg, #3A9D91);
    color: var(--button-info-text, #fff);
}

/* Floating cards, anchored clear of Leaflet's bottom attribution and stacked upwards so
   neither the legend nor the trail card has to know the other's height. */

.vehicle-map-overlays {
    position: absolute;
    left: 0.75rem;
    bottom: 1.75rem;
    z-index: 700;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
}

.vehicle-map-legend,
.vehicle-map-trail {
    padding: 0.45rem 0.6rem;
    background: var(--surface, #ffffff);
    color: var(--text-primary, #4a4a4a);
    border: 1px solid var(--border, #dbdbdb);
    border-radius: var(--radius-sm);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    font-size: var(--text-xs);
    max-width: 14rem;
}

.vehicle-map-legend-title {
    font-weight: 700;
    margin-bottom: 0.25rem;
}

.vehicle-map-legend-item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    line-height: 1.5;
}

.vehicle-map-legend-count {
    margin-left: auto;
    color: var(--text-secondary, #7a7a7a);
}

/* Position trail, offered while a vehicle is selected. */

.vehicle-map-trail-title {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    font-weight: 700;
    margin-bottom: 0.35rem;
}

.vehicle-map-trail-status {
    margin-top: 0.35rem;
    color: var(--text-secondary, #7a7a7a);
}

/* Nothing to say yet - an empty status line would still take up a row. */
.vehicle-map-trail-status:empty {
    display: none;
}

/*
 * The path itself. Leaflet writes stroke colours as SVG presentation attributes, which CSS
 * outranks, so the trail is themed here rather than coloured from script: the heater-off
 * stretches read as background and the heating ones as the thing worth looking at.
 */
.vehicle-trail-path {
    stroke: var(--text-secondary, #7a7a7a);
    stroke-opacity: 0.7;
}

.vehicle-trail-path.is-heating {
    stroke: var(--success, #48c78e);
    stroke-opacity: 1;
}

.vehicle-trail-start {
    stroke: #fff;
    fill: var(--text-secondary, #7a7a7a);
    fill-opacity: 1;
}

.map-tone-swatch {
    flex: 0 0 auto;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: var(--radius-circle);
    background-color: var(--map-tone, var(--info, #3e8ed0));
}

.vehicle-map-row .map-tone-swatch {
    align-self: center;
}

/* Highlight the marker matching the hovered or selected row. */
.vehicle-marker.is-highlighted {
    z-index: 650;
}

.vehicle-marker > div {
    transition: transform var(--duration-fast) ease-out;
}

.vehicle-marker.is-highlighted > div {
    transform: scale(1.3);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--button-info-bg, #3A9D91) 55%, transparent);
}

/* Mobile: the panel becomes a bottom sheet over the map. */
@media screen and (max-width: 768px) {
    .vehicle-map-workspace {
        flex-direction: column;
    }

    .vehicle-map-panel {
        flex: 0 0 45%;
        border-left: 0;
        border-top: 1px solid var(--border, #dbdbdb);
        box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.12);
    }
}

/* Leaflet paints the container itself while tiles are still loading, and its stock #ddd is
   exactly the light rectangle a dark theme is trying not to show. */
[data-theme^="dark"] .vehicle-map-page #vehicle-map {
    background: var(--background, #1a1a1a);
}

[data-theme] .vehicle-map-page #vehicle-map .leaflet-popup-content-wrapper {
    background: var(--surface, #ffffff);
    color: var(--text-primary, #4a4a4a);
    border: 1px solid var(--border, #dbdbdb);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2);
}

[data-theme] .vehicle-map-page #vehicle-map .leaflet-popup-tip {
    background: var(--surface, #ffffff);
    border: 1px solid var(--border, #dbdbdb);
}

[data-theme] .vehicle-map-page #vehicle-map .leaflet-popup-content a {
    color: var(--button-primary-bg, var(--primary, #1F72A5));
}

.map-layer-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
}

/* The display above outranks the [hidden] attribute's own rule, so hiding a toggle - the
   geofence one, while the OU markers are off - has to be spelled out here. */
.map-layer-toggle[hidden] {
    display: none;
}

[data-theme] .vehicle-map-page #vehicle-map .leaflet-tooltip {
    background: var(--surface, #ffffff);
    color: var(--text-primary, #4a4a4a);
    border: 1px solid var(--border, #dbdbdb);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
}

[data-theme] .vehicle-map-page #vehicle-map .leaflet-tooltip-top::before {
    border-top-color: var(--border, #dbdbdb);
}

.map-tooltip {
    font-size: var(--text-sm);
    line-height: 1.25;
}

.map-tooltip div:first-child {
    font-weight: 700;
}

.vehicle-popup .popup-title {
    font-weight: bold;
    font-size: 1.1em;
    margin-bottom: 4px;
}

.vehicle-popup .popup-detail {
    margin: 2px 0;
    font-size: 0.9em;
}

.vehicle-popup .popup-actions {
    margin-top: 6px;
}

.vehicle-popup .popup-chip {
    display: inline-flex;
    align-items: center;
    padding: 0.1rem 0.45rem;
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: 700;
    border: 1px solid transparent;
}

.vehicle-popup .popup-chip.is-selected {
    background: color-mix(in srgb, var(--button-info-bg, #3A9D91) 16%, transparent);
    color: var(--button-info-bg, #3A9D91);
}

.vehicle-popup .popup-chip.is-subunit {
    background: color-mix(in srgb, var(--text-secondary, #7a7a7a) 18%, transparent);
    color: var(--text-secondary, #7a7a7a);
}

.ou-marker-badge {
    position: relative; /* anchors the depot occupancy badge */
    width: 26px;
    height: 26px;
    border-radius: var(--radius-md);
    border: 3px solid #fff;
    box-shadow: 0 2px 6px rgba(0,0,0,0.3);
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--button-primary-bg, var(--primary, #1F72A5));
    color: #fff;
}

/* Scaled to the badge it sits in, both sizes below, so the script never sets a pixel size. */
.ou-marker-badge i {
    font-size: var(--text-xs);
}

.ou-marker-badge.is-selected {
    width: 32px;
    height: 32px;
    background-color: var(--button-info-bg, var(--info, #3A9D91));
}

.ou-marker-badge.is-selected i {
    font-size: var(--text-sm);
}
