@charset "UTF-8";
/**
 * @section CSS Reset
 * Adapted from Andy Bell's modern CSS reset
 * @link https://hankchizljaw.com/wrote/a-modern-css-reset/
 */
/**
 * Remove the tap delay in webkit
 * @link https://medium.com/@adactio/delay-a9df9edceef3#.7dmbl3xow
 */
a,
button,
input,
select,
textarea,
label,
summary {
  touch-action: manipulation;
}

/**
 * Add box sizing to everything
 * @link http://www.paulirish.com/2012/box-sizing-border-box-ftw/
 */
*,
*:before,
*:after {
  box-sizing: border-box;
}

/**
 * 1. Force scrollbar display to prevent jumping on pages.
 * 2. Prevent iOS text size adjust after orientation change, without disabling
 *    user zoom.
 */
html {
  overflow-x: hidden;
  overflow-y: hidden;
  text-size-adjust: 100%;
}

/**
 * 1. Remove the margin in all browsers (opinionated).
 * 2. For the body to fill the viewport
 */
body {
  padding: 0;
  min-height: 100vh;
  overflow-x: hidden;
  overflow-y: hidden;
  width: 100vw;
  margin: auto;
}

/**
 * Prevent img and video elements from spilling outside of the page on smaller screens.
 */
img,
video {
  max-width: 100%;
  height: auto;
}

/**
 * Prevent iframe, object, and embed elements from spilling outside of the page on smaller screens.
 * height: auto causes iframes to smush, so it's omitted here.
 */
iframe,
object,
embed {
  max-width: 100%;
}

/**
 * @workaround Remove focus from tabindex="-1" elements which are only script focusable
 * @link https://code.google.com/p/chromium/issues/detail?id=37721
 */
[tabindex="-1"]:focus {
  outline: none;
}

.noSelectFlash {
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.noSelectFlash:focus {
  outline: none !important;
}

.resetMarginPadding {
  margin: 0;
  padding: 0;
}

svg {
  pointer-events: none;
}

/* Three scopes mean "not a section" and share one ramp, so it is written ONCE here. They are kept
   as separate selectors rather than one comma list because each may legitimately diverge later
   (a warmer neutral for "you", a cooler one for chrome over terrain); until one does, a mixin is
   the honest way to say "identical on purpose, not by coincidence". */
/* Alpha steps on the light hue: `tint` .1, `lightT` .2, `light55` .55 — the number in the name is
   the alpha, same convention as `darker90`. `light55` exists because the precipitation bars in the
   Daily and 15-min charts were unreadable in daylight at `lightT`'s .2 (Laurent, on a phone,
   2026-09-17), and `lightT` could not simply be raised: it also paints the weekly today-row and the
   checked-toggle border, which would have become solid cyan bands. Charts get their own step. */
:root {
  --grey0-95: #fffffff2;
  --grey0-90: #FFFFFFE6;
  --grey0-85: #ffffffd9;
  --grey0-82: #FFFFFFD1;
  --grey0-75: #ffffffbf;
  --grey0-50: #ffffff80;
  --grey0-25: #ffffff40;
  --grey0-15: #ffffff26;
  --grey0-10: #ffffff1a;
  --grey0-5: #ffffff0d;
  --grey0: #ffffff;
  /* True neutrals (restored 2026-07-04 — same lightness as the blue scale) */
  --grey1: #ECECEC;
  --grey2: #D8D8D8;
  --grey3: #B4B4B4;
  --grey4: #8C8C8C;
  --grey4T: rgba(230, 230, 230, 0.2);
  --grey4-5: #777777;
  --grey5: #7E7E7E;
  --grey6: #373737;
  --grey6-5: #2E2E2E;
  --grey7: #242424;
  --grey8: #1A1A1A;
  --grey8-90: #1a1a1ae6;
  /* SURFACE SCALE — the ground under everything: panels, buttons, borders, map
     chrome. 46 rules read these, so the whole shell re-themes from here.
     2026-08-11: went from a dark NAVY (#0C1829 and friends) to a cool graphite,
     same lightness at every stop so no contrast ratio moved, only the hue.
     The reason is the accents, not taste: a chromatic ground taxes everything
     that sits on it. Azure was the same hue family as the navy so it had to
     fight its own background (which is what read as "corporate"), violet went
     cool and muddy, green drifted toward teal, magenta picked up a cold cast.
     Neutral gives all five section ramps back their true hue, and unblocks the
     half of the wheel that used to fight the navy.
     Not pure grey: ~10% saturation at hue 215 keeps it deliberate rather than
     looking like a page nobody styled. Names kept as --blueN on purpose, so
     this stayed a colour change and not a 46-site rename in the same commit. */
  --blue1: #EBEDEF;
  --blue2: #D4D8DD;
  --blue3: #ADB4BD;
  --blue4: #818A98;
  --blue4-5: #6C7684;
  --blue5: #707B89;
  --blue6: #32363D;
  --blue6-5: #292D32;
  --blue7: #202327;
  --blue8: #17191C;
  --blue8-90: #17191ce6;
  --grey9: #000000;
  --grey9-5: #0000000d;
  --grey9-10: #0000001A;
  --grey9-15: #00000026;
  --grey9-25: #00000040;
  --grey9-50: #00000080;
  --grey9-75: #000000bf;
  --grey9-82: #000000D1;
  --grey9-85: #000000d9;
  --grey9-90: #000000e6;
  --grey9-95: #000000F2;
  /* SECTION IDENTITY — the FALLBACK ramp, and as of 2026-08-13 it is NEUTRAL, not weather azure.
     It was azure only because weather was the first section, so for months anything outside a
     [data-section] scope came out silently weather-coloured. That is not a harmless default, it is
     a trap that fails QUIETLY: it had to be repaired four separate times, each time discovered by
     eye rather than by the app misbehaving (map chrome v826, the html/body background v826,
     #tl-widget v846, and #mini-cal today). Now an unscoped element looks unstyled, which is a bug
     you can SEE.
     What made the flip possible: every one of the 18 dialogs carries an explicit scope, and a scan
     of all 539 elements outside any scope found exactly ONE thing still painted from here, the
     spinner, which is now [data-section="neutral"] on its own merits (it spins for weather, GPX
     and offline downloads alike). Toasts never read this ramp at all: error and success are
     hardcoded, info uses the ground, warning uses the reserved amber.
     Weather azure still exists, unchanged, in [data-section="weather"] below — it just has to be
     asked for now. Rules that came with all this: a dialog takes the colour of the SECTION THAT
     OWNS IT, never of the door that opened it, as a static attribute in app.html; and ownership
     means where a thing is REACHED FROM, not what it is about (BT Remote is magenta because it
     lives in the Extra panel, not violet for navigation). */
  --color-primary-ultralight: #F5F7FA;
  --color-primary-bright: #DCE1E8;
  --color-primary-light: #AEB6C2;
  --color-primary-lightT: rgba(174, 182, 194, 0.2);
  --color-primary-light55: rgba(174, 182, 194, 0.55);
  --color-primary-tint: rgba(174, 182, 194, 0.1);
  --color-primary: #5A6472;
  --color-primary-dark: #454E59;
  --color-primary-darker: #2E343D;
  --color-primary-darker90: #2E343DE6;
  --color-primary-deep: #1E232A;
  --color-secondary-ultralight: #FDE68A;
  --color-secondary-light: #F59E0B;
  --color-secondary: #D97706;
  --color-secondary-dark: #92400E;
  --color-secondary-darker: #451A03;
  --color-secondary-darker90: #451A03E6;
  --color-border: #dde2e1;
  --color-copy: #232928;
  --color-copy-light: #5e6e6c;
  --color-copy-lighter: #849593;
  --color-success: #015201;
  --color-warning: #525201;
  --color-error: #520101;
  --color-success-content: #55fd55;
  --color-warning-content: #fdfd55;
  --color-error-content: #c81e1e;
  /* Bright variants — high-visibility alert backgrounds (white text on top) */
  --color-success-bright: #2a8c4a;
  --color-success-bright-strong: #1e6e38;
  --color-warning-bright: #c87800;
  --color-error-bright: #c81e1e;
  /* SOS family — buttons, top banner, marker pulse */
  --color-sos: #ef4444;
  --color-sos-strong: #dc2626;
  --color-sos-dark: #7f1d1d;
  --color-sos-90: #ef4444e6;
  --color-sos-0: #ef444400;
  --color-map-pin: #ff0000;
  --color-map-accuracy: #4a90d9;
  --font-xs: 0.75rem;
  --font-sm: 0.87rem;
  --font-md: 1rem;
  --font-lg: 1.15rem;
  --font-xl: 1.25rem;
  --font-xxl: 1.5rem;
  --font-xxxl: 2rem;
}

/* THEMES CLASS */
.theme-nav {
  color: var(--grey0);
  background-color: var(--grey9-90);
}

.theme-grey {
  color: var(--grey1);
  background-color: var(--grey9-95);
}

.theme-grey-light {
  color: var(--grey2);
  background-color: var(--grey7);
}

.theme-std {
  color: var(--color-primary-darker);
  background-color: var(--grey0);
}

.theme-light-inverse {
  color: var(--grey0);
  background-color: var(--color-primary);
}

.theme-light {
  color: var(--color-primary);
  background-color: var(--grey0);
}

.theme-std-bkg {
  color: var(--color-primary-darker);
  background-color: var(--grey3);
}

/* GREY CLASS */
.grey0-95 {
  color: var(--grey0-95);
}

.grey0-95-bkg {
  background-color: var(--grey0-95);
}

.grey0-85 {
  color: var(--grey0-85);
}

.grey0-85-bkg {
  background-color: var(--grey0-85);
}

.grey0-75 {
  color: var(--grey0-75);
}

.grey0-75-bkg {
  background-color: var(--grey0-75);
}

.grey0-50 {
  color: var(--grey0-50);
}

.grey0-50-bkg {
  background-color: var(--grey0-50);
}

.grey0-25 {
  color: var(--grey0-25);
}

.grey0-25-bkg {
  background-color: var(--grey0-25);
}

.grey0-15 {
  color: var(--grey0-15);
}

.grey0-15-bkg {
  background-color: var(--grey0-15);
}

.grey0-5 {
  color: var(--grey0-5);
}

.grey0-5-bkg {
  background-color: var(--grey0-5);
}

.grey0 {
  color: var(--grey0);
}

.grey0-bkg {
  background-color: var(--grey0);
}

.grey1 {
  color: var(--grey1);
}

.grey1-bkg {
  background-color: var(--grey1);
}

.grey2 {
  color: var(--grey2);
}

.grey2-bkg {
  background-color: var(--grey2);
}

.grey3 {
  color: var(--grey3);
}

.grey3-bkg {
  background-color: var(--grey3);
}

.grey4 {
  color: var(--grey4);
}

.grey4-bkg {
  background-color: var(--grey4);
}

.grey5 {
  color: var(--grey5);
}

.grey5-bkg {
  background-color: var(--grey5);
}

.grey6 {
  color: var(--grey6);
}

.grey6-bkg {
  background-color: var(--grey6);
}

.grey7 {
  color: var(--grey7);
}

.grey7-bkg {
  background-color: var(--grey7);
}

.grey8 {
  color: var(--grey8);
}

.grey8-bkg {
  background-color: var(--grey8);
}

.grey9 {
  color: var(--grey9);
}

.grey9-bkg {
  background-color: var(--grey9);
}

.grey9-5 {
  color: var(--grey9-5);
}

.grey9-5-bkg {
  background-color: var(--grey9-5);
}

.grey9-15 {
  color: var(--grey9-15);
}

.grey9-15-bkg {
  background-color: var(--grey9-15);
}

.grey9-25 {
  color: var(--grey9-25);
}

.grey9-25-bkg {
  background-color: var(--grey9-25);
}

.grey9-50 {
  color: var(--grey9-50);
}

.grey9-50-bkg {
  background-color: var(--grey9-50);
}

.grey9-75 {
  color: var(--grey9-75);
}

.grey9-75-bkg {
  background-color: var(--grey9-75);
}

.grey9-85 {
  color: var(--grey9-85);
}

.grey9-85-bkg {
  background-color: var(--grey9-85);
}

.grey9-95 {
  color: var(--grey9-95);
}

.grey9-95-bkg {
  background-color: var(--grey9-95);
}

/* COLORS CLASS */
.color-primary-ultralight {
  color: var(--color-primary-ultralight);
}

.color-primary-light {
  color: var(--color-primary-light);
}

.color-primary {
  color: var(--color-primary);
}

.color-primary-dark {
  color: var(--color-primary-dark);
}

.color-primary-darker {
  color: var(--color-primary-darker);
}

.color-secondary-ultralight {
  color: var(--color-secondary-ultralight);
}

.color-secondary-light {
  color: var(--color-secondary-light);
}

.color-secondary {
  color: var(--color-secondary);
}

.color-secondary-dark {
  color: var(--color-secondary-dark);
}

.color-secondary-darker {
  color: var(--color-secondary-darker);
}

.color-border {
  color: var(--color-border);
}

.color-copy {
  color: var(--color-copy);
}

.color-copy-light {
  color: var(--color-copy-light);
}

.color-copy-lighter {
  color: var(--color-copy-lighter);
}

.color-success {
  color: var(--color-success);
}

.color-warning {
  color: var(--color-warning);
}

.color-error {
  color: var(--color-error);
}

.color-success-content {
  color: var(--color-success-content);
}

.color-warning-content {
  color: var(--color-warning-content);
}

.color-error-content {
  color: var(--color-error-content);
}

/* FONT SIZE CLASS */
.font-xs {
  font-size: var(--font-xs);
}

.font-sm {
  font-size: var(--font-sm);
}

.font-md {
  font-size: var(--font-md);
}

.font-lg {
  font-size: var(--font-lg);
}

.font-xl {
  font-size: var(--font-xl);
}

.font-xxl {
  font-size: var(--font-xxl);
}

.font-xxxl {
  font-size: var(--font-xxxl);
}

/* =====================================================================
   SECTION RAMPS — identity by panel.
   Each section redeclares the SAME 10 custom properties, so every pill,
   switch, button and eye inside re-themes itself with no per-component
   work: 92 rules already read var(--color-primary*).
   RESERVED, never a section hue: amber = warning, red = danger / map pin.
   Lime was rejected on evidence, not taste — it and amber both drift to
   the same yellow-brown under deuteranopia, so it would have fought a
   safety signal.
   ===================================================================== */
[data-section=weather] {
  --color-primary-ultralight: #C4F7FF;
  --color-primary-bright: #5CE9FF;
  --color-primary-light: #00D3F2;
  --color-primary-lightT: rgba(0, 211, 242, 0.2);
  --color-primary-light55: rgba(0, 211, 242, 0.55);
  --color-primary-tint: rgba(0, 211, 242, 0.1);
  --color-primary: #0E7C90;
  --color-primary-dark: #0B6274;
  --color-primary-darker: #06414D;
  --color-primary-darker90: #06414DE6;
  --color-primary-deep: #04323D;
}

[data-section=gpx] {
  --color-primary-ultralight: #DCFCE7;
  --color-primary-bright: #86EFAC;
  --color-primary-light: #22C55E;
  --color-primary-lightT: rgba(34, 197, 94, 0.2);
  --color-primary-light55: rgba(34, 197, 94, 0.55);
  --color-primary-tint: rgba(34, 197, 94, 0.1);
  --color-primary: #15803D;
  --color-primary-dark: #166534;
  --color-primary-darker: #14532D;
  --color-primary-darker90: #14532DE6;
  --color-primary-deep: #052E16;
}

[data-section=nav] {
  --color-primary-ultralight: #F3E8FF;
  --color-primary-bright: #D8B4FE;
  --color-primary-light: #A855F7;
  --color-primary-lightT: rgba(168, 85, 247, 0.2);
  --color-primary-light55: rgba(168, 85, 247, 0.55);
  --color-primary-tint: rgba(168, 85, 247, 0.1);
  --color-primary: #7E22CE;
  --color-primary-dark: #6B21A8;
  --color-primary-darker: #581C87;
  --color-primary-darker90: #581C87E6;
  --color-primary-deep: #2E0B52;
}

[data-section=extra] {
  --color-primary-ultralight: #FFD6F6;
  --color-primary-bright: #FB7FE4;
  --color-primary-light: #F026C9;
  --color-primary-lightT: rgba(240, 38, 201, 0.2);
  --color-primary-light55: rgba(240, 38, 201, 0.55);
  --color-primary-tint: rgba(240, 38, 201, 0.1);
  --color-primary: #A81A8C;
  --color-primary-dark: #8C1474;
  --color-primary-darker: #5E0D4E;
  --color-primary-darker90: #5E0D4EE6;
  --color-primary-deep: #42063A;
}

/* Settings was warm stone until 2026-08-11, deliberately the quiet one. Two things retired that:
   warm stone was picked against a NAVY ground, where it read as a warm hue, and on the neutral
   ground it stopped being a hue at all; and the map scope below now owns "quiet", so Settings no
   longer has to. Working out what was left took longer than picking it: amber and red are reserved,
   lime and orange were already rejected (both collapse into amber under deuteranopia), and
   cyan / green / violet / magenta are taken, which leaves the indigo-to-blue arc and nothing else.
   Indigo over royal blue because royal sits ~30 degrees from weather azure and could read as "the
   blue one"; indigo is a clean step from both neighbours. NB the five sections are never on screen
   together in the app, so section-to-section separation only ever has to survive design.html. */
[data-section=settings] {
  --color-primary-ultralight: #E0E7FF;
  --color-primary-bright: #A5B4FC;
  --color-primary-light: #6366F1;
  --color-primary-lightT: rgba(99, 102, 241, 0.2);
  --color-primary-light55: rgba(99, 102, 241, 0.55);
  --color-primary-tint: rgba(99, 102, 241, 0.1);
  --color-primary: #4338CA;
  --color-primary-dark: #3730A3;
  --color-primary-darker: #312E81;
  --color-primary-darker90: #312E81E6;
  --color-primary-deep: #1E1B4B;
}

/* MAP — chrome floating over the basemap (.btn-map and friends). Until 2026-08-11 this had no
   scope at all, so it inherited root, and root happens to be weather azure: an accident, not a
   decision. Its own scope also stops the chrome changing hue every time a tab is switched, which
   is what would happen if it borrowed the active section instead, in peripheral vision, on a bike.
   Near-neutral on purpose: over unpredictable terrain (forest, city, snow, lake) any saturated
   chrome collides with something, and a sixth hue would crowd amber and red, which have to stay
   unmistakable over the map above everything else.
   The rule this buys: hue = you are in a section, neutral = you are on the map. */
/* USER — the account / help / about door (2026-08-12). Near-neutral, and NOT because the wheel ran
   out: walking it showed every remaining arc is a squeeze (teal 14deg off azure, royal blue between
   azure and indigo, purple between violet and magenta, rose next to danger red), but the real
   argument is Pierre's — "user IS neutral". The account door is not weather/gpx/nav/extra/settings,
   so it must not look like one. That sharpens the whole rule to:
   **hue = a SECTION of the app; neutral = NOT a section (map chrome, or you / the shell).**
   No worry about it reading as map chrome: membership of the rail is carried by the shared plate
   and five hued siblings, not by one button's hue. */
[data-section=user] {
  --color-primary-ultralight: #F5F7FA;
  --color-primary-bright: #DCE1E8;
  --color-primary-light: #AEB6C2;
  --color-primary-lightT: rgba(174, 182, 194, 0.2);
  --color-primary-light55: rgba(174, 182, 194, 0.55);
  --color-primary-tint: rgba(174, 182, 194, 0.1);
  --color-primary: #5A6472;
  --color-primary-dark: #454E59;
  --color-primary-darker: #2E343D;
  --color-primary-darker90: #2E343DE6;
  --color-primary-deep: #1E232A;
}

/* NEUTRAL — for a surface that belongs to the whole APP rather than to any one section. First user:
   the first-run dialog, which shows all six sections at once and therefore must not wear any one of
   their hues. Without a scope it would inherit root, i.e. weather azure, which is the same accident
   the map chrome was living with until 2026-08-11. */
[data-section=neutral] {
  --color-primary-ultralight: #F5F7FA;
  --color-primary-bright: #DCE1E8;
  --color-primary-light: #AEB6C2;
  --color-primary-lightT: rgba(174, 182, 194, 0.2);
  --color-primary-light55: rgba(174, 182, 194, 0.55);
  --color-primary-tint: rgba(174, 182, 194, 0.1);
  --color-primary: #5A6472;
  --color-primary-dark: #454E59;
  --color-primary-darker: #2E343D;
  --color-primary-darker90: #2E343DE6;
  --color-primary-deep: #1E232A;
}

[data-section=map] {
  --color-primary-ultralight: #F5F7FA;
  --color-primary-bright: #DCE1E8;
  --color-primary-light: #AEB6C2;
  --color-primary-lightT: rgba(174, 182, 194, 0.2);
  --color-primary-light55: rgba(174, 182, 194, 0.55);
  --color-primary-tint: rgba(174, 182, 194, 0.1);
  --color-primary: #5A6472;
  --color-primary-dark: #454E59;
  --color-primary-darker: #2E343D;
  --color-primary-darker90: #2E343DE6;
  --color-primary-deep: #1E232A;
}

/* =====================================================================
   _design.scss — the semantic design system.
   Generic reusable component classes ONLY (buttons, select groups, map
   buttons, ...). JS-hook classes carry zero CSS and never live here.
   New generic classes go in this file, not main.css.
   ===================================================================== */
.btn-primary, .btn-secondary-hztal, .btn-secondary-vtal, .btn-tertiary {
  padding: 0.45rem 1.5rem;
  border-radius: 1rem;
  cursor: pointer;
  font-weight: 600;
  font-family: inherit;
  white-space: nowrap;
  transition: background 120ms, color 120ms;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  flex-shrink: 0;
  min-height: 44px;
  box-sizing: border-box;
}

.btn-primary {
  font-size: var(--font-lg);
  text-align: center;
  border: 1.5px solid transparent;
  background: var(--color-primary);
  color: var(--color-primary-ultralight);
}

.btn-primary:hover {
  background: var(--color-primary-darker);
  color: var(--color-primary-ultralight);
}

.btn-primary:disabled {
  opacity: 0.35;
  cursor: default;
}

.btn-secondary-hztal {
  font-size: var(--font-md);
  padding: 0.45rem 1rem;
  text-align: center;
  border: 1px solid var(--blue6);
  background: var(--blue7);
  color: var(--blue3);
  transition: background 120ms, color 120ms, border-color 120ms;
}

.btn-secondary-hztal:hover {
  background: var(--blue7);
  color: var(--blue3);
  border-color: var(--color-primary-ultralight);
}

.btn-secondary-hztal:disabled {
  opacity: 0.35;
  cursor: default;
}

.btn-secondary-hztal .svg-ico {
  color: var(--color-primary-dark);
  transition: color 120ms;
}

.btn-secondary-hztal:hover .svg-ico {
  color: var(--color-primary-light);
}

/* Vertical variant — icon on top, text below, both centered. Square shape;
   text wraps (multi-word labels like "Weather Area") instead of overflowing. */
.btn-secondary-vtal {
  flex-direction: column;
  gap: 0.3rem;
  width: 64px;
  height: 64px;
  padding: 0.4rem;
  text-align: center;
  border: 1px solid var(--blue6);
  background: var(--blue7);
  color: var(--blue3);
  font-size: var(--font-xs);
  line-height: 1.15;
  white-space: normal;
  transition: background 120ms, color 120ms, border-color 120ms;
}

.btn-secondary-vtal:hover {
  background: var(--blue7);
  color: var(--blue3);
  border-color: var(--color-primary-ultralight);
}

.btn-secondary-vtal:disabled {
  opacity: 0.35;
  cursor: default;
}

.btn-secondary-vtal .svg-ico {
  color: var(--color-primary-dark);
  transition: color 120ms;
}

.btn-secondary-vtal:hover .svg-ico {
  color: var(--color-primary-light);
}

.btn-tertiary {
  font-size: var(--font-sm);
  padding: 0.4rem 0.5rem;
  border-radius: 0.25rem;
  min-width: 44px;
  text-align: center;
  border: 1px solid var(--blue6);
  background: var(--blue8);
  color: var(--blue3);
  transition: background 120ms, color 120ms, border-color 120ms;
}

.btn-tertiary:hover {
  background: var(--blue7);
  color: var(--color-primary-ultralight);
  border-color: var(--color-primary-ultralight);
}

.btn-tertiary:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Map buttons — every control floating over the map (coords row, nav-left
   strip, MapLibre zoom, #coords-input). Nav palette, same in light and dark
   map mode. Grouping/positioning stays on the container IDs. */
.btn-map {
  pointer-events: all;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.3rem;
  border-radius: 0.4rem;
  border: 1px solid var(--blue6);
  background: var(--blue8-90);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 2px 8px var(--grey9-25);
  color: var(--blue2);
  cursor: pointer;
  line-height: 0;
  transition: background 120ms, color 120ms, border-color 120ms;
}

/* Hover feedback only on real hover-capable devices — on touch, :hover sticks
   after a tap (the button would stay blue until you tap elsewhere). */
@media (hover: hover) {
  .btn-map:hover {
    color: var(--color-primary-light);
  }
}
.btn-map:active {
  opacity: 0.6;
}

.btn-map:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Toggled-on state (marker-config, radar, relief, 3D, fires, hazards).
   INVERTED, not tinted. This rule used to read `color: var(--color-primary-bright)`,
   which worked only because .btn-map sat outside every section scope and inherited
   weather azure: ON was bright cyan. Since map chrome got its own neutral scope
   (`[data-section=map]`, 2026-08-11) that variable resolves to #DCE1E8 against a
   #D4D8DD resting glyph — an 8-point shift, invisible on a phone in daylight.
   Hue can no longer carry the signal here, so the FILL carries it: light chip,
   dark glyph. Hue-free by construction, so it survives whatever the palette does next.
   The border keeps the RESTING BACKGROUND colour (dark) rather than matching the fill:
   a light chip edged in light grey dissolves into a light map, and map chrome has to
   hold its edge over terrain it does not control. Dark border = the two states swap
   fill and border, and the button's outline never moves. */
.btn-map.mc-active,
.btn-map.radar-active,
.btn-map.active {
  background: var(--blue2);
  border-color: var(--blue8);
  color: var(--blue8);
}

/* MapLibre zoom buttons — btn-map class added in JS after addControl().
   The group container supplies the joined-stack look; buttons lose their
   own radius/shadow, built-in icons are dark glyphs → invert to grey2-ish. */
.maplibregl-ctrl-group {
  background: transparent !important;
  box-shadow: 0 2px 8px var(--grey9-25) !important;
  border-radius: 0.4rem;
}

/* Re-assert .btn-map visuals — maplibre-gl.css's own button rules
   (background transparent, border 0) out-specify the bare class */
.maplibregl-ctrl-group button.btn-map {
  width: 36px;
  height: 36px;
  box-shadow: none;
  border-radius: 0;
  padding: 0;
  background: var(--blue8-90);
  border: 1px solid var(--blue6);
}

.maplibregl-ctrl-group button.btn-map:first-child {
  border-radius: 0.4rem 0.4rem 0 0;
}

.maplibregl-ctrl-group button.btn-map:last-child {
  border-radius: 0 0 0.4rem 0.4rem;
  margin-top: -1px;
}

/* A one-button group (relief, 3D, hazards) matched BOTH rules and the last one won: round below,
   square above (Laurent, 2026-09-12). Alone in its group = a full chip. */
.maplibregl-ctrl-group button.btn-map:only-child {
  border-radius: 0.4rem;
  margin-top: 0;
}

/* :not(.active) — without it this hover rule (0,4,1) outranks the toggled-on rule
   below (0,3,1) and hovering an ON relief/3D/hazards button would drop the light
   fill while the glyph stayed dark, i.e. an invisible icon. */
.maplibregl-ctrl-group button.btn-map:not(:disabled):not(.active):hover {
  background-color: var(--blue8-90);
}

/* Same inversion, restated at ctrl-group specificity. The re-assert block above
   (0,2,1) out-specifies the plain .btn-map.active rule (0,2,0), which is why relief,
   3D and hazards have had NO visible ON state at all: even the old border-colour half
   of it was being overwritten by `border: 1px solid var(--blue6)`. */
.maplibregl-ctrl-group button.btn-map.mc-active,
.maplibregl-ctrl-group button.btn-map.radar-active,
.maplibregl-ctrl-group button.btn-map.active {
  background: var(--blue2);
  border-color: var(--blue8);
  color: var(--blue8);
}

.btn-map .maplibregl-ctrl-icon {
  filter: invert(0.85);
}

/* Maplibre's built-in glyphs are dark artwork lightened by the filter above; on an
   inverted button they must go back to dark or they vanish into the light fill. */
.btn-map.active .maplibregl-ctrl-icon {
  filter: none;
}

@media (hover: hover) {
  .btn-map:hover .maplibregl-ctrl-icon {
    filter: invert(1);
  }
}
/* Generic segmented choice group — flush-joined square items, rounded only at
   the group's outer ends, 1px border overlap so the shared seam doesn't double
   up. Carries ALL visual styling; component classes (e.g. .activity-selector /
   .activity-btn) stay CSS-free and exist purely as JS hooks. */
.select-group-type1 {
  display: flex;
  gap: 0;
  align-items: flex-start;
  justify-content: center;
}

.select-group-type1 > * {
  flex: 0 0 auto;
  aspect-ratio: 1/1;
  padding: 0.37rem 0.4rem;
  border: 1px solid var(--blue6);
  border-radius: 0;
  background: var(--blue7);
  color: var(--blue4);
  font-size: var(--font-sm);
  font-family: inherit;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  position: relative;
  white-space: nowrap;
  transition: background 120ms, color 120ms, border-color 120ms;
}

.select-group-type1 > * .svg-ico {
  color: var(--color-primary-dark);
  transition: color 120ms;
}

.select-group-type1 > *.active .svg-ico {
  color: var(--color-primary-light);
}

.select-group-type1 > * + * {
  margin-left: -1px;
}

/* Square on purpose: rounded = button (it acts), square = selector (it chooses). */
.select-group-type1 > *:hover {
  background: var(--blue6);
  color: var(--blue1);
  border-color: var(--blue5);
  z-index: 1;
}

.select-group-type1 > *.active {
  background: var(--color-primary-tint);
  border-color: var(--color-primary-light);
  color: var(--blue1);
  font-weight: 600;
  z-index: 1;
}

/* Generic segmented choice group — flush text-only items sharing one outer
   border/radius (via container overflow:hidden), divider between items via
   border-left. Carries ALL visual styling; component classes (e.g.
   .map-mode-selector / .map-mode-btn) stay CSS-free and exist purely as JS hooks. */
.select-group-type2 {
  display: flex;
  border: 1px solid var(--blue6);
  border-radius: 0; /* square = selector; see .select-group-type1 */
  overflow: hidden;
}

.select-group-type2 > * {
  flex: 1;
  padding: 0.3rem 0.7rem;
  border: none;
  border-radius: 0;
  background: var(--blue7);
  color: var(--blue3);
  font-size: var(--font-xs);
  font-family: inherit;
  cursor: pointer;
  min-height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms, color 120ms;
}

.select-group-type2 > * + * {
  border-left: 1px solid var(--blue6);
}

.select-group-type2 > *:hover {
  background: var(--blue6);
  color: var(--blue1);
}

.select-group-type2 > *.active {
  background: var(--color-primary-tint);
  color: var(--color-primary-ultralight);
  font-weight: 700;
  box-shadow: inset 0 -2px 0 var(--color-primary-light);
}

/* Pick ANY of these, rather than pick one. Same pills, simply taken apart: a gap between them and
   each carrying its own border, since there is no shared container edge left to sit inside.
   The glued form (plain .select-group-type2, with a label in front) means pick exactly one. */
.select-group-type2.sg-spaced {
  border: none;
  overflow: visible;
  gap: 0.35rem;
}

.select-group-type2.sg-spaced > * {
  border: 1px solid var(--blue6);
  min-height: 36px; /* glove-tested floor, same as the track-width pills */
}

/* EYE BUTTON — "is this thing displayed?" (Timeline, POI). An action, so it is rounded and it
   fires now; the STATE is carried by the glyph itself, eye open or eye closed, the same drawing
   that already means "on the map" in the track list and the marker palette. No fill or shadow to
   squint at through glare: the icon is the message. JS swaps the glyph via btn_eye_set(). */
.btn-eye {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  flex-shrink: 0;
  min-height: 44px;
  padding: 0.45rem 1rem;
  border: 1px solid var(--blue6);
  border-radius: 1rem;
  background: var(--blue7);
  color: var(--blue3);
  font-family: inherit;
  font-size: var(--font-md);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms;
}

.btn-eye .svg-ico {
  width: 20px;
  height: 20px;
  color: var(--color-primary-light);
  transition: color 120ms;
}

.btn-eye.be-off {
  color: var(--blue4);
}

.btn-eye.be-off .svg-ico {
  color: var(--blue5);
}

.btn-eye:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Compact variant for dense rows (the nav panel's display options): same grammar, less chrome. */
.btn-eye-sm {
  min-height: 36px;
  padding: 0.3rem 0.55rem;
  font-size: var(--font-xs);
  font-weight: 500;
  gap: 0.3rem;
}

.btn-eye-sm .svg-ico {
  width: 18px;
  height: 18px;
}

/* ON/OFF SWITCH — "is this behaviour enabled?" (Travel time, Voice, Off-track alert, Auto-zoom,
   Remote mode). Nothing appears or disappears when it flips; a rule starts applying. Round track
   because it acts, and it says its state in WORDS as well as knob position + fill, because those
   two are exactly what die under glare, gloves and polarised glasses. "on"/"off" stay untranslated
   on purpose: three characters is all the track can hold, and they are read as symbols here, the
   way play/pause glyphs are. Was 36x22 in main.css; the 22px knob with a 32px throw is the real
   upgrade, a 16px knob moving 15px was too small a change to read at arm's length. */
.toggle-label {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  font-size: var(--font-xs);
  color: var(--blue4);
  user-select: none;
  white-space: nowrap;
  transition: color 120ms;
}

.toggle-label:has(input:checked) {
  color: var(--blue1);
}

.toggle-label:has(input:disabled) {
  opacity: 0.4;
  cursor: default;
  pointer-events: none;
}

.toggle-label input[type=checkbox] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.toggle-check-box {
  display: inline-block;
  width: 62px;
  height: 30px;
  border-radius: 999px;
  background: var(--blue6);
  border: 1px solid var(--grey4T);
  box-sizing: border-box;
  position: relative;
  flex-shrink: 0;
  transition: background 200ms, border-color 200ms;
}

.toggle-check-box::after {
  content: "";
  position: absolute;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--blue4-5);
  top: 3px;
  left: 3px;
  transition: transform 200ms, background 200ms;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}

.toggle-check-box::before {
  content: "off";
  position: absolute;
  right: 9px;
  top: 50%;
  transform: translateY(-50%);
  font-size: var(--font-xs);
  line-height: 1;
  color: var(--blue3);
}

.toggle-label:not(:has(input:checked)):not(:has(input:disabled)):hover .toggle-check-box {
  background: var(--blue5);
}

.toggle-label:has(input:checked) .toggle-check-box {
  background: var(--color-primary-deep);
  border-color: var(--color-primary-lightT);
}

.toggle-label:has(input:checked) .toggle-check-box::after {
  background: var(--color-primary-bright);
  transform: translateX(32px);
}

.toggle-label:has(input:checked) .toggle-check-box::before {
  content: "on";
  right: auto;
  left: 10px;
  color: var(--blue1);
}

.toggle-label input:focus-visible ~ .toggle-check-box {
  outline: 2px solid var(--color-primary-ultralight);
  outline-offset: 2px;
}
