/* ===================== BASE ===================== */
*, *::before, *::after { box-sizing: border-box; }

/* Hide scrollbars globally while keeping scroll functional */
* { scrollbar-width: none; }
*::-webkit-scrollbar { display: none; }

html, body {
    margin: 0; padding: 0;
    height: 100%;
    font-family: "Inter", sans-serif;
    font-size: 13px;
    /* 2026-08-11: was --color-primary-darker, i.e. the WEATHER ramp, so the page
       behind everything was a dark teal. html/body sit outside every
       [data-section], so it could never follow the active section either: it was
       just azure forever. The ground is what --blue8 means, so point it there. */
    background-color: var(--blue8);
    color: var(--blue1);
}

.hide { display: none !important; }
.svg-ico { display: block; width: 1em; height: 1em; color: inherit; }
.svg-ico svg { fill: currentColor; }
.ico-size { width: 24px !important; height: 24px !important; }
.ico-size-sm { width: 16px !important; height: 16px !important; }
.ico-size-lg { width: 32px !important; height: 32px !important; }

/* ===================== FOCUS VISIBLE ===================== */
.nav-tab:focus-visible,
.activity-btn:focus-visible,
.unit-btn:focus-visible,
.temp-unit-btn:focus-visible,
.time-btn:focus-visible,
.map-mode-btn:focus-visible,
.td-btn:focus-visible,
.track-item:focus-visible,
.qs-opt:focus-visible { outline: 2px solid var(--color-primary-ultralight); outline-offset: 2px; }

/* ===================== SPINNER ===================== */
#spinner {
    display: none;
    position: fixed;
    inset: 0;
    background: radial-gradient(circle at center, var(--grey9-75) 0%, var(--grey9-15) 5rem, transparent 20rem);
    z-index: 9999;
    align-items: center;
    justify-content: center;
    pointer-events: all;
    cursor: wait;
}
#spinner.show { display: flex; }
.ring-spinner {
    width: 3.5rem; height: 3.5rem;
    border-radius: 50%;
    border: 0.3rem solid var(--color-primary-ultralight);
    border-top-color: var(--color-primary);
    animation: ring-spin 1.2s linear infinite;
}
@keyframes ring-spin { to { transform: rotate(360deg); } }

/* ===================== LAYOUT ===================== */
#app {
    position: relative;
    height: 100vh;
    height: 100dvh; /* dynamic viewport height — excludes mobile browser chrome */
    overflow: hidden;
}

/* ===================== MAP ===================== */
#map {
    position: absolute;
    inset: 0;
    z-index: 0;
    /* Shows through where 3D terrain doesn't fill the near foreground — a soft haze
       (matches the 3D sky's fog-color) instead of the dark body background. */
    background-color: #dfe8f0;
}

/* THE BRANDING SAT ON TOP OF OUR OWN DATA (fixed 2026-08-12, Pierre spotted it).
   maplibre-gl.css gives all four corner containers `z-index: 2`, while markers and
   popups get NO z-index at all — they live in the canvas container, which is an
   earlier sibling. So the attribution strip painted above every weather icon,
   track point and Active Point that happened to be near the bottom-right corner.
   The user-location marker already carried an inline `zIndex = 1000` (main.js), a
   one-off patch for exactly this, made without the general cause being noticed.
   Fix the layering instead: BOTTOM corners drop below the markers, TOP corners are
   deliberately left at 2 so the zoom / relief / 3D / hazards stack stays above the
   markers and can never have its taps stolen by an icon drifting under it.
   Content order over the map, bottom to top: canvas -> attribution -> markers and
   popups -> map buttons. */
#map .maplibregl-ctrl-bottom-left,
#map .maplibregl-ctrl-bottom-right { z-index: 0; }
#map .maplibregl-marker,
#map .maplibregl-popup { z-index: 1; }

/* ===================== WEATHER MAP MARKERS ===================== */
.weather-marker { display: flex; align-items: center; justify-content: center; }
.weather-marker img,
.cc-marker img { width: 36px; height: 36px; display: block; }

/* Suppress all transitions during orientation snap */
/* .no-transition retired 2026-08-11 with the sheet's slide transform. */

/* ===================== NAV BASE ===================== */
:root {
    --nav-tabs-h: 52px; --handle-h: 22px; --cal-col: 2rem;
    --dialog-shadow: 0 8px 32px rgba(0,0,0,0.5);
}

/* Sheet handle — portrait only */

/* FLOATING RAIL + POPOVER (2026-08-11, replaces the full-height sidebar and the
   bottom sheet). Pierre's reasoning: the marker palette works BECAUSE it is a
   popover over a live map, and all the purpose of the app is the map.
   #nav is now only a positioner. It paints nothing and swallows nothing: the
   container is pointer-events:none so the map stays live in the gaps, and the
   two real surfaces (the rail and the popover) switch it back on. That is also
   why no stopPropagation is needed anywhere here — an opaque element already
   keeps taps away from the MapLibre canvas, and v821 proved that reaching for
   stopPropagation breaks the document-delegated handlers. */
#nav {
    position: fixed;
    z-index: 1000;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--blue1);
    pointer-events: none;
    background: none;
    box-shadow: none;
}
#nav > * { pointer-events: auto; }

/* The popover. Sized to its CONTENT, which is the whole point: the gpx, nav and
   extra panels are 98-145px tall and have no business being 55vh. The cap only
   bites on the two big panels (weather, settings) and only then does it scroll. */
#nav .nav-body {
    width: max-content;
    max-width: min(360px, calc(100vw - 5rem));
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 0.75rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    background: var(--blue8-90);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--blue6);
    border-radius: 0.75rem;
    box-shadow: var(--dialog-shadow);
}
/* Closed = gone, not merely collapsed: a zero-height scroll container still
   traps wheel events and still reports a box to hit-testing. */
#nav:not(.nav-open) .nav-body { display: none; }

/* Toggle — landscape only, left-edge handle */
/* #nav-toggle CSS deleted 2026-08-11 with its button. */

/* Landscape default (incl. desktop): rail on the LEFT edge, vertically centred,
   popover opening to its right. Portrait overrides this in media.css.
   Vertically centred rather than top-anchored so the popover has room to grow
   both ways and the buttons stay in the same place whichever panel is open. */
/* --vvh, not %, dvh or svh (2026-08-12, found by Pierre on a OnePlus 13 in a browser).
   #nav is position:fixed, and for a fixed element a PERCENTAGE resolves against the LARGE viewport,
   the one you get with the URL bar hidden. So `top: 50%` centred the rail on a box ~56px taller
   than the visible area: it sat too low and the LAST button was sliced in half at the bottom while
   the first was still fine. That asymmetry is the signature of a wrong CENTRE, not a wrong size.
   dvh reports the large value too while the bar retracts, and svh came back near the large figure
   on his device as well, so switching units did not fix it. --vvh is published from
   visualViewport.height by vvh_publish() in main.js: the one number defined as "what is visible
   right now", re-measured when the URL bar slides. The fallback keeps this sane before JS runs. */
#nav {
    left: 0.75rem;
    top: calc(var(--vvh, 100svh) / 2);
    transform: translateY(-50%);
    flex-direction: row;
}
#nav .nav-tabs { flex-direction: column; }
#nav .nav-body { max-height: calc(var(--vvh, 100svh) - 2rem); }

/* ===================== NAV RAIL ===================== */
/* The five buttons. Its own surface now, matching .btn-map so every floating
   thing over the map reads as one family. */
.nav-tabs {
    display: flex;
    gap: 0.25rem;
    padding: 0.25rem;
    flex-shrink: 0;
    background: var(--blue8-90);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--blue6);
    border-radius: 0.75rem;
    box-shadow: 0 2px 8px var(--grey9-25);
}

.nav-tab {
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    width: 44px;
    height: 44px;
    min-height: 44px;
    border-radius: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--blue5);
    transition: color 120ms, background 120ms;
}
/* 36px, not 24 (Pierre, 2026-08-12). Desktop >=1024px has shown 36 since forever
   via media.css, so the rail was the last place still at phone-24: this makes the
   phone match what he had already approved on the desktop. */
.nav-tab .ico-size { width: 36px !important; height: 36px !important; }

/* ===================== THE APP MARK =====================
   `W` over `GPX`, the launcher icon (img/favicon.svg, and icon-192/512.png) with its dark rounded
   tile taken off. Tile-less on purpose, Pierre's call: a #111 square would be the only sticker on
   the graphite rail, and the icon's #4a8cf7 would read as the WEATHER hue now that :root is
   neutral. As two lines of currentColor it inherits the rail's resting / hover / pressed treatment
   exactly like the five pictograms beside it, and stays legible at 36px because that is what the
   mark was drawn for (it is also the 16px favicon).
   Sized in `em` so one font-size per context scales both lines, keeping the 2:1 proportion the
   original SVG uses (18 over 9 in a 32 box). Never translated: it is a name, not a label. */
.wgpx-mark {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    line-height: 0.92;
}
.wgpx-mark > span:first-child { font-size: 1.25em; }
.wgpx-mark > span:last-child  { font-size: 0.62em; letter-spacing: 0.06em; }
/* 18px, not 16: the five neighbours are pictograms whose ink fills the whole 36px box, and at 16px
   the two text lines came to 27px, reading as a smaller button. At 18px the block is ~31px. */
.nav-tab .wgpx-mark, .qs-leg .wgpx-mark { width: 36px; height: 36px; font-size: 18px; }

/* The wordmark at the top of the account panel. Deliberately NOT the rail's two-line mark and NOT
   the panel's neutral: it is the landing page's own signature (index.html `.lp-logo`, "Weather" in
   near-white + "GPX" in the brand blue), and Pierre's reason for the exception is that it signs the
   app rather than labelling a control. So this is the ONE place a colour is allowed to come from
   outside the section ramps.
   --brand-blue is scoped HERE rather than to :root so it cannot be mistaken for a palette entry: it
   is the same #4a8cf7 as img/favicon.svg and index.html's --primary, and all three must move
   together if it ever changes. */
.app-mark {
    --brand-blue: #4a8cf7;
    display: flex;
    align-items: center;
    justify-content: center;
    padding-bottom: 0.15rem;
}
.app-mark-name {
    font-size: var(--font-lg);
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--blue1);
}
.app-mark-name span { color: var(--brand-blue); }

/* EVERY rail icon carries its section hue, resting included — reversing the
   2026-08-11 "inactive tabs stay neutral" call. That call answered the wrong
   question (which tab am I on) and the strip already answers it with a plate and
   a ring. The question worth answering is which FAMILY a floating button belongs
   to, because the rail and the map buttons share the same surface, the same
   radius and the same blur: **hue = rail (goes into the app), neutral = map
   chrome (acts on the map)**. That is the same sentence as the map scope's own
   rule, one level up. State is then a second step within the hue: resting
   `-light`, pressed `-bright` on the tint plate. */
.nav-tab .svg-ico, .nav-tab .wgpx-mark { color: var(--color-primary-light); }
.nav-tab:hover { color: var(--blue2); background: var(--blue7); }
.nav-tab:hover .svg-ico, .nav-tab:hover .wgpx-mark { color: var(--color-primary-bright); }
/* The pressed state carries real weight now: the same button is the ONLY way
   out of the popover (no close button, no outside-tap dismissal), so it has to
   say "I am the way out" and not merely "you are here".
   GATED ON `#nav.nav-open` (2026-08-12, Pierre from a cold cache): app.html ships
   with `class="nav-tab active"` on the weather tab so the popover has a panel to
   show when it first opens, but on a fresh load the popover is CLOSED — and the
   button was painting itself pressed with nothing open behind it, i.e. lying about
   the one thing the pressed state is for. `.active` keeps meaning "this is the tab
   the popover will show"; only its PAINT is now conditional on something being
   open. Nothing in JS had to change. */
#nav.nav-open .nav-tab.active {
    color: var(--color-primary-ultralight);
    background: var(--color-primary-tint);
    box-shadow: inset 0 0 0 1px var(--color-primary-light);
}
#nav.nav-open .nav-tab.active .svg-ico,
#nav.nav-open .nav-tab.active .wgpx-mark { color: var(--color-primary-bright); }

/* Signed-in dot on the user tab. Green, not the section's own neutral, because this is a STATUS
   and status colours are shared across the app rather than owned by a section — the same reason
   amber and red are reserved. Small and top-right so it never fights the glyph. */
#nav-tab-user { position: relative; }
#nav-tab-user.signed-in::after {
    content: '';
    position: absolute;
    top: 6px; right: 6px;
    width: 7px; height: 7px;
    border-radius: 50%;
    /* -content, not -bright: -bright (#2a8c4a) is tuned for filling a banner behind white text,
       and a 7px dot of it disappears on the graphite rail. -content (#55fd55) is the palette's
       reading of "success on a dark surface", which is exactly this case. */
    background: var(--color-success-content);
    box-shadow: 0 0 0 2px var(--blue8);
}

/* Panels no longer stretch or scroll themselves: the popover is sized to the
   panel, so the panel states its natural height and the POPOVER does any
   scrolling. Two scroll containers nested inside each other was what made the
   old sheet need an explicit max-height at every breakpoint. */
.nav-tab-panel {
    display: none;
    flex: 0 0 auto;
    flex-direction: column;
    gap: 0.5rem;
}
.nav-tab-panel.active { display: flex; }

/* The `#nav #panel-elevation` rule was deleted 2026-08-11: dead. The chart's
   canvas holder lives in a hidden div OUTSIDE #nav (app.html) and moves into
   #chart-fullscreen-dialog on demand, so the selector had stopped matching. */

#nav #tracks-edit-row,
#nav #track-list {
    flex-shrink: 0;
}

/* ===================== NAV CONTROLS ===================== */
.nav-controls,
.weather-controls {
    display: flex;
    flex-direction: column;
    width: 100%;
}

.weather-controls > * {
    padding: 1rem 0;
    border-bottom: 1px solid var(--blue7);
}
.weather-controls > *:first-child { padding-top: 0.25rem; }
.weather-controls > *:last-child { border-bottom: none; }

.nav-controls .nav-inline,
.nav-controls .nav-group {
    width: 100%;
    padding: 1rem 0;
    border-bottom: 1px solid var(--blue7);
}
.nav-controls > :last-child { border-bottom: none; }
#user-auth .nav-group {
    padding: 0.5rem 0 1rem;
}
#user-profile .nav-group {
    padding: 0.5rem 0;
    gap: 0.5rem;
}

.settings-footer {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.3rem;
    padding: 1rem 0 0.5rem;
}
.settings-footer a {
    font-size: var(--font-xs);
    color: var(--blue4);
    text-decoration: none;
}
.settings-footer a:hover { color: var(--blue2); }
.settings-footer .footer-help { font-size: var(--font-md); color: var(--blue2); }
.settings-footer .footer-gap { margin-top: 0.4rem; }
/* Version label. Was inline opacity 0.35 at 0.72rem, i.e. barely visible on a phone in daylight,
   which is exactly the moment someone wants to check which build they are on. Now identical to the
   aone line above it: same colour, same size, full opacity. */
#app-version {
    font-size: var(--font-xs);
    color: var(--blue4);
}
.btn-pwa {
    display: inline-flex;
    align-items: center;
    gap: 0.3em;
    padding: 0.25em 0.75em;
    border: none;
    border-radius: 0.5rem;
    background-color: var(--grey0);
    color: var(--color-primary-darker);
    font-weight: 700;
    font-size: var(--font-xs);
    letter-spacing: 0.06em;
    cursor: pointer;
    opacity: 0.85;
    margin-top: 0.4rem;
    transition: opacity 0.15s ease, box-shadow 0.15s ease;
}
.btn-pwa:hover { opacity: 1; box-shadow: 0 2px 8px var(--grey0-25); background-color: var(--color-primary-ultralight); }
.btn-pwa:active { opacity: 0.7; box-shadow: none; background-color: var(--color-primary-ultralight); }

.nav-group {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.nav-inline {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
}
/* Settings > Modules: one toggle per optional module (see MODULES in main.js) */
#modules-list {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}
#modules-list .toggle-label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 44px;   /* touch target */
    font-size: var(--font-sm);
    color: var(--blue2);
}
#modules-list .toggle-label:has(input:disabled) { opacity: 0.45; }
.nav-inline:has(.unit-selector),
.nav-inline:has(.temp-unit-selector),
.nav-inline:has(.time-selector),
.nav-inline:has(.map-mode-selector) {
    justify-content: center;
    gap: 0.5rem;
}

.nav-label {
    font-size: var(--font-xs);
    opacity: 0.75;
    white-space: nowrap;
}

/* ===================== COORDS ROW ===================== */
#coords-row {
    position: absolute;
    top: 0.75rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 400;
    pointer-events: none;
    display: flex;
    align-items: center;
    gap: 0.3rem;
}
/* .btn-map supplies the visuals; only input-specific layout lives here */
#coords-input {
    width: 24ch;
    padding: 0.3rem 0.7rem;
    font-size: var(--font-md);
    font-family: monospace;
    text-align: center;
    line-height: normal;
    cursor: text;
}
#coords-input::placeholder { color: var(--blue5); }
#coords-input:focus { outline: none; border-color: var(--color-primary-light); }

/* ===================== MARKER CONFIG PANEL ===================== */
#marker-config-panel {
    position: fixed;
    z-index: 2000;
    background: var(--blue8);
    border: 1px solid var(--blue6);
    border-radius: 0.5rem;
    box-shadow: 0 6px 24px rgba(0,0,0,0.5);
    padding: 0.6rem;
    /* Sized to its content, not to a guess. A fixed 300px stretched the label column to 129px of
       mostly empty space; max-content lets the labels ask for exactly what they need and gives the
       rest back to the map. It is a palette, not a dialog: what matters is leaving map to DRAG. */
    width: max-content;
    min-width: 0;
    max-width: calc(100vw - 16px);
    /* 11 rows at a glove-sized 34px can outgrow a short viewport; the JS positioner flips the
       panel above the button but cannot shrink it. */
    max-height: calc(100vh - 16px);
    overflow-y: auto;
}
.mc-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    font-size: var(--font-xs);
    font-weight: 600;
    color: var(--blue2, #ccc);
}
#btn-mc-reset {
    padding: 0.2rem 0.5rem;
    border-radius: 999px;
    border: 1px solid var(--blue6);
    background: none;
    color: var(--blue4);
    font-size: var(--font-xs);
    font-family: inherit;
    cursor: pointer;
    transition: background 120ms, color 120ms;
}
#btn-mc-reset:hover { background: var(--color-primary-light); color: var(--grey0); }
.mc-grid {
    display: grid;
    /* auto, not 1fr: 1fr made the label column swallow every spare pixel. */
    grid-template-columns: auto 48px 48px 48px;
    /* stretch, NOT center: centering shrank every cell to the height of the 18px box, so the real
       tap target was 48 wide by 18 tall. Stretching hands each label the whole row height. */
    align-items: stretch;
    row-gap: 0.25rem;
    column-gap: 0.25rem;
}
.mc-grid-head {
    text-align: center;
    font-size: var(--font-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--blue4, #888);
    line-height: 1.2;
    word-break: break-word;
    padding-bottom: 0.25rem;
    border-bottom: 1px solid var(--blue7, #333);
}
.mc-grid-label {
    font-size: var(--font-md);   /* the width freed by dropping 1fr pays for readable labels: +8px */
    color: var(--blue2, #ccc);
    white-space: nowrap;
    padding-right: 0.35rem;
    display: flex;
    align-items: center;   /* rows stretch now, so the text has to centre itself */
}
.mc-grid-cb {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 34px;      /* the tap target; the box inside stays smaller on purpose */
    cursor: pointer;
}
/* The View row switches a whole source on and off, so it is separated from the per-field rows
   below it rather than reading as a tenth field, and it uses an eye instead of a checkbox so the
   two are not the same control wearing the same clothes. */
.mc-view-lbl, .mc-view-cb { border-bottom: 1px solid var(--blue7, #333); padding-bottom: 0.15rem; }
.mc-view-cb input[type="checkbox"] { display: none; }
.mc-view-cb .svg-ico { width: 26px; height: 26px; }
.mc-view-cb .mc-eye-on  { color: var(--color-secondary); }
.mc-view-cb .mc-eye-off { color: var(--blue5); }
.mc-view-cb input:not(:checked) ~ .mc-eye-on,
.mc-view-cb input:checked ~ .mc-eye-off { display: none; }
.mc-grid-cb input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 24px;
    height: 24px;
    border: 1px solid var(--blue5);
    border-radius: 0.25rem;
    background: var(--blue7);
    cursor: pointer;
    transition: background 120ms, border-color 120ms;
    flex-shrink: 0;
}
.mc-grid-cb input[type="checkbox"]:checked {
    background: var(--color-primary-light);
    border-color: var(--color-primary-light);
}

/* ===================== INPUTS ===================== */
#tl-widget {
    position: absolute;
    z-index: 900;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.5rem 0.7rem 0.4rem;
    border-radius: 1rem;
    background: var(--blue8);
    color: var(--blue1);
    box-shadow: var(--dialog-shadow);
    width: fit-content;
    max-width: 92vw;
}
#tl-widget.hide { display: none; }
body.radar-on #tl-widget { display: none; } /* radar playbar borrows the slot — one time axis at a time */
/* Real box is a tall touch band (touch-action:none only reliably suppresses
   page/map panning within the element's actual layout box — a pseudo-element
   painted outside that box does NOT extend the gesture-routing region, so a
   thin 6px bar left fingers landing just off it, falling through to the map
   pan gesture). The visible slim bar is drawn via ::before, centered inside. */
#tl-track {
    position: relative;
    height: 36px;
    margin: 0.25rem 2rem;
    cursor: pointer;
    touch-action: none;
}
#tl-track::before {
    content: '';
    position: absolute;
    left: 0; right: 0; top: 50%;
    height: 6px;
    transform: translateY(-50%);
    border-radius: 999px;
    background: var(--blue6);
    pointer-events: none;
}
#tl-track-fill {
    position: absolute;
    top: 50%; left: 0;
    height: 6px;
    width: 0%;
    border-radius: 999px;
    background: var(--color-primary-light);
    transform: translateY(-50%);
    pointer-events: none;
}
#tl-track-cursor {
    position: absolute;
    top: 50%; left: 0%;
    width: 14px; height: 14px;
    border-radius: 50%;
    background: var(--color-primary-light);
    box-shadow: 0 1px 4px rgba(0,0,0,0.4);
    transform: translate(-50%, -50%);
    pointer-events: none;
}
#tl-controls {
    display: flex;
    align-items: center;
    gap: 0.2rem;
}
.tl-btn {
    background: none;
    border: none;
    color: inherit;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    cursor: pointer;
}
.tl-btn:disabled { opacity: 0.3; cursor: default; }
.tl-btn .tl-ico-pause { display: none; }
.tl-btn.is-playing .tl-ico-play  { display: none; }
.tl-btn.is-playing .tl-ico-pause { display: flex; }
.tl-label {
    background: none;
    border: none;
    color: inherit;
    display: flex;
    align-items: center;
    min-height: 44px;
    min-width: 0;
    padding: 0 0.4rem;
    cursor: pointer;
    font-weight: 600;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
/* width is a preferred (flex-basis) size, not a floor — min-width:0 lets the
   flex item shrink below it under real pressure instead of forcing overflow
   (no truncation shown — it just quietly gives up room to its siblings). */
#tl-date-label {
    display: inline-block;
    width: 12ch;
    min-width: 0;
}
#tl-time-label {
    display: inline-block;
    max-width: 6ch;
    min-width: 0;
}
#tl-date-btn {
    font-size: var(--font-sm);
}
.tl-now {
    background: var(--blue7);
    border: 1px solid var(--blue6);
    color: var(--blue2);
    border-radius: 999px;
    min-height: 32px;
    padding: 0 0.6rem;
    margin-left: 0.15rem;
    cursor: pointer;
}
.tl-now:hover { color: var(--color-primary-ultralight); border-color: var(--color-primary-ultralight); }
.tl-label.picker-open { color: var(--color-primary-ultralight); }
.tp-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.3rem;
    padding: 0.4rem 0;
}
.tp-btn {
    padding: 0.4rem 0.2rem;
    border-radius: 0.3rem;
    border: 1px solid var(--blue7);
    background: var(--blue8);
    color: var(--blue3);
    font-size: var(--font-sm);
    font-family: monospace;
    cursor: pointer;
    text-align: center;
    transition: background 80ms, color 80ms;
}
.tp-btn:hover  { background: var(--blue7); color: var(--blue1); }
.tp-btn.active { background: var(--color-primary-light); border-color: var(--color-primary-light); color: var(--grey0); }

#interval-input,
#points-input,
#speed-input {
    width: 3.8rem;
    padding: 0.5rem 0.4rem;
    border-radius: 0.3rem;
    border: 1px solid var(--blue6);
    background: var(--blue8);
    color: var(--grey0);
    font-size: inherit;
    text-align: center;
}

/* ===================== BUTTONS ===================== */
#fetch-row {
    display: flex;
    flex-direction: column;
    gap: 0;
    border-bottom: none !important;
    padding-bottom: 0 !important;
}
/* Update and Timeline share a centred row; the "Updated:" line sits UNDER the pair. The date used
   to live in a column with Update alone, which pushed Timeline's centre below the button's. */
#fetch-go-row {
    display: grid;
    grid-template-columns: auto auto;
    justify-content: center;
    align-items: center;
    gap: 0.15rem 1rem;
}
#btn-fetch-weather { grid-column: 1; grid-row: 1; }
#btn-tl-visible    { grid-column: 2; grid-row: 1; }
#fetch-info        { grid-column: 1 / -1; grid-row: 2; justify-self: center; }
/* Timeline show/hide — same 2-line block style as the POI toggle (toggle over label) */

#btn-fetch-weather { white-space: normal; }
#btn-fetch-weather span:last-child { width: min-content; } /* wraps "Update Weather" onto 2 lines */
/* ===================== WEATHER PANEL, GROUPED BY DOMAIN (2026-08-12) ===================== */
/* Two blocks, one per domain, because the panel used to alternate weather and track line by line
   with nothing on screen saying so. Portrait stacks them; landscape sets them side by side, which
   is where the grouping pays for itself: the popover is ~250px wide on an 800px screen, so the
   second column costs width nobody was using and the block height becomes the TALLER column
   instead of the sum of both. */
/* NO group headings (Pierre, 2026-08-12). "WEATHER" over Active/Area/Forecast and "TRACK" over
   Track/Pts/Departure said the same word the controls already say, and in English and Spanish the
   heading landed directly above a toggle of the identical name. The ROW is the group: everything on
   one line belongs to one domain, and the gap between rows is what separates them. That gap is
   therefore load-bearing and deliberately larger than the gap INSIDE a group (which only has to
   hold the From row under the track row), so the two domains do not read as one four-line block.
   No separator RULE: the two .wx-sep lines deleted on 2026-08-11 cost 44px for the same job. */
#wx-groups {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-top: 1rem;
    margin-bottom: 0.5rem;
}
.wx-group {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.4rem;
    min-width: 0;
}
/* Each domain is ONE horizontal row of cells, not a column (Pierre's layout, 2026-08-12). The
   column version stacked three 30-64px controls per domain and cost ~150px per block; as a row the
   block is only as tall as its tallest cell, i.e. the 64px door, so the two domains together cost
   about what the single old scope row plus doors row did. The row IS the group. */
.wx-row {
    display: flex;
    align-items: center;
    justify-content: space-around;
    gap: 0.5rem;
    min-width: 0;
}
/* A cell is its label OVER its control, so three of them fit a 375px phone. Side by side (the way
   .toggle-label draws by default) "Active Point" plus a 62px switch is ~130px, and three of those
   would need 390px. Matched to the 64px door height so the row has one baseline. */
.wx-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    min-height: 64px;
    text-align: center;
    min-width: 0;
}
/* .toggle-label is inline-flex with the words beside the switch; .wx-cell overrides it to stack.
   The label must be allowed to wrap here, because .toggle-label sets nowrap for its own row form
   and "Active Point" in German or Spanish would otherwise push the row wider than the panel. */
.wx-cell.toggle-label { white-space: normal; line-height: 1.15; }
#pts-block .nav-opt-lbl { padding: 0; cursor: pointer; }
#pts-block input { width: 3.2rem; padding: 0.25rem 0.2rem; }
/* No track = the whole track block goes, title and all. Leaving a titled block containing nothing
   is worse than the old vanishing controls, which at least sat in a row that still had content. */
#wx-groups:has(input[data-scope="track"]:disabled) .wx-group[data-domain="track"] {
    display: none;
}
/* A door with nothing behind it gives up its slot rather than just turning invisible. Same for a
   switcher entry whose view cannot render. */
.wx-row > button:disabled,
.dlg-switch > button:disabled {
    display: none;
}
/* Design-system classes (.btn-*, .select-group-*, .btn-map) live in
   css/scss/components/_design.scss → compiled into alien.css */
#fetch-info {
    font-size: var(--font-sm);
    text-align: center;
    margin-top: 0.4rem;
}
#last-update-line {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    gap: 0.3rem;
    text-align: center;
    opacity: 0.6;
    white-space: nowrap;
}

/* .toggle-label / .toggle-check-box (the on/off switch) and .btn-eye moved to
   css/scss/components/_design.scss: generic components live there, not here. */

/* ---- info button ---- */
.btn-i {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    border-radius: 50%;
    border: 1px solid var(--blue5);
    background: none;
    color: var(--blue3);
    font-weight: 700;
    font-size: var(--font-xs);
    line-height: 1;
    cursor: pointer;
    flex-shrink: 0;
    transition: border-color 120ms, color 120ms;
}
.btn-i:hover {
    color: var(--color-primary-light);
    border-color: var(--color-primary-light);
    box-shadow: none;
}
.btn-i:active { opacity: 0.5; box-shadow: none; }

/* ---- tooltip ---- */
.tooltip-popup {
    position: fixed;
    z-index: 99999;
    background: var(--grey0);
    color: var(--grey9);
    padding: 0.5em 0.75em;
    border-radius: 0.4rem;
    font-size: var(--font-xs);
    line-height: 1.4;
    max-width: 18rem;
    box-shadow: 0 2px 10px var(--grey9-50);
    pointer-events: auto;
    white-space: pre-line;
}

.tooltip-popup a { color: var(--color-primary-light); }
#btn-gmaps-confirm, #btn-gmaps-cancel { flex: 1; justify-content: center; }

/* ===================== MAP DARK MODE ===================== */
/* Filter on canvas only — markers, track icons and DOM overlays stay full brightness */
#map .maplibregl-canvas {
    filter: saturate(1.5);
    transition: filter 0.6s ease;
}
body.map-dark #map .maplibregl-canvas {
    filter: brightness(0.35) saturate(1);
}
/* Keep terrain-void haze dark in dark map mode so it blends with the darkened canvas */
body.map-dark #map { background-color: var(--color-primary-darker); }

/* Map overlay controls (.btn-map) use the dark nav palette in both map modes —
   no per-mode overrides needed. */

/* ===================== LANGUAGE SELECTOR ===================== */
.lang-selector {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.2rem;
}
.lang-btn {
    cursor: pointer;
    padding: 0.2em 0.5em;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    font-weight: 600;
    letter-spacing: 0.05em;
    color: var(--color-primary-ultralight);
    opacity: 0.4;
    transition: opacity 100ms ease;
    user-select: none;
}
.lang-btn:hover { opacity: 0.75; }
.lang-btn.active {
    font-weight: 800;
    text-decoration: underline;
    text-underline-offset: 0.2em;
    opacity: 1;
}
/* Track-width selector: now in the NAV panel (a legibility control you reach for while
   riding, not a set-once preference). It was a sibling of #activity-section for the same
   reason it is safe here: the travel-time disabled state is scoped to
   #activity-controls/#speed-param, so width never greys with it. */
/* Centred, with the label sitting just left of the pills rather than pushed to the far edge:
   space-between stranded "Track width" against one wall and the pills against the other, which
   read as two unrelated things. Pierre's call after seeing it on the phone. */
#track-width-row {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.6rem;
    padding: 0.6rem 0 0.25rem;
}
/* text-only pills: drop the square aspect the icon buttons use. Sized up when this moved to
   the nav panel: it is now a control you use with gloves on, and 36px is the floor Pierre
   glove-tested for the nav right-hand stack. */
#track-width-row .width-btn {
    aspect-ratio: auto;
    padding: 0.3rem 0.6rem;
    min-width: 2.8rem;
    min-height: 36px;
}
.cc-chip {
    flex-shrink: 0;
    font-size: 0.85em;
    font-weight: 600;
    border: 1px solid var(--blue5);
    border-radius: 3px;
    padding: 0 3px;
    color: var(--blue3);
}
.td-btn {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.45rem 0.75rem;
    border-radius: 0.4rem;
    border: 1px solid var(--blue6);
    background: var(--blue7);
    color: var(--blue2);
    cursor: pointer;
    font-size: var(--font-xs);
    font-family: inherit;
    transition: background 120ms, color 120ms, border-color 120ms;
    white-space: nowrap;
}
.td-btn:disabled { opacity: 0.35; cursor: default; }
.td-btn:hover:not(:disabled) { background: var(--blue7); color: var(--color-primary-ultralight); border-color: var(--color-primary-ultralight); }
.td-btn.active { border-color: var(--color-primary-light); color: var(--color-primary-light); }
.td-btn--primary {
    border-color: transparent;
    background: var(--color-primary-light);
    color: var(--grey0);
    font-weight: 600;
}
.td-btn--primary:hover { background: var(--color-primary); color: var(--blue1); }
#confirm-dialog {
    background: var(--blue8);
    color: var(--grey0);
    border: 1px solid var(--blue6);
    border-radius: 0.85rem;
    padding: 1.25rem;
    min-width: 260px;
    max-width: min(420px, 90vw);
    box-shadow: var(--dialog-shadow);
}
#confirm-dialog:not([open]) { display: none; }
#confirm-dialog::backdrop { background: rgba(0,0,0,0.55); backdrop-filter: blur(2px); }
.confirm-body { display: flex; flex-direction: column; gap: 1rem; }
#confirm-message { margin: 0; font-size: var(--font-sm); line-height: 1.5; color: var(--blue2); }
.confirm-actions { display: flex; gap: 0.5rem; justify-content: flex-end; }
.td-btn--unjoin { margin-top: 0.1rem; }

/* ===================== SUB ACCESS / CHART BUTTON ROWS ===================== */
/* #sub-doors-row was retired 2026-08-12: the two doors were one weather and one track sitting side
   by side, so the domain grouping sent each to its own block. They are now direct children of
   .wx-group and keep their 64x64 .btn-secondary-vtal size there. */

/* Switcher strip inside each dialog family: which view of this room am I looking at, and what
   else is in here. Full width under the header so it reads as navigation, not as a form control. */
.dlg-switch {
    margin: 0 0 0.5rem;
    width: 100%;
    /* NEVER let it shrink. Every dialog in a family is a COLUMN FLEX container, so when the
       content is taller than the dialog (mobile landscape, 375px high) flex crushes whatever is
       allowed to shrink. This strip has no intrinsic min-height of its own, so it was collapsing
       to 2px while its two 36px buttons spilled out and landed on the row below: the switcher
       simply vanished as a row, in landscape only. Found on the Departure dialog 2026-08-13.
       Navigation is chrome, not slack: the LIST is what gives way. */
    flex-shrink: 0;
}
.dlg-switch > button { flex: 1 1 0; min-height: 36px; }

/* MUTED, NOT DISABLED — the 15-min pill where no high-resolution model reaches. `disabled` cannot
   carry this state: the rule above takes a disabled switcher entry out of the row entirely, so the
   pill vanished in Cebu instead of explaining itself. Nor can a `title` tooltip carry the reason —
   there is no hover on a phone, which is where riders are. So the pill stays live, reads as quiet,
   and tapping it opens the panel that says why. Costs nothing: the coarse box answers offline and
   no request is sent. Found on a real device, 2026-08-28. */
.dlg-switch > button.pill-muted { color: var(--blue4); }
.dlg-switch > button.pill-muted:not(.active):hover { background: var(--blue7); color: var(--blue4); }

/* Emergency numbers. Reference you read and tap, not a layer you toggle, so it
   sits behind a divider rather than reading as a seventh hazard checkbox. */
.emg-block { margin-top: 0.9rem; padding-top: 0.75rem; border-top: 1px solid var(--blue6); }
.emg-title { margin: 0 0 0.5rem; font-size: var(--font-sm); font-weight: 600; }
.emg-head { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
.emg-flag { font-size: 1.25rem; line-height: 1; flex-shrink: 0; }
.emg-picker {
    flex: 1; min-width: 0;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--blue6); border-radius: 0.4rem;
    background: var(--blue7); color: var(--blue1);
    font-size: var(--font-sm);
}
/* Country note. Shown only for the six countries where it changes what you DO
   (Vorarlberg's 144, Argentina's provinces, Gabon mobile-vs-landline). Sits
   ABOVE the numbers on purpose: it tells you which of them to trust. */
.emg-note {
    margin: 0 0 0.5rem;
    padding: 0.45rem 0.6rem;
    border-left: 3px solid var(--color-warning-bright);
    border-radius: 0 0.3rem 0.3rem 0;
    background: var(--blue7);
    font-size: var(--font-sm);
    line-height: 1.35;
}
.emg-list { display: flex; flex-direction: column; gap: 0.3rem; }
/* Rounded because it acts: tapping a row dials. Kept tall for gloves. */
.emg-row {
    display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
    min-height: 44px; padding: 0.45rem 0.7rem;
    border: 1px solid var(--blue6); border-radius: 0.5rem;
    background: var(--blue7); color: var(--blue1);
    text-decoration: none;
}
.emg-row:hover { background: var(--blue6); }
.emg-label { font-size: var(--font-sm); }
.emg-num { font-size: 1.05rem; font-weight: 700; letter-spacing: 0.02em; white-space: nowrap; }
.emg-none { margin: 0.3rem 0; font-size: var(--font-sm); opacity: 0.75; }
.emg-src { margin: 0.55rem 0 0; font-size: 0.72rem; opacity: 0.55; }
.emg-src a { color: inherit; }
/* ===================== POINT DETAIL PANEL ===================== */
#point-detail {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
#pd-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}
#pd-icon { width: 40px; height: 40px; flex-shrink: 0; }
#pd-info {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}
#pd-condition {
    font-size: var(--font-xs);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
#pd-when {
    font-size: var(--font-xs);
    opacity: 0.55;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
#pd-stats {
    display: grid;
    grid-template-columns: max-content max-content;
    grid-auto-flow: row dense;
    row-gap: 0.2rem;
    column-gap: 0.4rem;
    align-items: center;
    justify-content: center;
}
.pd-stat { display: contents; }
.pd-lbl {
    grid-column: 1;
    font-size: var(--font-xs);
    opacity: 0.55;
    text-align: right;
    white-space: nowrap;
}
.pd-lbl::after { content: ':'; }
.pd-val {
    grid-column: 2;
    font-size: var(--font-xs);
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 0.15rem;
    white-space: nowrap;
}

/* ===================== WEATHER ROWS (.wl-*) =====================
   Named after the old #weather-list panel, which is gone. These rules are NOT dead:
   wl_item_html() still renders these rows inside the Best departure forecast. What
   went with the panel was its own container, its grouping wrappers and the rules
   scoped under #weather-list (the boxed look, hover, and the open state, none of
   which the departure list ever used). Kept under the old prefix on purpose: renaming
   the classes would touch the markup for no gain. */
.wl-item {
    padding: 0.45rem 0.75rem;
    cursor: pointer;
}
.wl-row {
    display: grid;
    grid-template-columns: 26px 6ch 1fr;
    align-items: center;
    gap: 0.5rem;
}
.wl-icon {
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    border-radius: 50%;
}
.wl-summary {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
}
.wl-condition {
    font-size: var(--font-xs);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.wl-meta {
    font-size: var(--font-xs);
    opacity: 0.55;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.wl-temp {
    font-size: var(--font-sm);
    font-weight: 700;
    white-space: nowrap;
    text-align: center;
}
.wl-details {
    display: none;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.3rem 0.25rem;
    padding: 0.5rem 0 0.2rem;
}
.wl-item--open .wl-details { display: grid; }
.wl-details { padding-left: calc(26px + 0.5rem); }
.wl-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.05rem;
}
.wl-val {
    font-size: var(--font-xs);
    font-weight: 600;
    white-space: nowrap;
}
.wl-wind {
    display: flex;
    align-items: center;
    gap: 2px;
}
.wl-lbl {
    font-size: var(--font-xs);
    opacity: 0.55;
    white-space: nowrap;
}

/* ===================== CHARTS ===================== */
.chart-panel {
    height: 140px;
    position: relative;
}
.chart-panel canvas {
    width: 100% !important;
}
.chart-expand-btn {
    background: var(--color-primary-light);
    border: none;
    border-radius: 0.4rem;
    padding: 0.25rem 0.4rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    color: var(--grey0);
    flex-shrink: 0;
    transition: background 120ms;
    position: relative;
    min-height: 32px;
}
.chart-expand-btn:hover { background: var(--color-primary); }
.chart-expand-btn::before { content: ''; position: absolute; inset: -6px; }
/* elevation: float button over chart */
.chart-panel > .chart-expand-btn {
    position: absolute;
    top: 4px;
    right: 4px;
    z-index: 10;
}
.cfd-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.7rem 1rem;
    font-size: var(--font-sm);
    font-weight: 600;
    border-bottom: 1px solid var(--blue6);
    flex-shrink: 0;
}
#weather-info-dialog[open] {
    border: 1px solid var(--blue6);
    border-radius: 0.85rem;
    background: var(--blue8);
    color: var(--blue1);
    padding: 0;
    width: min(94vw, 480px);
    height: auto;
    max-height: 80dvh;
    box-shadow: var(--dialog-shadow);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
#weather-info-dialog:not([open]) { display: none; }
#weather-info-dialog::backdrop {
    background: rgba(0,0,0,0.6);
    backdrop-filter: blur(2px);
}
#weather-info-content {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 0 0.75rem 0.75rem;
}

/* Weather alert summary strip */
#weather-alerts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    padding: 0.5rem 0 0.25rem;
}
.wa-pill {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.5rem 0.2rem 0.3rem;
    border-radius: 4px;
    font-size: var(--font-xs);
    border: 1px solid;
    cursor: pointer;
    white-space: nowrap;
}
.wa-pill:hover { filter: brightness(1.15); }
.wa-warn   { background: rgba(255,160,0,0.12); border-color: rgba(255,160,0,0.35); color: #d4900a; }
.wa-danger { background: rgba(210,50,50,0.12); border-color: rgba(210,50,50,0.35); color: #c84032; }
.wa-ico    { width: 18px; height: 18px; flex-shrink: 0; }

/* Vertical secondary buttons sit side by side, and their labels wrap to one or two lines
   depending on the word and the language ("Compare" vs "Best departure"). The button box is a
   fixed 64x64 either way, but centring a one-line label against a two-line neighbour drops its
   icon lower and the row looks crooked. Reserving two lines on every label lines the icons up,
   and does it per language rather than by padding a specific string. */
.btn-secondary-vtal > span:not(.svg-ico) {
    min-height: 2.3em;   /* 2 lines at line-height 1.15 */
}

/* 8.2 — "Weather from" selector, in the fetch options above the buttons. */
#weather-from-row {
    display: flex;
    align-items: center;
    justify-content: center;
    /* Landscape gives this row 203px. Label + pill measures 203.0 exactly once both are allowed
       to wrap their words, i.e. it fits with NOTHING to spare, which is not something to trust
       across font metrics. So the row itself may wrap too: if the pill cannot sit beside the
       label on a given device it drops underneath instead of overflowing. Costs ~20px of height
       in landscape, and only when it is actually needed. */
    flex-wrap: wrap;
    gap: 0.35rem;
    padding: 0.35rem 0 0.1rem;
}
/* Wraps to two lines when it has to. Landscape gives this row 203px, and "Weather from" plus a
   one-line pill needs about 250. Nothing is forced: in portrait there is room and it stays on one
   line, which reads better. */
#weather-from-row .wx-from-lbl {
    font-size: var(--font-xs);
    color: var(--blue3);
    line-height: 1.15;
    text-align: center;
    /* Must not shrink: .select-group-type2 clips (overflow:hidden), so a shrinking flex item does not
       wrap, it silently cuts its own words in half. Refusing to shrink is what makes the row
       wrap instead. */
    flex-shrink: 0;
}

/* ===================== ACCURACY TOOLTIP ===================== */
.accuracy-tip {
    background: rgba(0,0,0,0.65);
    border: none;
    border-radius: 4px;
    color: #fff;
    font-size: var(--font-sm);
    padding: 2px 6px;
    white-space: nowrap;
    box-shadow: none;
}
.accuracy-tip::before { display: none; }

/* ===================== ROUTE WEATHER MARKERS ===================== */
.route-marker {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    width: fit-content;
    font-family: "Inter", sans-serif;
    transform: translateX(-50%);
    cursor: pointer;
}
.route-marker-icon {
    display: block;
    border-radius: 50%;
    box-shadow: 0 0 0 3px rgba(255,255,255,0.88), 0 1px 4px rgba(0,0,0,0.28);
}
.route-marker-info {
    display: flex;
    flex-direction: column;
    align-items: center;
    background: rgba(255,255,255,0.92);
    border-radius: 4px;
    padding: 1px 5px 2px;
    box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}
.route-marker-temp {
    font-size: var(--font-sm);
    font-weight: 700;
    color: var(--color-primary-darker);
    white-space: nowrap;
}
.route-marker-time {
    font-size: var(--font-xs);
    color: var(--color-copy-light);
    white-space: nowrap;
}
.route-marker-feels { font-size: var(--font-xs); color: var(--color-copy-lighter); white-space: nowrap; }
.route-marker-cond,
.route-marker-ele  { font-size: var(--font-xs); color: var(--color-copy-light); white-space: nowrap; max-width: 90px; overflow: hidden; text-overflow: ellipsis; }
.route-marker-wind  { font-size: var(--font-xs); color: var(--color-copy-light); white-space: nowrap; display: flex; align-items: center; gap: 2px; }
.route-marker-precip{ font-size: var(--font-xs); color: #337; white-space: nowrap; }
.route-marker-prob  { font-size: var(--font-xs); color: #337; white-space: nowrap; }

.route-marker--active .route-marker-icon {
    box-shadow: 0 0 0 3px var(--color-map-pin), 0 1px 4px rgba(0,0,0,0.28);
}
.rma-info {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
    background: rgba(255,255,255,0.95);
    border-radius: 6px;
    padding: 4px 7px 5px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.28);
}
.rma-temp {
    font-size: var(--font-md);
    font-weight: 700;
    color: var(--color-primary-darker);
    white-space: nowrap;
}
.rma-feels {
    font-size: var(--font-xs);
    color: var(--color-copy-lighter);
    white-space: nowrap;
}
.rma-label,
.rma-ele {
    font-size: var(--font-xs);
    color: var(--color-copy-light);
    text-align: center;
    white-space: nowrap;
    max-width: 110px;
    overflow: hidden;
    text-overflow: ellipsis;
}
.rma-wind {
    display: flex;
    align-items: center;
    gap: 3px;
    font-size: var(--font-sm);
    color: var(--color-copy-light);
    white-space: nowrap;
}
.rma-time {
    font-size: var(--font-xs);
    font-weight: 600;
    color: var(--color-map-pin);
    white-space: nowrap;
}
.rma-precip { font-size: var(--font-xs); color: #337; white-space: nowrap; }
.rma-prob   { font-size: var(--font-xs); color: #337; white-space: nowrap; }

/* ===================== CURRENT CONDITIONS MARKER ===================== */
.cc-marker {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    width: fit-content;
    font-family: "Inter", sans-serif;
    transform: translateX(-50%);
}
.cc-marker-icon {
    display: block;
    border-radius: 50%;
    box-shadow: 0 0 0 2px rgba(255,255,255,0.88), 0 1px 4px rgba(0,0,0,0.28);
}
.cc-marker-info {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    background: rgba(255,255,255,0.92);
    border-radius: 6px;
    padding: 3px 6px 4px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.25);
}
.cc-marker--active .cc-marker-info {
    background: rgba(255,255,255,0.98);
    box-shadow: 0 2px 10px rgba(0,10,150,0.22);
}
/* the place name on the active point's card (2026-09-13): the title line, from the map tiles */
.cc-marker-star { position: absolute; top: -2px; right: 0; width: 22px; height: 22px; padding: 0; border: 0; background: none; color: var(--grey5); font-size: 15px; line-height: 22px; cursor: pointer; }
.cc-marker-star.is-saved { color: #F59E0B; }
.cc-marker--custom .cc-marker-info { position: relative; padding-right: 18px; }
.cc-marker-name { font-weight: 700; font-size: var(--font-sm); color: var(--grey8); line-height: 1.15; white-space: nowrap; max-width: 12rem; overflow: hidden; text-overflow: ellipsis; }
.cc-marker-temp {
    font-size: var(--font-md);
    font-weight: 700;
    color: var(--color-primary-darker);
    white-space: nowrap;
}
.cc-marker-wind {
    display: flex;
    align-items: center;
    gap: 3px;
    font-size: var(--font-sm);
    color: var(--color-copy-light);
    white-space: nowrap;
}
.cc-marker-label,
.cc-marker-ele {
    font-size: var(--font-xs);
    color: var(--color-copy-light);
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
.cc-marker-uv, .route-marker-uv, .rma-uv { font-size: var(--font-xs); color: #b45309; font-weight: 600; }
.cc-marker-time {
    font-size: var(--font-xs);
    font-weight: 600;
    color: var(--color-primary-darker);
    white-space: nowrap;
}
/* Shown only when the point's zone differs from the phone's: quieter than the time it follows. */
.cc-marker-tz     { font-size: var(--font-xs); font-weight: 400; color: var(--color-copy-lighter); white-space: nowrap; margin-left: 4px; }
.cc-marker-feels  { font-size: var(--font-xs); color: var(--color-copy-lighter); white-space: nowrap; }
.cc-marker-precip { font-size: var(--font-xs); color: #337; white-space: nowrap; }
.cc-marker-prob   { font-size: var(--font-xs); color: #337; white-space: nowrap; }

.cc-marker--area .cc-marker-icon { box-shadow: 0 0 0 3px var(--color-secondary), 0 2px 4px rgba(0,0,0,0.28); }
.cc-marker--area .cc-marker-temp { color: var(--color-secondary); }
.cc-marker--area.cc-marker--active .cc-marker-icon { box-shadow: 0 0 0 2px var(--color-map-pin), 0 1px 4px rgba(0,0,0,0.28); }
.cc-marker--area.cc-marker--active .cc-marker-temp { color: var(--color-map-pin); }
.cc-marker--area.cc-marker--active .cc-marker-info { box-shadow: 0 2px 10px rgba(255,0,0,0.22); }

.cc-marker--custom .cc-marker-icon { box-shadow: 0 0 0 2px var(--color-map-pin), 0 1px 4px rgba(0,0,0,0.28); }
.cc-marker--custom .cc-marker-temp { color: var(--color-map-pin); }
.cc-marker--custom .cc-marker-info { box-shadow: 0 2px 10px rgba(255,0,0,0.22); }

/* ===================== PICKERS (popup) ===================== */
#mini-cal, #tl-time-picker, #xp-mini-cal, #xp-time-picker {
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: var(--blue8);
    border: 1px solid var(--blue6);
    border-radius: 0.5rem;
    box-shadow: 0 6px 24px rgba(0,0,0,0.5);
    padding: 0.6rem;
    z-index: 2000;
}
.picker-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin: -0.2rem -0.2rem 0.2rem;
}
.picker-title {
    font-size: var(--font-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-primary-light);
}
.picker-header .close-btn { width: 32px; height: 32px; position: relative; }
.picker-header .close-btn::before { content: ''; position: absolute; inset: -6px; }
.tp-grid { padding: 0; }
.cal-dow-row {
    display: grid;
    grid-template-columns: repeat(7, var(--cal-col));
    gap: 2px;
    text-align: center;
    margin-left: calc(2rem + 0.25rem);
}
.cal-month-block {
    display: flex;
    align-items: flex-start;
    gap: 0.25rem;
}
.cal-month-label {
    width: 2rem;
    min-width: 2rem;
    flex-shrink: 0;
    font-size: var(--font-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    opacity: 0.5;
    text-align: center;
    padding-top: 0.25rem;
    line-height: 1.3;
    word-break: break-word;
}
.cal-days-grid {
    display: grid;
    grid-template-columns: repeat(7, var(--cal-col));
    gap: 2px;
    text-align: center;
}
.cal-dow {
    font-size: var(--font-xs);
    opacity: 0.4;
    padding: 2px 0;
}
.cal-day {
    font-size: var(--font-sm);
    padding: 2px 0;
    border-radius: 50%;
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 120ms;
}
.cal-day.forecast { background: var(--blue7); cursor: pointer; }
.cal-day.forecast:hover { background: var(--blue6); }
.cal-day.out { opacity: 0.2; }
.cal-day.selected { background: var(--color-primary-light); font-weight: 600; }
.cal-day.selected:hover { background: var(--color-primary-light); }
.cal-day.today { background: var(--color-primary); color: #fff; font-weight: 700; }
.cal-day.today:hover { background: var(--color-primary); }

/* ---- coords popup ---- */
.coords-copy {
    background: none;
    border: none;
    color: #333;
    font: inherit;
    cursor: pointer;
    padding: 0;
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}
.coords-copy:hover { color: var(--color-primary-light); }

/* Name read from the vector tiles. Light popup bg → force dark text, like .poi-popup-name.
   Road first, settlement under it: same order as a written address, and the same order
   the search string is built in. Either line can be absent. */
.coords-road, .coords-place { margin-top: 0.35rem; line-height: 1.3; }
.coords-road  { font-weight: 600; color: #000; }
.coords-place { color: #333; }
.coords-link {
    display: block;
    margin-top: 0.4rem;
    color: var(--color-primary);
    font: inherit;
    text-decoration: none;
}
.coords-link:hover { color: var(--color-primary-light); text-decoration: underline; }

/* ===================== AUTH PANEL ===================== */
#auth-tabs {
    display: flex;
    border-radius: 0.35rem;
    overflow: hidden;
    border: 1px solid var(--blue6);
}
.auth-tab {
    flex: 1;
    padding: 0.4rem;
    border: none;
    background: none;
    color: var(--blue3);
    font-size: var(--font-sm);
    cursor: pointer;
    transition: background 120ms, color 120ms;
}
.auth-tab.active {
    background: var(--color-primary-light);
    color: var(--grey0);
}
#auth-email,
#auth-password,
#change-pw-current,
#change-pw-new {
    width: 100%;
    padding: 0.75rem 0.6rem;     /* roomier: these are tapped on a phone, often one-handed */
    min-height: 44px;            /* ...and a proper touch target; they were 36px */
    border-radius: 0.35rem;
    border: 1px solid var(--blue6);
    background: var(--blue8);
    color: var(--grey0);
    /* was `inherit`, i.e. the body's 13px. --font-md is also 1rem, so it would have changed
       nothing: --font-lg is the first step that is actually bigger. */
    font-size: var(--font-lg);
    font-family: inherit;
    color-scheme: dark;
    box-sizing: border-box;
}
.password-wrap {
    position: relative;
}
/* Any field inside a password-wrap leaves room for its eye button, not just #auth-password:
   the change-password form reuses the same wrapper. */
.password-wrap input {
    padding-right: 2.2rem;
}
#btn-toggle-password,
.pw-eye {
    position: absolute;
    right: 0.4rem;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    cursor: pointer;
    color: var(--blue4);
    padding: 0.2rem;
    display: flex;
    align-items: center;
}
#btn-toggle-password .svg-ico,
.pw-eye .svg-ico { width: 18px; height: 18px; }
#btn-toggle-password:hover,
.pw-eye:hover { color: var(--blue1); }

/* Change password, account view (added 2026-08-17 with inc/api/password_change.php). The fields
   deliberately inherit the sign-in sizing above: 44px touch target and --font-lg, because this is
   tapped one-handed on a phone just like signing in. */
#change-pw-section,
#change-pw-form {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
#change-pw-msg {
    font-size: var(--font-sm);
    color: var(--blue3);
}
#change-pw-msg.error { color: #ff6b6b; }

#btn-auth-submit,
#btn-change-pw-submit {
    width: 100%;
    padding: 0.45rem;
    border: none;
    border-radius: 0.35rem;
    background: var(--color-primary-light);
    color: var(--grey0);
    font-size: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: background 120ms;
}
#btn-auth-submit:hover,
#btn-change-pw-submit:hover { background: var(--color-primary); color: var(--blue1); }
.auth-divider {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: var(--font-xs);
    color: var(--blue5);
}
.auth-divider::before,
.auth-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--blue7);
}
#btn-google {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.45rem;
    border: 1px solid var(--blue6);
    border-radius: 0.35rem;
    background: var(--grey0);
    color: var(--grey9);
    font-size: var(--font-sm);
    font-weight: 500;
    cursor: pointer;
    transition: background 120ms;
}
#btn-google:hover { background: var(--blue1); }
.auth-error {
    font-size: var(--font-xs);
    color: var(--color-error-content);
    background: var(--color-error);
    padding: 0.35rem 0.5rem;
    border-radius: 0.3rem;
    text-align: center;
}

/* ---- Plan comparison ---- */
.auth-plans {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--blue7);
}
.auth-plan {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: var(--font-xs);
    color: var(--blue4);
    line-height: 1.45;
}
.auth-plan-title {
    font-weight: 600;
    color: var(--blue2);
    margin-bottom: 0.15rem;
}
.auth-plan-price {
    font-weight: 600;
    color: var(--blue2);
    margin-bottom: 0.2rem;
}
.auth-plan-storage-note {
    font-style: italic;
    opacity: 0.7;
}

/* ---- Subscription ---- */
#subscription-row {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}
#subscription-status {
    font-size: var(--font-xs);
    opacity: 0.6;
}
#subscription-status[data-status="active"]    { color: var(--color-primary-light); opacity: 1; }
#subscription-status[data-status="past_due"]  { color: #ff9800; opacity: 1; }
#subscription-status[data-status="cancelled"] { opacity: 0.4; }
#plan-picker {
    display: flex;
    gap: 0.4rem;
}
.plan-btn {
    flex: 1;
    padding: 0.35rem 0.3rem;
    border: 1px solid var(--blue6);
    border-radius: 0.35rem;
    background: none;
    color: var(--blue4);
    font-size: var(--font-xs);
    font-family: inherit;
    cursor: pointer;
    text-align: center;
    transition: background 120ms, color 120ms, border-color 120ms;
}
.plan-btn:hover { color: var(--blue1); border-color: var(--blue4); }
.plan-btn.active {
    background: var(--color-primary-light);
    border-color: var(--color-primary-light);
    color: var(--grey0);
}
#profile-actions-row {
    display: flex;
    gap: 0.4rem;
}
#btn-subscribe,
#btn-manage-sub,
#btn-logout,
#btn-change-pw {
    flex: 1;
    padding: 0.55rem 0.4rem;
    border-radius: 0.35rem;
    border: 1px solid var(--blue6);
    background: none;
    color: var(--blue3);
    font-size: var(--font-sm);
    font-family: inherit;
    cursor: pointer;
    text-align: center;
    transition: background 120ms, border-color 120ms;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
#btn-subscribe:hover,
#btn-manage-sub:hover,
#btn-logout:hover,
#btn-change-pw:hover { background: var(--blue7); border-color: var(--blue4); }
#btn-subscribe:disabled,
#btn-manage-sub:disabled { opacity: 0.5; cursor: wait; }
#subscription-warning {
    font-size: var(--font-xs);
    color: #ff9800;
    background: rgba(255, 152, 0, 0.1);
    padding: 0.35rem 0.5rem;
    border-radius: 0.3rem;
    border: 1px solid rgba(255, 152, 0, 0.3);
}

/* ---- User profile ---- */
#user-info-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}
#user-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}
#user-name {
    font-weight: 600;
    font-size: var(--font-sm);
}
#user-email {
    font-size: var(--font-xs);
    opacity: 0.65;
}

/* ===================== SYNC STATUS ===================== */
/* Base look is .btn-secondary-hztal, exactly like its neighbours in the User panel — per the
   component-class rule, the id carries no appearance of its own. Only the STATE is bespoke:
   amber and pressable when there is something to save, green and inert when there is not. */
/* The label changes length with the count, and this sits in a column of fixed-width buttons —
   so it must wrap rather than widen and break the alignment of the whole panel. */
#sync-status { white-space: normal; }
#sync-status.is-unsaved {
    color: var(--color-warning-bright);
    border-color: var(--color-warning-bright);
    cursor: pointer;
}
#sync-status.is-unsaved:hover { color: var(--color-warning-bright); border-color: var(--color-warning-bright); }
#sync-status.is-saved  { color: var(--color-success-bright); cursor: default; }
/* Nothing to press, so the shared hover must not imply otherwise. */
#sync-status.is-saved:hover  { color: var(--color-success-bright); border-color: var(--blue6); }
#sync-status.is-saving { opacity: 0.7; cursor: default; }
/* The icon carries the state too, so the row reads at a glance rather than needing the label to
   be read. .btn-secondary-hztal tints .svg-ico with --color-primary-dark and brightens it on
   hover, both of which would otherwise fight the state colour. */
#sync-status.is-unsaved .svg-ico, #sync-status.is-unsaved:hover .svg-ico { color: var(--color-warning-bright); }
#sync-status.is-saved   .svg-ico, #sync-status.is-saved:hover   .svg-ico { color: var(--color-success-bright); }
#sync-status.is-saving:hover { border-color: var(--blue6); }

/* ===================== FETCH LIMIT MODAL ===================== */
/* Blocking questions (point limit, delete account). Stretched to the viewport so a click beside the
   box still lands on the dialog itself, which is what the existing dismiss handlers test for.
   z-index is deliberately absent: these are top-layer now, and mixing the two is what hid them. */
.modal-dialog {
    position: fixed;
    inset: 0;
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    border: none;
    padding: 0;
    background: transparent;
    display: none;
    align-items: center;
    justify-content: center;
}
.modal-dialog[open] { display: flex; }
.modal-dialog::backdrop { background: rgba(0,0,0,0.6); backdrop-filter: blur(2px); }
.modal-box {
    background: var(--blue8);
    border: 1px solid var(--blue6);
    border-radius: 0.85rem;
    padding: 1.2rem;
    max-width: 320px;
    width: 90%;
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
    box-shadow: var(--dialog-shadow);
    color: var(--blue1);
}
.modal-box p {
    margin: 0;
    font-size: var(--font-sm);
    color: var(--blue2);
    line-height: 1.4;
}
.modal-btns {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.modal-btns button {
    padding: 0.75rem 0.8rem;
    min-height: 44px;
    border-radius: 0.35rem;
    border: 1px solid var(--blue6);
    background: none;
    color: var(--blue2);
    font-size: var(--font-sm);
    font-family: inherit;
    cursor: pointer;
    text-align: center;
    transition: background 120ms;
}
.modal-btns button:hover { background: var(--blue8); }
/* #btn-fetch-spread / #btn-fetch-cancel rules removed in v872 with the point-limit box. The
   .modal-btns frame above stays: #delete-account-modal still uses it. */
#btn-delete-confirm { border-color: #b93535; color: #f77; }
#btn-delete-confirm:hover { background: #2a0a0a; }

/* ---- Subscribe terms note ---- */
#subscribe-terms { font-size: var(--font-xs); color: var(--blue5); text-align: center; }
#subscribe-terms a { color: var(--blue5); }
#subscribe-terms a:hover { color: var(--blue3); }

/* ---- Forgot password ---- */
#btn-forgot-pw {
    background: none;
    border: none;
    color: var(--blue5);
    font-size: var(--font-xs);
    cursor: pointer;
    padding: 0;
    font-family: inherit;
    text-decoration: underline;
    text-align: left;
}
#btn-forgot-pw:hover { color: var(--blue3); }
#forgot-pw-section { display: flex; flex-direction: column; gap: 0.6rem; }
#forgot-pw-section p { font-size: var(--font-sm); color: var(--blue4); }
#forgot-pw-section input[type="email"] { width: 100%; }
#forgot-pw-msg { font-size: var(--font-xs); color: var(--blue3); text-align: center; padding: 0.25rem 0; }
#forgot-pw-msg.error { color: #f77; }
#btn-forgot-pw-back {
    background: none;
    border: none;
    color: var(--blue5);
    font-size: var(--font-xs);
    cursor: pointer;
    padding: 0;
    font-family: inherit;
    text-align: left;
}
#btn-forgot-pw-back:hover { color: var(--blue3); }

/* ---- Delete account ---- */
#account-danger {
    border-top: 1px solid var(--blue7);
    padding-top: 0.75rem;
    margin-top: 0.5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
}
#btn-delete-account {
    background: none;
    border: none;
    color: var(--blue5);
    font-size: var(--font-xs);
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    text-decoration: underline;
    font-family: inherit;
}
#btn-delete-account:hover { color: #f77; }

/* ---- Toast notifications ---- */
#toast {
    position: fixed;
    top: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9999;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    pointer-events: none;
}
.toast-msg {
    pointer-events: auto;
    padding: 0.6rem 1rem;
    border-radius: 0.4rem;
    font-size: var(--font-sm);
    line-height: 1.4;
    max-width: min(90vw, 360px);
    text-align: center;
    cursor: pointer;
    opacity: 0;
    transform: translateY(-0.5rem);
    transition: opacity 200ms, transform 200ms;
    box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}
.toast-msg.show { opacity: 1; transform: translateY(0); }
.toast-msg.toast-error   { background: #b93535; color: #fff; }
.toast-msg.toast-success { background: #2a7a3b; color: #fff; }
.toast-msg.toast-info    { background: var(--blue7); color: var(--blue1); border: 1px solid var(--blue5); }
.toast-msg.toast-warning { background: var(--color-warning); color: var(--color-warning-content); border: 1px solid var(--color-warning-bright); }

/* ===================== EXPRESS WIZARD ===================== */
/* Quick start dialog */
#quickstart-dialog:not([open]) { display: none; }
#quickstart-dialog[open] {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--blue6);
    border-radius: 0.85rem;
    background: var(--blue8);
    color: var(--blue1);
    padding: 0;
    width: min(92vw, 360px);
    box-shadow: var(--dialog-shadow);
}
#quickstart-dialog::backdrop { background: rgba(0,0,0,0.55); backdrop-filter: blur(2px); }
.qs-header-title { display: flex; align-items: center; gap: 0.45rem; font-size: var(--font-lg); }
.qs-header-title .svg-ico { width: 18px; height: 18px; color: var(--color-primary-light); }
.qs-sub {
    font-size: var(--font-md);
    color: var(--blue3);
    padding: 0 1rem 1rem;
}
.qs-options {
    display: grid;
    grid-template-columns: max-content 1fr;
    align-items: center;
    gap: 0.75rem;
    padding: 0 1rem 1.25rem;
}
.qs-opt-sub { min-width: 0; font-size: var(--font-sm); color: var(--blue3); }

/* ===================== FIRST-RUN WELCOME BOX ===================== */
/* Same shell as #quickstart-dialog on purpose: it is the other half of the same first-run moment,
   and two greeting boxes that did not match would read as two different apps. */
#welcome-dialog:not([open]) { display: none; }
#welcome-dialog[open] {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--blue6);
    border-radius: 0.85rem;
    background: var(--blue8);
    color: var(--blue1);
    padding: 0;
    width: min(92vw, 360px);
    box-shadow: var(--dialog-shadow);
}
#welcome-dialog::backdrop { background: rgba(0,0,0,0.55); backdrop-filter: blur(2px); }

/* The wand row. Its own data-section="map" makes the wand neutral, because that is what it actually
   is out on the map (#coords-row is map chrome). Six hued tiles below + one neutral icon above =
   the colour rule stated without a sentence explaining it. */
.wc-start {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 0 1rem 1.25rem;
    padding: 0 0.25rem;
}
/* The wand drawn as the BUTTON it is, not as a decorated row (Pierre, 2026-08-12): same 34px chip,
   border, fill, radius and shadow as a real .btn-map, so what the box shows is the thing he has to
   go and find. The border belongs to the wand alone — putting it round the whole row framed the
   sentence instead of the control.
   NOT given the .btn-map class: nothing in this dialog is clickable, so no cursor, hover or
   transition comes with it. The only difference from the map is the icon colour: --blue2 out there
   at rest, --color-primary-light here, which is the same hue .btn-map takes when it is pressed. So
   the box shows the button already lit, which is the "slight colour change" and also a hint. */
.wc-wand {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.3rem;
    border-radius: 0.4rem;
    border: 1px solid var(--blue6);
    background: var(--blue8-90);
    box-shadow: 0 2px 8px var(--grey9-25);
    line-height: 0;
}
.wc-wand .svg-ico { width: 24px; height: 24px; color: var(--color-primary-light); }
.wc-start-txt { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.wc-start-txt b { font-size: var(--font-md); color: var(--blue1); }
.wc-start-txt span { font-size: var(--font-sm); color: var(--blue3); line-height: 1.25; }

/* ---- First-run legend (2026-08-12) ----
   Three columns, two rows: the same six items the rail carries, in the same order, so the box and
   the rail can be read against each other. Each tile's hue comes from its own [data-section] scope,
   which is the point of the whole exercise — this is the ONE screen where all six hues are on show
   at once, and after it green means gpx everywhere else in the app.
   NOT interactive (2026-08-12): these were <button>s that opened their panel, which made the box
   compete with the wand it is pointing at and offered doors into panels that are empty on a first
   run. They are <span>s now — no cursor, no hover, no focus ring. The rail itself is six permanent
   buttons at the edge of the screen, so a legend has no reason to be a second way to press them. */
.qs-legend-sub {
    font-size: var(--font-sm);
    color: var(--blue4);
    padding: 0 1rem 0.5rem;
    margin: 0;
}
.qs-legend {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
    padding: 0 1rem 1.25rem;
}
.qs-leg {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    padding: 0.5rem 0.25rem;
    border: 1px solid var(--blue6);
    border-radius: 0.5rem;
    background: none;
    color: var(--blue2);
    font-size: var(--font-xs);
    line-height: 1.2;
    text-align: center;
}
.qs-leg .svg-ico, .qs-leg .wgpx-mark { color: var(--color-primary-light); }

/* ===================== LOGIN / ACCOUNT DIALOG ===================== */
#login-dialog[open] {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--blue6);
    border-radius: 0.85rem;
    background: var(--blue8);
    color: var(--blue1);
    padding: 0;
    width: min(92vw, 400px);
    max-height: 88dvh;
    box-shadow: var(--dialog-shadow);
}
#login-dialog::backdrop { background: rgba(0,0,0,0.55); backdrop-filter: blur(2px); }
.login-dialog-body { overflow-y: auto; padding: 0 1rem 1rem; }
#btn-account { justify-content: center; }

/* Extra apps panel (accordions added in step 3) */
.xtra-apps-placeholder { padding: 1.5rem 0; text-align: center; font-size: var(--font-sm); color: var(--blue3); }
.xtra-accordion { border-bottom: 1px solid var(--blue7); }
.xtra-acc-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.9rem 0.25rem;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--blue1);
    font-size: var(--font-md);
}
.xtra-acc-icon { width: 20px; height: 20px; color: var(--color-primary-light); flex-shrink: 0; }
.xtra-acc-title { flex: 1; text-align: left; font-weight: 600; }
.xtra-acc-chevron { width: 18px; height: 18px; color: var(--blue3); transition: transform 180ms; flex-shrink: 0; }
.xtra-acc-launch { width: 16px; height: 16px; color: var(--blue3); flex-shrink: 0; }
.xtra-accordion.open .xtra-acc-chevron { transform: rotate(180deg); }
.xtra-acc-body { display: none; padding: 0 0.25rem 0.75rem; }
.xtra-accordion.open .xtra-acc-body { display: block; }

.xp-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.85rem 1rem 0;
    font-size: var(--font-md);
    font-weight: 600;
    color: var(--blue1);
    flex-shrink: 0;
}
.close-btn {
    width: 44px;
    height: 44px;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--blue3);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: color 120ms;
}
.close-btn:hover { color: var(--grey0); }
.close-btn:focus { outline: none; }
/* Daily dialog header: prev/next day arrows grouped with the close button */
.cfd-header-btns { display: flex; align-items: center; }
.cfd-header-btns .close-btn:disabled { opacity: 0.25; cursor: default; }
.cfd-header-btns .close-btn:disabled:hover { color: var(--blue3); }

sup.day-offset { font-size: 0.65em; color: var(--color-primary-light); font-weight: 600; vertical-align: super; line-height: 0; }

/* Print button in settings panel — .btn-secondary-hztal supplies the visuals;
   .nav-controls stretches flex children full-width by default, so this just
   opts the button out of that to center it on its own line. */
#btn-print-weather { align-self: center; margin-top: 0.35rem; }
.settings-sep { border: none; border-top: 1px solid var(--blue6); margin: 0.65rem 0 0; }
.pa-header { font-size: var(--font-md); margin-bottom: 0.5rem; }
.pa-title { font-weight: 700; font-size: var(--font-lg); }
.pa-dep { display: block; color: #555; font-size: var(--font-sm); margin-top: 0.15rem; }
.pa-sep { border: none; border-top: 1px solid #ccc; margin: 0.4cm 0; }
.pa-no-data { color: #555; font-style: italic; }

/* ===================== LIVE NAVIGATION ===================== */
body.nav-mode #nav { display: none !important; }

/* Larger controls during nav */
body.nav-mode .ico-size { width: 45px !important; height: 45px !important; }
body.nav-mode #coords-input { font-size: var(--font-xl); width: 20ch; }
/* Nav-mode has its own glove-sized +/- on the left edge, so hide MapLibre's
   pair. Target only the group that CONTAINS the zoom buttons — relief, 3D and
   hazards each sit in their own .maplibregl-ctrl-group and must stay visible. */
body.nav-mode .maplibregl-ctrl-group:has(.maplibregl-ctrl-zoom-in) { display: none; }

/* Nav control stacks — separated vertical strips in the top corners, so the
   middle of the screen stays for the map and the track:
     LEFT  = ride controls (+ / recenter / -), the BT remote's three actions,
             48px because these are the ones tapped with gloves while moving
     RIGHT = session controls (stop nav, HUD weather eye), 36px and spaced to
             continue MapLibre's relief / 3D / hazards column below them, so the
             whole right edge reads as one uniform stack
   Glove test 2026-08-05: 48px with 8px gaps was fine with summer gloves, marginal
   with mid-season and poor with winter gloves. Pierre's call: go **56px flush** on
   the ride stack, trading the gaps for target size, since these three are the only
   ones pressed while moving. The session stack keeps 36px + gaps: it is touched
   with the engine off, and its spacing is what keeps the right edge uniform. */
#nav-left-controls,
#nav-right-controls {
    position: fixed;
    top: 0.75rem;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    pointer-events: none;
}
#nav-left-controls  {
    left: 0.75rem;
    gap: 0;                    /* flush — winter gloves want size, not separation */
    box-shadow: 0 2px 8px rgba(0,0,0,0.2);
    border-radius: 0.4rem;
}
#nav-right-controls { right: 0.75rem; gap: 10px; }  /* 36 + 10 = MapLibre's 45px pitch below */

/* .btn-map supplies the visuals; only sizing lives here. */
#nav-left-controls  .btn-map { width: 56px; height: 56px; padding: 0; border-radius: 0; box-shadow: none; }
#nav-right-controls .btn-map { width: 36px; height: 36px; padding: 0; }

/* Flush strip: round only the outer corners, and overlap the shared borders */
#nav-left-controls > .btn-map:first-child { border-radius: 0.4rem 0.4rem 0 0; }
#nav-left-controls > .btn-map:last-child  { border-radius: 0 0 0.4rem 0.4rem; }
#nav-left-controls > .btn-map + .btn-map  { margin-top: -1px; }

/* Separator between the three ride buttons. The collapsed .btn-map border is
   --blue6 on a --blue8-90 background: two near-identical darks, so the strip
   reads as one undivided block. Draw an inset line in a lighter shade instead,
   so the three targets are visually distinct while staying flush (winter gloves
   want the size, the eye wants the boundary). Held after the 2026-08-11 ground
   swap: the two stops are still adjacent, the hue is the only thing that moved.
   The transparent border-top leaves no hole: background-clip is border-box, so
   the button's own background still paints under it. */
#nav-left-controls > .btn-map + .btn-map { position: relative; border-top-color: transparent; }
#nav-left-controls > .btn-map + .btn-map::before {
    content: '';
    position: absolute;
    top: -1px;
    left: 10px;
    right: 10px;
    height: 1px;
    background: var(--blue5);
    pointer-events: none;
}

/* Explicit icon size: body.nav-mode blows .ico-size up to 45px, which overflowed
   the old 36px strip (measured 34x45 in a 36px box). Scope it back per stack. */
#nav-left-controls .btn-map .svg-ico,
#nav-left-controls .btn-map svg   { width: 28px !important; height: 28px !important; }
#nav-right-controls .btn-map .svg-ico,
#nav-right-controls .btn-map svg  { width: 22px !important; height: 22px !important; }

/* Continue the right column's rhythm: 2 x 36px buttons + 2 x 10px gaps, so the
   first MapLibre group lands exactly one pitch below the eye button.
   UPDATE THIS if a button is ever added to #nav-right-controls. */
body.nav-mode .maplibregl-ctrl-top-right { margin-top: 92px; }

/* Recenter moved to the ride stack, so drop it from the coords row during nav;
   the orientation toggle takes its slot instead. */
body.nav-mode #btn-my-location { display: none; }

/* coords-row keeps the smaller size — those are set-and-forget controls */
body.nav-mode #coords-row button { width: 36px; height: 36px; padding: 0; }

/* HUD — configurable grid */
#nav-hud {
    position: fixed;
    bottom: 0.5rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1100;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    min-width: 220px;
}
/* The two HUD bars share the glassy box styling */
#nav-hud-grid, #nav-hud-weather {
    background: var(--blue8-90);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--blue6);
    border-radius: 1rem;
    color: var(--blue1);
    box-shadow: 0 4px 20px var(--grey9-50);
}
#nav-hud-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.4rem 0.75rem;
    padding: 0.5rem 0.9rem;
}
/* Weather line — its own centered bar below the values */
#nav-hud-weather {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.4rem 0.85rem;
    font-size: var(--font-xl);
    color: var(--blue2);
}
/* Alerts (curve / off-track / wrong-dir / no-GPS) live inside the HUD flex
   container — drop their standalone fixed positioning so flexbox lays them out
   with the bars. */
#nav-hud > #nav-curve,
#nav-hud > #nav-offtrack,
#nav-hud > #nav-wrongdir,
#nav-hud > #nav-nogps {
    position: static;
    inset: auto;
    transform: none;
    z-index: auto;
    margin: 0;
}
.nav-hud-cell { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.nav-hud-cell.hud-cell-sm { align-self: end; }
.nav-hud-cell.hud-cell-sm .nav-hud-cell-value { font-size: var(--font-xxl); }
.nav-hud-cell.hud-span-2 { grid-column: span 2; }
.nav-hud-cell.hud-span-4 {
    grid-column: 1 / -1;
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
    padding-top: 0.3rem;
    margin-top: 0.1rem;
    border-top: 1px solid var(--grey0-10);
}
.nav-hud-cell-label {
    font-size: var(--font-xs);
    color: var(--color-primary-ultralight);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    line-height: 1;
}
.nav-hud-cell-value { font-size: var(--font-xxxl); font-weight: 700; line-height: 1.2; white-space: nowrap; }
.nav-hud-inline-label {
    font-size: var(--font-xs);
    color: var(--color-primary-ultralight);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
    flex-shrink: 0;
}
.nav-hud-weather-row {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    font-size: var(--font-xl);
    color: var(--blue2);
}
.nav-hud-dot { opacity: 0.4; }

/* Global live route button */
#nav-go-row {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
}
.nav-voice-block {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
}
.nav-voice-lbl {
    font-size: var(--font-sm);
    color: var(--blue3);
    white-space: nowrap;
}
.nav-voice-toggle {
    min-height: auto;
    padding: 2px 0;
}
.nav-voice-toggle:has(input:checked) .toggle-check-box::after { background: var(--color-success-bright); }
.nav-voice-toggle:not(:has(input:disabled)):has(input:checked):hover .toggle-check-box::after { background: var(--color-success-bright-strong); }

/* Nav options: rows of option cells (label above its toggle). Each row shares its width
   equally between its cells, so a row of 3 sits on thirds and a row of 2 on halves, both
   evenly spread. Adding an option = one more .nav-opt; the row re-spaces itself. */
#nav-options {
    display: flex;
    flex-direction: column;
    margin-top: 0.5rem;
}
.nav-opt-row {
    display: flex;
    align-items: flex-start;
}
/* The eye row holds two natural-width buttons, not equal-share cells: centred as a pair with a
   small gap, so they read as one group rather than two islands pushed to the edges. */
.nav-opt-row:has(> .btn-eye) {
    justify-content: center;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.4rem;
}
.nav-opt {
    flex: 1 1 0;        /* equal shares: 3-per-row = thirds, 2-per-row = halves */
    min-width: 0;       /* a long word must wrap, never widen its own cell */
    display: flex;
    flex-direction: column;
    align-items: center;
}
.nav-opt-lbl {
    font-size: var(--font-xs);
    color: var(--blue3);
    text-align: center;
    padding: 0.3rem 0.1rem 0;
    line-height: 1.25;
    overflow-wrap: anywhere;
}
#nav-options .toggle-label {
    min-height: 44px;   /* touch target */
    display: flex;
    align-items: center;
}

/* Off-track alert */
#nav-offtrack {
    position: fixed;
    bottom: 6rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1101;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    background: var(--color-error-bright);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--grey0-25);
    border-radius: 2rem;
    padding: 0.8rem 1.5rem;
    color: var(--grey0);
    font-size: var(--font-xxl);   /* deliberately large: if it covers more map, the rider is off
                                     the route anyway, so the map matters less than the warning */
    font-weight: 700;
    box-shadow: 0 4px 20px var(--grey9-50);
    white-space: nowrap;
    pointer-events: none;
}
.nav-offtrack-dist { opacity: 0.85; font-weight: 400; }

/* Wrong direction warning */
#nav-wrongdir {
    position: fixed;
    bottom: 6rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1101;
    display: flex;
    align-items: center;
    background: var(--color-warning-bright);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--grey0-25);
    border-radius: 2rem;
    padding: 0.7rem 1.4rem;
    color: var(--grey0);
    font-size: var(--font-lg);
    font-weight: 700;
    box-shadow: 0 4px 20px var(--grey9-50);
    white-space: nowrap;
    pointer-events: none;
}

/* No GPS signal. Originally muted navy on the reasoning that losing signal in a tunnel is normal
   and should not look like a fault. Ride-tested 2026-08-07 and Pierre's call reverses that: at
   speed, with a helmet on, it has to be READ, and "the app has stopped knowing where you are" is
   worth a warning. Uses the warning pair (olive / bright yellow) rather than the orange of
   #nav-wrongdir, so the two stay tellable apart at a glance. */
#nav-nogps {
    position: fixed;
    bottom: 6rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1101;
    display: flex;
    align-items: center;
    background: var(--color-warning);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--color-warning-bright);
    border-radius: 2rem;
    padding: 0.8rem 1.5rem;
    color: var(--color-warning-content);
    font-size: var(--font-xxl);
    font-weight: 700;
    box-shadow: 0 4px 20px var(--grey9-50);
    white-space: nowrap;
    pointer-events: none;
}

/* Curve-ahead panel */
#nav-curve {
    position: fixed;
    bottom: 6rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1100;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    background: var(--blue8-90);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--blue6);
    border-radius: 1rem;
    padding: 0.5rem 0.75rem;
    color: var(--blue1);
    box-shadow: 0 4px 20px var(--grey9-50);
    white-space: nowrap;
    pointer-events: none;
}
.nav-curve-ico svg { display: block; width: 24px; height: 24px; fill: currentColor; }
.nav-curve-info { display: flex; flex-direction: column; gap: 0.05rem; }
.nav-curve-deg  { font-size: var(--font-sm); font-weight: 700; }
.nav-curve-dist { font-size: var(--font-xs); color: var(--blue3); }

/* Nav position marker */
.nav-arrow-marker { display: flex; align-items: center; justify-content: center; }

/* ===================== NAV SETTINGS DIALOG ===================== */
#nav-settings-dialog:not([open]) { display: none; }
#nav-settings-dialog[open] {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--blue6);
    border-radius: 0.85rem;
    background: var(--blue8);
    color: var(--blue1);
    padding: 0;
    width: min(92vw, 360px);
    box-shadow: var(--dialog-shadow);
    overflow: hidden;
}
#nav-settings-dialog::backdrop {
    background: rgba(0,0,0,0.6);
    backdrop-filter: blur(2px);
}
.nsd-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.7rem 1rem;
    font-size: var(--font-sm);
    font-weight: 600;
    border-bottom: 1px solid var(--blue6);
    flex-shrink: 0;
}
.nsd-close {
    background: none;
    border: none;
    color: var(--blue2);
    font-size: var(--font-xxl);
    line-height: 1;
    cursor: pointer;
    padding: 0 0.15rem;
}
.nsd-close:hover { color: var(--grey0); }
.nsd-items {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    padding: 0.85rem 1rem 0.5rem;
}
.nsd-item {
    background: var(--blue7);
    border: 1px solid var(--blue6);
    border-radius: 2rem;
    color: var(--blue2);
    font-size: var(--font-xs);
    padding: 0.3rem 0.75rem;
    cursor: pointer;
    min-height: 32px;
    transition: background 120ms, color 120ms, border-color 120ms;
}
.nsd-item:hover { background: var(--blue6); color: var(--grey0); }
.nsd-item.nsd-item-in-grid {
    background: var(--blue6);
    border-color: var(--color-primary-light);
    color: var(--color-primary-light);
}
.nsd-hint {
    font-size: var(--font-xs);
    color: var(--blue4);
    padding: 0 1rem 0.5rem;
    margin: 0;
}
#nsd-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
    padding: 0 1rem 0.85rem;
}
.nsd-cell {
    background: var(--blue7);
    border: 1px dashed var(--blue6);
    border-radius: 0.5rem;
    min-height: 52px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.4rem 0.5rem;
    position: relative;
    cursor: pointer;
    transition: background 120ms;
}
.nsd-cell:hover { background: var(--blue6); }
.nsd-cell.nsd-cell-span { grid-column: 1 / -1; }
.nsd-cell-empty { font-size: var(--font-lg); color: var(--blue5); line-height: 1; }
.nsd-cell-key {
    font-size: var(--font-xs);
    color: var(--blue1);
    font-weight: 500;
    text-align: center;
    flex: 1;
}
.nsd-cell-remove {
    position: absolute;
    top: 0.2rem;
    right: 0.3rem;
    background: none;
    border: none;
    color: var(--blue4);
    font-size: var(--font-sm);
    line-height: 1;
    cursor: pointer;
    padding: 0;
}
.nsd-cell-remove:hover { color: var(--blue1); }
.nsd-footer {
    display: flex;
    gap: 0.5rem;
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--blue6);
    flex-shrink: 0;
}
.nsd-btn-reset {
    flex: 1;
    min-height: 36px;
    background: var(--blue7);
    border: 1px solid var(--blue6);
    border-radius: 0.4rem;
    color: var(--blue3);
    font-size: var(--font-sm);
    cursor: pointer;
    transition: color 120ms, border-color 120ms;
}
.nsd-btn-reset:hover { color: var(--blue1); border-color: var(--blue4); }
.nsd-btn-done {
    flex: 2;
    min-height: 36px;
    background: var(--color-primary-light);
    border: none;
    border-radius: 0.4rem;
    color: #fff;
    font-size: var(--font-sm);
    font-weight: 600;
    cursor: pointer;
    transition: background 120ms;
}
.nsd-btn-done:hover { background: var(--color-primary); }

/* ── Track filter row (in panel) ──────────────────────── */

/* ── Group riding ─ host chrome only; panel/SOS/marker styles live in
   group/css/group.css (W GROUP module) ────────────────── */

/* .btn-map supplies layout; red alert palette overrides it here.
   First button of the #coords-row cluster in both views. */
#btn-sos-map {
    background: var(--color-sos);
    color: var(--grey0);
    border-color: var(--color-sos-strong);
    font-size: var(--font-xs);
    font-weight: 700;
    letter-spacing: 0.06em;
    line-height: normal;
    /* match sibling button size: icon is 24px + 0.6rem padding + 2px border */
    min-width: calc(24px + 0.6rem + 2px);
    min-height: calc(24px + 0.6rem + 2px);
}
#btn-sos-map:hover { background: var(--color-sos-strong); color: var(--grey0); border-color: var(--color-sos-strong); }
#btn-sos-map.sos-active { background: var(--color-sos-dark); border-color: var(--color-sos-dark); }

/* 2c: pulse the orient button on nav entry to draw the eye (North-up cue). */
@keyframes nav-orient-pulse {
    0%   { box-shadow: 0 0 0 0   var(--color-primary-bright); }
    70%  { box-shadow: 0 0 0 9px transparent; }
    100% { box-shadow: 0 0 0 0   transparent; }
}
.btn-map.nav-orient-pulse { animation: nav-orient-pulse 1.1s ease-out infinite; }
/* .group-msg stays host-side: the obd2/group accordion loading placeholders use it */
.group-msg { font-size: var(--font-xs); color: var(--color-error-content); text-align: center; margin: 0.4rem 0 0; }

/* ── Offline Maps ────────────────────────────────────────────────────────── */

#offline-dialog {
    border: 1px solid var(--blue6);
    border-radius: 0.85rem;
    background: var(--blue8);
    color: var(--blue1);
    padding: 0 0 0;
    width: min(92vw, 360px);
    height: min(80dvh, 520px);
    box-shadow: var(--dialog-shadow);
}
#offline-dialog[open] { display: flex; flex-direction: column; overflow: hidden; }
#offline-dialog:not([open]) { display: none; }
#offline-dialog::backdrop { background: rgba(0,0,0,0.55); backdrop-filter: blur(2px); }

.offline-info {
    margin: 0 1rem 0.5rem;
    font-size: var(--font-sm);
    color: var(--blue3);
    line-height: 1.4;
    flex-shrink: 0;
}
.offline-dl-summary {
    margin: 0 0 0.5rem;
    font-size: var(--font-xs);
    font-style: italic;
    color: var(--blue3);
    line-height: 1.5;
}
.offline-folder-tip {
    margin: 0 1rem 0.75rem;
    padding: 0.5rem 0.65rem;
    font-size: var(--font-xs);
    color: var(--blue2);
    line-height: 1.45;
    flex-shrink: 0;
    background: var(--blue7);
    border: 1px solid var(--blue6);
    border-radius: 0.35rem;
}

#offline-region-list { display: flex; flex-direction: column; gap: 0.5rem; padding: 0 0.75rem 1rem; flex: 1; overflow-y: auto; }

.offline-continent { border: none; }
.offline-continent-header {
    list-style: none;
    font-size: var(--font-xl);
    font-weight: 600;
    color: var(--blue3);
    padding: 0.25rem 0 0.35rem;
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.offline-continent-header::after {
    content: '▾';
    font-size: var(--font-xl);
    transition: transform 0.2s;
}
.offline-continent:not([open]) .offline-continent-header::after { transform: rotate(-90deg); }
.offline-continent-header::-webkit-details-marker { display: none; }
.offline-continent-body { display: flex; flex-direction: column; gap: 0.4rem; padding-bottom: 0.25rem; }

.offline-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    background: var(--blue7);
    border: 1px solid var(--blue6);
    border-radius: 0.4rem;
    padding: 0.55rem 0.65rem;
}
.offline-item-left { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.offline-item-right { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; }
.offline-item-name { font-size: var(--font-sm); font-weight: 500; color: var(--blue1); }
.offline-item-meta { font-size: var(--font-xs); color: var(--blue3); }
.offline-badge {
    font-size: var(--font-xs);
    font-weight: 600;
    color: var(--color-primary-light);
    white-space: nowrap;
}
.offline-btn-del, .offline-btn-relink { font-size: var(--font-xs); padding: 0.3rem 0.6rem; min-height: 32px; }

.offline-progress-wrap { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; min-width: 100px; }
.offline-progress-track {
    flex: 1;
    height: 6px;
    background: var(--blue6);
    border-radius: 3px;
    overflow: hidden;
}
.offline-progress-bar {
    height: 100%;
    background: var(--color-primary-light);
    border-radius: 3px;
    width: 0%;
    transition: width 200ms linear;
}
.offline-progress-bar.indeterminate {
    width: 40% !important;
    transition: none;
    animation: offline-indeterminate 1.4s ease-in-out infinite;
}
@keyframes offline-indeterminate {
    0%   { transform: translateX(-150%); }
    100% { transform: translateX(320%); }
}
#ol-prog-pct { font-size: var(--font-xs); color: var(--blue3); white-space: nowrap; }

.offline-empty { padding: 0 0.75rem; font-size: var(--font-sm); color: var(--blue3); }

/* ── OBD2 embedded module — accordion controls + fullscreen overlay ──────── */
#obd2-section { display: flex; flex-direction: column; gap: 0.6rem; }
#obd2-section .obd2-hud-toggle { display: flex; align-items: center; gap: 0.5rem; }
#obd2-section .obd2-hud-lbl { font-size: var(--font-sm); }
#obd2-hud-pick .obd2-pick-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.25rem 0.75rem;
    margin-top: 0.3rem;
}
#obd2-hud-pick label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: var(--font-sm);
}
#obd2-hud-pick input[disabled] + * , #obd2-hud-pick label:has(input[disabled]) { opacity: 0.45; }

/* launch row — Dashboard / Data / All settings, each a fullscreen surface */
.obd2-launch-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.4rem; }
.obd2-launch-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
    padding: 0.5rem 0.2rem;
    font-size: var(--font-xs);
    color: var(--blue1);
    background: var(--blue7);
    border: 1px solid var(--blue6);
    border-radius: 0.5rem;
    cursor: pointer;
}
.obd2-launch-btn:active { background: var(--blue6); }

/* fullscreen overlay (dashboard / data / settings) — background = the obd2
   standalone shell's */
#obd2-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 3000;
    background: radial-gradient(ellipse at 50% 0%, #14202f 0%, #0a0f16 60%, #05080c 100%);
}
#obd2-fullscreen.hide { display: none; }
#obd2-fs-body { height: 100%; overflow: auto; }
/* data + settings pages get readable width and clear the ✕ button */
#obd2-fs-body .obd2-view-data,
#obd2-fs-body .obd2-view-settings {
    padding: 3rem 0.75rem 1rem;
    max-width: 640px;
    margin: 0 auto;
}
#obd2-fs-close {
    position: absolute;
    top: calc(0.5rem + env(safe-area-inset-top, 0px));
    right: 0.75rem;
    z-index: 1;
}

/* ride-mode flip chevrons — nav map ⇄ fullscreen dashboard (glove-sized) */
.obd2-flip-btn {
    display: none;
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 96px;
    align-items: center;
    justify-content: center;
    padding: 0;
    background: var(--blue8-90);
    border: 1px solid var(--blue6);
    color: var(--blue1);
    cursor: pointer;
    z-index: 1150;
}
.obd2-flip-btn .svg-ico { width: 30px; height: 30px; }
.obd2-flip-right { right: 0; border-radius: 12px 0 0 12px; border-right: none; }
.obd2-flip-left  { left: 0;  border-radius: 0 12px 12px 0; border-left: none; }
/* map → dashboard: ride mode with the module loaded, overlay closed */
body.nav-mode.obd2-ready #obd2-flip { display: flex; }
body.obd2-fs-open #obd2-flip { display: none !important; }
/* dashboard → map: inside the overlay, ride mode only (✕ covers the rest) */
body.nav-mode.obd2-fs-open #obd2-fs-back { display: flex; position: absolute; z-index: 1; }

/* riding with the dashboard page up: safety alerts punch through the overlay,
   the info bars (values grid / weather / chips) stay behind it */
body.nav-mode.obd2-fs-open #nav-hud { z-index: 3100; }
body.nav-mode.obd2-fs-open #nav-hud-grid,
body.nav-mode.obd2-fs-open #nav-hud-weather,
body.nav-mode.obd2-fs-open #nav-hud-obd { display: none; }

/* A View eye with no points behind it: reads closed and stops taking taps. Same principle as the
   vanishing no-track controls, softened to a dim because the column header still has to be read. */
.mc-view-cb.mc-no-data { opacity: 0.35; pointer-events: none; }
/* No track scope registered (W WEATHER standalone): the Track column leaves the grid entirely, so
   the rectangle stays perfect for the landscape transpose (media.css). */
.mc-no-track .mc-col-track { display: none; }
.mc-no-track .mc-grid { grid-template-columns: auto 48px 48px; }
