/* hopandhaul - design tokens, layout, components. No build step: plain CSS, custom properties,
   native nesting-free selectors so it works in any evergreen browser without a preprocessor. */

/* ---------------------------------------------------------------- tokens
   Accent discipline - --arc = flight + interaction, --rail = savings + ground leg, never
   swapped. --arc goes ONLY on: links, focus rings, the one selected control per group, the
   combobox active option, .skip-link, spinner, flight legs + origin pin, and the direct-flight
   hero value. --arc NEVER on: the hero savings number (always --rail - that figure staying in
   the "money saved" color is the product story), panel borders/titles (stay --dim), .btn at
   rest/hover, badges/tags at rest, weather chip, non-recommended pins, scrollbars, or any wash
   stronger than 14%.

   This discipline is per-TOKEN, not per-hex: every theme below reassigns --arc/--rail to its
   own palette (a jetstream blue in most, but the amber terminal theme below deliberately
   breaks from blue - blue has no place in a monochrome-phosphor CRT look, so --arc goes amber
   there and --rail stays a second, cooler phosphor green, matching real dual-color terminals).
   What never moves is which SLOT a color goes in and what it means once it's there. Every
   theme's worst-case text contrast (ink/dim/arc/rail/warn/bad against panel, plus the
   rec-card color-mix blends) is computed against WCAG AA - 4.5:1 body text, 3:1 large text
   and UI components - before it ships, not eyeballed. The contrast comment on each color
   below is that computed ratio against --panel. */
:root,
[data-theme="dark"] {                /* DARK - Departure Board (default) */
  color-scheme: dark;
  --bg:        #0c1519;
  --panel:     #122026;
  --panel-2:   #182a31;
  --line:      #29404a;
  --ink:       #e7f0f2;
  --dim:       #9db4bc;              /* 7.7:1 on panel */
  --arc:       #47b6ff;              /* "Jetstream" - flight blue. 8.3:1 on bg */
  --rail:      #3dd68c;              /* "Signal" - rail/savings green. ~9:1 */
  --accent:    var(--arc);
  --accent-ink:#04161f;              /* 9.5:1 on accent fill */
  --good:      var(--rail);
  --warn:      #e0a83c;              /* 7.8:1 */
  --bad:       #f25f5c;              /* 5.2:1 on panel */
  --hub:       var(--dim);
  --map-bg:    #0c1519;
  --map-ref:   #55707c;              /* faint direct-flight reference line */
  --map-water: #0e1c22;              /* self-drawn atlas base (atlas.js) - open water */
  --map-land:  #1b2f37;
  --map-coast: #33505c;
  --map-border:#2b4550;
  --focus:     0 0 0 3px rgba(71, 182, 255, .5);
  --shadow-1:  0 1px 3px  rgba(2, 10, 14, .40);   /* chips, legend, tags */
  --shadow-2:  0 6px 18px rgba(2, 10, 14, .45);   /* floating panels */
  --shadow-3:  0 10px 30px rgba(2, 10, 14, .50);  /* modal, spinner */
  --radius: 12px;
  --radius-sm: 8px;
  --radius-xs: 6px;
}

[data-theme="light"] {               /* LIGHT - Boarding Pass (warm timetable paper) */
  color-scheme: light;
  --bg:        #f6f4ee;
  --panel:     #ffffff;
  --panel-2:   #f0ede4;
  --line:      #dcd6c8;
  --ink:       #1b2b33;              /* petrol ink ties the themes together */
  --dim:       #566b73;              /* 5.5:1 on white */
  --arc:       #086dbd;              /* 5.3:1 on white */
  --rail:      #0c7d4d;              /* 5.2:1 */
  --accent:    var(--arc);
  --accent-ink:#ffffff;
  --good:      var(--rail);
  --warn:      #8a6100;
  --bad:       #c8323e;
  --hub:       var(--dim);
  --map-bg:    #bfdbe6;              /* atlas water tint - no white flash */
  --map-ref:   #8a989e;
  --map-water: #bfdbe6;
  --map-land:  #f0ede4;
  --map-coast: #b9c9ce;
  --map-border:#cdbfa0;
  --focus:     0 0 0 3px rgba(8, 109, 189, .35);
  --shadow-1:  0 1px 3px  rgba(27, 43, 51, .10);
  --shadow-2:  0 6px 18px rgba(27, 43, 51, .12);
  --shadow-3:  0 10px 30px rgba(27, 43, 51, .14);
}

[data-theme="oled"] {                /* DARK - Night Flight (true OLED black) */
  color-scheme: dark;
  --bg:        #000000;
  --panel:     #0a0a0c;
  --panel-2:   #131316;
  --line:      #2a2a30;
  --ink:       #f2f5f7;              /* 18.1:1 on panel */
  --dim:       #a9b2ba;              /* 9.2:1 on panel */
  --arc:       #5ec2ff;              /* 10.0:1 on panel */
  --rail:      #38e6a4;              /* 12.3:1 on panel */
  --accent:    var(--arc);
  --accent-ink:#00131c;
  --good:      var(--rail);
  --warn:      #ffcf4d;
  --bad:       #ff6e6b;
  --hub:       var(--dim);
  --map-bg:    #000000;
  --map-ref:   #4d5761;
  --map-water: #000000;
  --map-land:  #232a37;
  --map-coast: #4a5364;
  --map-border:#343c4b;
  --focus:     0 0 0 3px rgba(94, 194, 255, .55);
  --shadow-1:  0 1px 3px  rgba(0, 0, 0, .55);
  --shadow-2:  0 6px 18px rgba(0, 0, 0, .60);
  --shadow-3:  0 10px 30px rgba(0, 0, 0, .68);
}

[data-theme="amber"] {               /* DARK - Terminal (monochrome CRT departure board) */
  color-scheme: dark;
  --bg:        #0a0704;
  --panel:     #120d07;
  --panel-2:   #1c140b;
  --line:      #4a3418;
  --ink:       #ffd899;              /* 14.3:1 on panel */
  --dim:       #c99a55;              /* 7.6:1 on panel */
  --arc:       #ffb000;              /* primary phosphor amber - 10.6:1 on panel. Deliberately
                                         not blue: see the accent-discipline note above. */
  --rail:      #33d17a;              /* secondary phosphor green - 9.7:1 on panel, the one
                                         concession to a second hue, matching real dual-trace
                                         terminals and keeping "green = savings" legible here too */
  --accent:    var(--arc);
  --accent-ink:#1a0f00;
  --good:      var(--rail);
  --warn:      #ff8f2e;
  --bad:       #ff5a4d;
  --hub:       var(--dim);
  --map-bg:    #0a0704;
  --map-ref:   #6b4e26;
  --map-water: #0a0704;
  --map-land:  #2a1e0f;
  --map-coast: #5c4120;
  --map-border:#46311a;
  --focus:     0 0 0 3px rgba(255, 176, 0, .55);
  --shadow-1:  0 1px 3px  rgba(0, 0, 0, .55);
  --shadow-2:  0 6px 18px rgba(0, 0, 0, .60);
  --shadow-3:  0 10px 30px rgba(0, 0, 0, .68);
}

[data-theme="contrast"] {            /* DARK - High Contrast (accessibility: max-contrast) */
  color-scheme: dark;
  --bg:        #000000;
  --panel:     #000000;
  --panel-2:   #0d0d0d;
  --line:      #ffffff;              /* full-strength borders, not a subtle hairline */
  --ink:       #ffffff;              /* 21:1 on panel */
  --dim:       #d9d9d9;              /* 14.9:1 on panel - still far above AA, kept off pure
                                         white only so hierarchy (label vs value) still reads */
  --arc:       #ffd400;              /* 14.7:1 on panel - the canonical high-contrast link/
                                         focus yellow */
  --rail:      #22e06b;              /* 11.9:1 on panel */
  --accent:    var(--arc);
  --accent-ink:#000000;
  --good:      var(--rail);
  --warn:      #ffb300;
  --bad:       #ff5252;
  --hub:       var(--dim);
  --map-bg:    #000000;
  --map-ref:   #ffffff;
  --map-water: #000000;
  --map-land:  #262626;
  --map-coast: #ffffff;
  --map-border:#cfcfcf;
  --focus:     0 0 0 3px #ffd400;
  --shadow-1:  none;                 /* real high-contrast modes drop soft shadows too - the
                                         white 1px border alone (already 21:1) draws every
                                         boundary; a blurred shadow doesn't add legibility */
  --shadow-2:  none;
  --shadow-3:  none;
}
/* Thicker focus outline for this theme only - cheap, real accessibility value, not a hue swap. */
[data-theme="contrast"] :focus-visible {
  outline-width: 3px;
}
[data-theme="contrast"] button:focus-visible,
[data-theme="contrast"] input:focus-visible,
[data-theme="contrast"] select:focus-visible,
[data-theme="contrast"] [role="option"]:focus-visible {
  outline-width: 3px;
}

[data-theme="vintage"] {             /* LIGHT - Rail Poster (parchment + silkscreen ink) */
  color-scheme: light;
  --bg:        #f2e4c8;
  --panel:     #fbf3e1;
  --panel-2:   #ecdcb8;
  --line:      #b99a63;
  --ink:       #3b2412;              /* 13.1:1 on panel */
  --dim:       #6b4a20;              /* 7.2:1 on panel */
  --arc:       #0d6e63;              /* poster teal - 5.6:1 on panel */
  --rail:      #3f6d27;              /* poster olive-green - 5.6:1 on panel */
  --accent:    var(--arc);
  --accent-ink:#fdf6e7;
  --good:      var(--rail);
  --warn:      #8a5a0a;
  --bad:       #a23b2e;
  --hub:       var(--dim);
  --map-bg:    #c9b98a;
  --map-ref:   #9c7f4d;
  --map-water: #c9b98a;
  --map-land:  #f2e4c8;
  --map-coast: #6b4a20;
  --map-border:#b99a63;
  --map-graticule: rgba(107, 74, 32, .16);   /* poster-map lat/lng lines - a deliberate detail */
  --focus:     0 0 0 3px rgba(13, 110, 99, .35);
  --shadow-1:  0 1px 3px  rgba(59, 36, 18, .18);
  --shadow-2:  0 6px 18px rgba(59, 36, 18, .22);
  --shadow-3:  0 10px 30px rgba(59, 36, 18, .26);
}

[data-theme="sepia"] {               /* LIGHT - Old Map (aged-paper cartography) */
  color-scheme: light;
  --bg:        #e3d3b0;
  --panel:     #efe2c4;
  --panel-2:   #ddc99f;
  --line:      #a98b5c;
  --ink:       #3f2f18;              /* 10.0:1 on panel */
  --dim:       #5f4a26;              /* 6.6:1 on panel */
  --arc:       #2c4a6e;              /* old map-ink blue - 7.1:1 on panel */
  --rail:      #54662a;              /* faded ledger green - 4.9:1 on panel */
  --accent:    var(--arc);
  --accent-ink:#fbf3df;
  --good:      var(--rail);
  --warn:      #8a5410;
  --bad:       #953226;
  --hub:       var(--dim);
  --map-bg:    #c3b083;
  --map-ref:   #8d7346;
  --map-water: #c3b083;
  --map-land:  #e3d3b0;
  --map-coast: #5f4a26;
  --map-border:#a98b5c;
  --map-graticule: rgba(63, 47, 24, .18);    /* old-map lat/lng lines, aged-ink faint */
  --focus:     0 0 0 3px rgba(44, 74, 110, .35);
  --shadow-1:  0 1px 3px  rgba(63, 47, 24, .20);
  --shadow-2:  0 6px 18px rgba(63, 47, 24, .24);
  --shadow-3:  0 10px 30px rgba(63, 47, 24, .28);
}

[data-theme="coastal"] {             /* LIGHT - Coastal (sand + sea) */
  color-scheme: light;
  --bg:        #eaf1ee;
  --panel:     #f8fbfa;
  --panel-2:   #dcebe6;
  --line:      #a9c9c0;
  --ink:       #123531;              /* 12.8:1 on panel */
  --dim:       #3f6b64;              /* 5.8:1 on panel */
  --arc:       #00738f;              /* ocean teal - 5.2:1 on panel */
  --rail:      #177056;              /* seagrass green - 5.8:1 on panel */
  --accent:    var(--arc);
  --accent-ink:#ffffff;
  --good:      var(--rail);
  --warn:      #8a5a10;
  --bad:       #af3b33;
  --hub:       var(--dim);
  --map-bg:    #8fd0c8;
  --map-ref:   #5c9089;
  --map-water: #8fd0c8;
  --map-land:  #eef5ec;
  --map-coast: #3f6b64;
  --map-border:#a9c9c0;
  --focus:     0 0 0 3px rgba(0, 115, 143, .35);
  --shadow-1:  0 1px 3px  rgba(18, 53, 49, .12);
  --shadow-2:  0 6px 18px rgba(18, 53, 49, .14);
  --shadow-3:  0 10px 30px rgba(18, 53, 49, .16);
}

/* type scale - the savings figure needs headroom above everything else on the page */
:root {
  --fs-xs: 0.6875rem;   /* 11px  */
  --fs-sm: 0.75rem;     /* 12px  */
  --fs-base: 0.8125rem; /* 13px  */
  --fs-md: 0.9375rem;   /* 15px  */
  --fs-lg: 1.125rem;    /* 18px  */
  --fs-hero: 2.5rem;    /* 40px - the dollar-savings number, the whole point of the product */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --lh-tight: 1.1;
  --lh-snug: 1.4;
  --lh-body: 1.5;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif,
               "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
  --font-num: "B612 Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
  accent-color: var(--accent);
}

@font-face {
  font-family: "B612 Mono";
  src: url("./vendor/b612mono-regular.ttf") format("truetype");
  font-weight: 400; font-display: swap;
}
@font-face {
  font-family: "B612 Mono";
  src: url("./vendor/b612mono-bold.ttf") format("truetype");
  font-weight: 700; font-display: swap;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  font-family: var(--font-body);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  background: var(--bg); color: var(--ink);
}

#controls, #results, .aclist, .lang-list {
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

h1, .state-title, .rec-route { text-wrap: balance; }
.sub, .state-body, .rec-sub { text-wrap: pretty; }

.opt-price, .opt-meta, .rec-hero-value, .rec-breakeven, .wx-temp, .wx-fc, .leg,
.field-input[type="number"] { font-variant-numeric: tabular-nums lining-nums; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

a { color: var(--accent); }

/* visible, WCAG 2.2-compliant focus rings everywhere - never suppressed */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
button:focus-visible, input:focus-visible, select:focus-visible, [role="option"]:focus-visible {
  box-shadow: var(--focus);
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* ---------------------------------------------------------------- layout shell */
#map { position: absolute; inset: 0; z-index: 1; background: var(--map-bg); }

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}

.skip-link {
  position: absolute; top: -100px; inset-inline-start: 8px; z-index: 3000;
  background: var(--accent); color: var(--accent-ink);
  padding: 8px 14px; border-radius: var(--radius-sm); font-weight: 600;
  transition: top .15s var(--ease);
}
.skip-link:focus { top: 8px; }

/* Desktop: two floating panels. Mobile media query below turns #controls into a top bar
   and #results into a bottom sheet - this is the real responsive rework, not a fixed-width
   panel that silently overflows a 375px viewport. */
#controls {
  position: absolute; top: var(--space-4); inset-inline-start: var(--space-4); z-index: 1000; width: 320px;
  max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px); overflow-y: auto;
  padding: var(--space-4) var(--space-4) var(--space-3);
}
#controls h1 {
  font-size: var(--fs-md); margin: 0 0 2px; letter-spacing: .2px;
  display: flex; align-items: center; gap: 8px;
}
.controls-head { display: flex; align-items: flex-start; gap: 8px; }
#controls .sub { font-size: var(--fs-sm); color: var(--dim); margin: 0 0 var(--space-3); line-height: 1.4; }

.top-actions { display: flex; align-items: center; gap: 6px; margin-inline-start: auto; }

.row { display: flex; gap: var(--space-2); margin: var(--space-2) 0; }
.row label {
  flex: 1; font-size: var(--fs-xs); color: var(--dim);
  display: flex; flex-direction: column; gap: 4px;
}
.row input, .row select, .field-input {
  background: var(--panel-2); border: 1px solid var(--line); color: var(--ink);
  border-radius: var(--radius-sm); padding: var(--space-2); font-size: var(--fs-md); width: 100%;
  font-family: inherit;
}
.row input:focus-visible, .row select:focus-visible {
  border-color: var(--accent);
}

.seg { display: flex; background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; }
.seg button {
  flex: 1; background: transparent; border: 0; color: var(--dim);
  padding: var(--space-2); font-size: var(--fs-sm); cursor: pointer; font-family: inherit;
}
.seg button.on { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.seg button.on:focus-visible {
  outline-color: var(--accent-ink);
  box-shadow: 0 0 0 3px var(--accent);
}
@media (hover: hover) {
  .seg button:not(.on):hover { color: var(--ink); background: var(--line); }
}

.check { display: flex; align-items: center; gap: 7px; font-size: var(--fs-sm); color: var(--dim); cursor: pointer; margin-top: 2px; }

.badge { display: inline-block; font-size: var(--fs-xs); padding: 2px 7px; border-radius: 999px; border: 1px solid var(--line); }
.badge--est {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 35%, var(--panel));
  background: color-mix(in srgb, var(--warn) 12%, var(--panel));
}
.badge--live {
  color: var(--good);
  border-color: color-mix(in srgb, var(--good) 35%, var(--panel));
  background: color-mix(in srgb, var(--good) 12%, var(--panel));
}

.hint { font-size: var(--fs-sm); color: var(--dim); margin-top: var(--space-2); border-top: 1px solid var(--line); padding-top: var(--space-2); display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.hint-fx { flex-basis: 100%; font-size: var(--fs-xs); }

/* connectivity chip + its menu - a distinct concept from the pricing badge above it (that one
   says where FARE numbers came from; this one says whether the app can reach the network at
   all), so a separate control rather than folding a third state into the existing badge. Reuses
   the theme picker's own listbox pattern/markup (.theme-list/.theme-item) rather than a second
   copy of the same CSS. */
.net-picker { position: relative; margin-inline-start: auto; }
.net-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: inherit; font-size: var(--fs-xs); font-weight: 600; cursor: pointer;
  border-radius: 999px; border: 1px solid var(--line); background: var(--panel-2); color: var(--ink);
  padding: 3px 9px 3px 7px;
}
@media (hover: hover) {
  .net-chip:hover { background: var(--line); }
}
.net-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); flex: none; }
.net-chip--offline .net-dot { background: var(--warn); }
/* Two classes on purpose: .theme-item-desc's ellipsis rule sits later in this file, and at
   equal specificity it would win, re-truncating these longer connection-mode descriptions. */
.theme-item-desc.net-item-desc { white-space: normal; overflow: visible; text-overflow: clip; }
@media (pointer: coarse) {
  .net-chip { min-height: 44px; padding-inline: 12px; }
}

/* currency selector - a plain native <select> gets full a11y/keyboard support for free and
   matches the .field-input look already used everywhere else in the controls card. */
.currency-select { width: auto; min-width: 5.5rem; font-size: var(--fs-xs); padding: 3px 6px; }
@media (pointer: coarse) {
  .currency-select { min-height: 44px; }
}

/* button system */
.btn {
  font-family: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
  border-radius: var(--radius-sm); border: 1px solid var(--line); background: var(--panel-2);
  color: var(--ink); padding: 8px 12px; display: inline-flex; align-items: center; gap: 6px;
  transition: transform .1s var(--ease), background-color .15s var(--ease);
}
@media (hover: hover) {
  .btn:hover { background: var(--line); }
}
.btn:active { transform: scale(.97); }
.btn--ghost { background: transparent; }
.btn--sm { padding: 5px 9px; font-size: var(--fs-xs); }
.btn--icon { padding: 6px; font-size: var(--fs-md); line-height: 1; }
.btn--ok { color: var(--good); border-color: var(--good); }
.btn--err { color: var(--bad); border-color: var(--bad); }
#copy-link { min-inline-size: 9ch; justify-content: center; }

@media (pointer: coarse) {
  .btn, .seg button { min-height: 44px; }
  .btn--icon { min-width: 44px; justify-content: center; }
  .check { min-height: 44px; }
  .check input { width: 20px; height: 20px; }
  /* Same 44px floor as every other interactive control on a coarse pointer - these two measured
     22.5px and 16.5px respectively, well under it, with nothing else on the page marking them
     as an exception. */
  .itin > summary { min-height: 44px; display: flex; align-items: center; }
  .itin-leg-verify { min-height: 44px; padding-block: 4px; }
}

/* ---------------------------------------------------------------- language picker modal */
#lang-toggle { font-size: var(--fs-sm); gap: 4px; }
#lang-code { font-weight: 700; letter-spacing: .3px; font-family: var(--font-num); }

.lang-overlay {
  position: fixed; inset: 0; z-index: 4000;
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  display: flex; align-items: center; justify-content: center; padding: var(--space-4);
  opacity: 0; transition: opacity .16s var(--ease);
}
.lang-overlay[hidden] { display: none; }
.lang-overlay.lang-overlay--open { opacity: 1; }

.lang-card {
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-3);
  max-inline-size: 28rem; width: min(560px, calc(100% - 2 * var(--space-4)));
  max-height: min(640px, 86vh);
  display: flex; flex-direction: column;
  padding: var(--space-4);
  opacity: 0; transform: translateY(8px);
  transition: opacity .2s var(--ease), transform .2s var(--ease);
}
.lang-overlay.lang-overlay--open .lang-card { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .lang-overlay, .lang-card { transition: none; }
}

.lang-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: var(--space-3); }
.lang-head h2 { margin: 0; font-size: var(--fs-lg); }

.lang-filter {
  width: 100%; background: var(--panel-2); border: 1px solid var(--line); color: var(--ink);
  border-radius: var(--radius-sm); padding-block: var(--space-2); padding-inline: 10px;
  font-size: var(--fs-md); font-family: inherit;
  margin-bottom: var(--space-3); flex: none;
}
.lang-filter:focus { outline: none; }
.lang-filter:focus-visible { border-color: var(--accent); }

.lang-list {
  list-style: none; margin: 0; padding: 0; overflow: auto; flex: 1;
  overscroll-behavior: contain; scrollbar-width: thin;
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
  scrollbar-gutter: stable;
}
@media (max-width: 520px) {
  .lang-list { grid-template-columns: 1fr; }
}

.lang-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: var(--space-2) 11px; border-radius: var(--radius-sm); border: 1px solid transparent;
  cursor: pointer; background: var(--panel-2);
}
@media (hover: hover) {
  .lang-item:hover { background: var(--line); }
}
.lang-item--focused {
  background: var(--panel-2);
  border-inline-start: 2px solid var(--accent);
  padding-inline-start: 9px;
}
.lang-item--active { border-color: var(--accent); }
.lang-item-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.lang-item-native { font-size: var(--fs-base); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lang-item-en { font-size: var(--fs-xs); color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lang-item-check { color: var(--good); font-weight: 700; flex: none; }
.lang-item--empty { color: var(--dim); cursor: default; justify-content: center; }
.lang-item--empty:hover { background: var(--panel-2); }

@media (pointer: coarse) {
  .lang-item { padding-block: 12px; min-height: 44px; }
}

/* ---------------------------------------------------------------- theme picker
   A small anchored listbox popup (not a full-screen modal like the language picker above - 
   9 rows never need a filter box, so the lighter "menu button" pattern fits better and stays
   compact at any viewport width with zero special-casing for 360px). */
.theme-picker { position: relative; }
#theme-toggle { position: relative; }

/* Decorative-only preview of the CURRENTLY active theme's own accent - no JS required to keep
   this in sync: it's var(--accent), so it repaints for free every time data-theme changes. */
.theme-swatch-dot {
  position: absolute; inset-inline-end: 4px; inset-block-end: 4px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); border: 1.5px solid var(--panel);
  pointer-events: none;
}

.theme-list {
  position: absolute; top: calc(100% + 6px); inset-inline-end: 0; z-index: 1300;
  width: max-content; min-width: 15rem;
  max-width: min(19rem, calc(100vw - 2 * var(--space-4)));
  max-height: min(24rem, 70vh); overflow-y: auto; overscroll-behavior: contain;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-3);
  list-style: none; margin: 0; padding: 6px;
}
.theme-list[hidden] { display: none; }

.theme-item {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 8px; border-radius: var(--radius-xs); border: 1px solid transparent;
  cursor: pointer;
}
@media (hover: hover) {
  .theme-item:hover { background: var(--panel-2); }
}
.theme-item:focus-visible {
  background: var(--panel-2);
  outline: 2px solid var(--accent); outline-offset: -2px;
}
.theme-item--active { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.theme-item-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.theme-item-name { font-size: var(--fs-base); font-weight: 600; }
.theme-item-desc { font-size: var(--fs-xs); color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.theme-item-check { color: var(--good); font-weight: 700; flex: none; }

/* Live two-tone previews (bg / accent) of every OTHER theme's real palette - necessarily
   hardcoded here rather than read from custom properties, since the point is showing what a
   theme that ISN'T active looks like. Keep these in sync with their [data-theme] block above
   if a palette ever changes. */
.theme-swatch { width: 20px; height: 20px; border-radius: 50%; flex: none; border: 1px solid rgba(0, 0, 0, .18); }
.theme-swatch--auto     { background: linear-gradient(135deg, #0c1519 50%, #f6f4ee 50%); }
.theme-swatch--dark     { background: linear-gradient(135deg, #0c1519 50%, #47b6ff 50%); }
.theme-swatch--light    { background: linear-gradient(135deg, #f6f4ee 50%, #086dbd 50%); }
.theme-swatch--oled     { background: linear-gradient(135deg, #000000 50%, #5ec2ff 50%); }
.theme-swatch--amber    { background: linear-gradient(135deg, #0a0704 50%, #ffb000 50%); }
.theme-swatch--contrast { background: linear-gradient(135deg, #000000 50%, #ffd400 50%); }
.theme-swatch--vintage  { background: linear-gradient(135deg, #f2e4c8 50%, #0d6e63 50%); }
.theme-swatch--sepia    { background: linear-gradient(135deg, #e3d3b0 50%, #2c4a6e 50%); }
.theme-swatch--coastal  { background: linear-gradient(135deg, #eaf1ee 50%, #00738f 50%); }

@media (pointer: coarse) {
  .theme-item { padding-block: 12px; min-height: 44px; }
}
@media (max-width: 360px) {
  .theme-list { min-width: 0; width: min(17rem, calc(100vw - 2 * var(--space-3))); }
}

/* ---------------------------------------------------------------- results panel */
#results {
  position: absolute; top: var(--space-4); inset-inline-end: var(--space-4); z-index: 1000; width: 360px;
  max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px); overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-4);
  transform: translateX(400px); transition: transform .28s var(--ease), opacity .28s var(--ease);
  opacity: 0;
}
#results.show { transform: translateX(0); opacity: 1; }
#results[hidden] { display: none; }

.results-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.results-title, .results-subtitle {
  font-size: var(--fs-sm); margin: 0; color: var(--dim);
  text-transform: uppercase; letter-spacing: .7px;
}
.results-subtitle { margin: var(--space-4) 0 var(--space-2); }

/* the recommendation card - the one hero surface per screen; flat color-mix tint, never a
   gradient (a gradient card is itself a template tell) - capped at a 14% brand wash. */
.rec-card {
  background: color-mix(in srgb, var(--rail) 10%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--rail) 30%, var(--panel));
  border-inline-start: 3px solid var(--rail);
  border-radius: var(--radius); padding: var(--space-3) var(--space-4); margin: var(--space-2) 0 var(--space-4);
}
.rec-card--direct {
  background: color-mix(in srgb, var(--arc) 10%, var(--panel));
  border-color: color-mix(in srgb, var(--arc) 30%, var(--panel));
  border-inline-start-color: var(--arc);
}

.rec-route { font-size: var(--fs-lg); font-weight: 600; margin: 0 0 6px; }
.rec-hero { margin: 0 0 6px; line-height: var(--lh-tight); display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.rec-hero-value {
  font-family: var(--font-num);
  font-size: var(--fs-hero); font-weight: 700; letter-spacing: -0.5px;
  color: var(--good); /* the savings figure is always green - never the accent blue */
}
.rec-card--direct .rec-hero-value { color: var(--arc); }
.rec-hero-label { font-size: var(--fs-sm); color: var(--dim); font-weight: 400; }
.rec-sub { font-size: var(--fs-base); color: var(--ink); line-height: 1.45; margin: 0; }
.rec-breakeven { font-size: var(--fs-sm); color: var(--dim); margin: 8px 0 0; line-height: 1.45; }

/* cheapest-day strip - one chip per date in the sweep window, sitting directly under the
   recommendation card because it answers the second half of the same question. A horizontal
   scroller, not a wrap: 7 chips (15 at the widest window) laid out in rows would push the
   option list clean out of the mobile sheet's peek height, and being visible inside that peek
   is the entire reason it mounts here. Mirrors under RTL for free - flex row starts at the
   inline-start edge, and the padding/border below are all logical properties. */
.date-strip { position: relative; margin: 0 0 var(--space-3); }
/* Edge-fade scroll affordance: only shown on whichever side actually has more chips to reveal
   (toggled in results.js's updateDateStripFades), so a strip that already fits everything shows
   no fade at all, but a strip that scrolls is obviously scrollable instead of looking like a
   fixed row that happens to clip its last chip. */
.date-strip::before, .date-strip::after {
  content: ""; position: absolute; top: 0; bottom: 8px; width: 22px; pointer-events: none;
  z-index: 1; opacity: 0; transition: opacity .15s var(--ease);
}
.date-strip::before { inset-inline-start: 0; background: linear-gradient(to right, var(--panel), transparent); }
.date-strip::after { inset-inline-end: 0; background: linear-gradient(to left, var(--panel), transparent); }
[dir="rtl"] .date-strip::before { background: linear-gradient(to left, var(--panel), transparent); }
[dir="rtl"] .date-strip::after { background: linear-gradient(to right, var(--panel), transparent); }
.date-strip--fade-start::before { opacity: 1; }
.date-strip--fade-end::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .date-strip::before, .date-strip::after { transition: none; }
}
.date-strip-head {
  font-size: var(--fs-sm); color: var(--dim); margin: 0 0 6px;
  text-transform: uppercase; letter-spacing: .7px;
}
.date-strip-list {
  list-style: none; margin: 0; padding: 0 0 4px;
  display: flex; gap: 6px;
  overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: thin;
  overscroll-behavior-x: contain;
}
.date-strip-item { scroll-snap-align: start; flex: 0 0 auto; display: flex; }
.date-chip {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-width: 84px; padding: 6px 8px;
  background: var(--panel-2); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-family: inherit; cursor: pointer; white-space: nowrap;
}
.date-chip:hover:not(:disabled) { border-color: var(--accent); }
/* Selected state is an inset ring rather than a thicker border - a border-width change would
   shift every other chip in the strip by a pixel each time the visitor picks a new day. */
.date-chip--active { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.date-chip--best { border-color: color-mix(in srgb, var(--good) 45%, var(--panel)); }
.date-chip--failed { color: var(--dim); cursor: default; }
.date-chip-day { font-size: var(--fs-xs); color: var(--dim); }
.date-chip--active .date-chip-day { color: var(--ink); }
.date-chip-price { font-family: var(--font-num); font-size: var(--fs-base); font-weight: 700; }
/* Both tags on one row - the cheapest chip gets wider, not taller, so the strip stays one line
   shorter and survives the mobile sheet's peek height. */
.date-chip-tags { display: flex; align-items: center; gap: 4px; }
.date-strip-note { font-size: var(--fs-xs); color: var(--dim); margin: 6px 0 0; line-height: 1.4; }
.date-strip-note--err { color: var(--warn); }

/* weather chip */
.wx { display: flex; align-items: center; gap: 10px; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--space-2) 12px; margin: 0 0 var(--space-4); box-shadow: var(--shadow-1); }
.wx-ico { font-size: 22px; line-height: 1; }
.wx-temp { font-size: var(--fs-md); font-weight: 700; }
.wx-feels { font-size: var(--fs-sm); color: var(--dim); font-weight: 400; }
.wx-desc { font-size: var(--fs-sm); color: var(--dim); text-transform: capitalize; }
.wx-fc { font-size: var(--fs-xs); color: var(--dim); margin-inline-start: auto; text-align: end; line-height: 1.35; }

/* option list - one bordered container with hairline separators, not seven sibling boxes */
.opt-list {
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}
.opt {
  padding: var(--space-2) var(--space-3);
  background: var(--panel-2);
}
.opt + .opt { border-block-start: 1px solid var(--line); }
.opt--win { border-inline-start: 3px solid var(--rail); }
.opt-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.opt-name { font-size: var(--fs-base); font-weight: 600; }
.opt-price { font-size: var(--fs-md); font-weight: 700; white-space: nowrap; text-align: end; }
.opt-meta { font-size: var(--fs-xs); color: var(--dim); margin-top: 3px; display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.leg { white-space: nowrap; font-family: var(--font-num); }

.opt-saves[data-sign="pos"] { color: var(--good); font-weight: 600; }
.opt-saves[data-sign="neg"] { color: var(--dim); }

.tag {
  font-size: var(--fs-xs); padding: 1px 6px; border-radius: var(--radius-xs);
  border: 1px solid var(--line); color: var(--dim); white-space: nowrap;
}
.tag--ok {
  color: var(--good);
  border-color: color-mix(in srgb, var(--good) 35%, var(--panel));
}
.tag--warn {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 35%, var(--panel));
}
.tag--bad {
  color: var(--bad);
  border-color: color-mix(in srgb, var(--bad) 35%, var(--panel));
}
.tag--base {
  color: var(--arc);
  border-color: color-mix(in srgb, var(--arc) 35%, var(--panel));
}

/* per-option CO2e estimate - always paired with the "greenest" text tag, never color-only */
.opt-meta--co2 { margin-top: 4px; }
.opt-co2 { color: var(--dim); }
.opt-co2--greenest { color: var(--good); font-weight: 600; }

/* per-option itinerary: real airports, an example (or live) clock schedule, per-leg price
   provenance, and a verify link - a <details> so a 5-option list doesn't force every leg's
   worth of text on screen at once; the disclosure triangle is free a11y from the native element. */
.itin { margin-top: var(--space-2); font-size: var(--fs-xs); }
.itin > summary {
  cursor: pointer; color: var(--dim); font-weight: 600;
  padding: 3px 0; list-style: none;
}
.itin > summary::-webkit-details-marker { display: none; }
.itin > summary::before { content: "▸ "; }
.itin[open] > summary::before { content: "▾ "; }
.itin-note { color: var(--dim); margin: 4px 0 6px; line-height: 1.4; }
.itin-legs {
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden;
}
.itin-leg { padding: 6px 9px; background: var(--panel); }
.itin-leg + .itin-leg { border-block-start: 1px solid var(--line); }
.itin-leg-route { font-weight: 600; color: var(--ink); }
.itin-leg-route .icon--arrow { margin-inline: 3px; }
.itin-leg-time { color: var(--dim); margin-top: 2px; font-family: var(--font-num); }
.itin-checkin { color: var(--dim); margin-top: 2px; }
.itin-leg-price { margin-top: 2px; }
.itin-leg-verify {
  display: inline-flex; align-items: center; gap: 3px; margin-top: 4px;
  color: var(--arc); text-decoration: none; font-weight: 600;
}
.itin-leg-verify:hover, .itin-leg-verify:focus-visible { text-decoration: underline; }

/* cheapest-vs-greenest callout under the recommendation card, when they differ */
.rec-greenest-note {
  font-size: var(--fs-sm); color: var(--ink); line-height: 1.45;
  margin: 0 0 var(--space-4); padding: var(--space-2) 11px;
  border: 1px solid color-mix(in srgb, var(--good) 30%, var(--panel));
  border-radius: var(--radius-sm); background: var(--panel-2);
}

.notes {
  font-size: var(--fs-xs); color: var(--dim); line-height: 1.5;
  margin-top: var(--space-3); border-top: 1px solid var(--line); padding-top: var(--space-3);
}
.notes-head { color: var(--warn); font-weight: 700; margin: 0 0 4px; }
.notes ul { margin: 0; padding-inline-start: 18px; }
.notes li { margin: 2px 0; }

/* empty/loading/error states */
.state-panel { padding: var(--space-2) 0; }
.state-title { font-size: var(--fs-md); font-weight: 700; margin: 0 0 6px; }
.state-body { font-size: var(--fs-base); color: var(--dim); line-height: 1.5; margin: 0; }
.state-panel--error .state-title { color: var(--bad); }
.state-panel--loading .state-title::after { content: ""; display: inline-block; width: 1em; }
@media (prefers-reduced-motion: no-preference) {
  .state-panel--loading .state-title::after { animation: ellipsis 1.4s steps(4, end) infinite; }
}
@keyframes ellipsis {
  0% { content: ""; }
  25% { content: "."; }
  50% { content: ".."; }
  75%, 100% { content: "..."; }
}

/* empty-state illustration - same arc/rail/node semantics as the map */
.empty-art { display: block; margin: 0 0 var(--space-3); }
.empty-art .arc-path { stroke: var(--arc); }
.empty-art .rail-path { stroke: var(--rail); }
.empty-art .dot-origin { fill: var(--arc); }
.empty-art .dot-node { fill: var(--ink); }
.empty-art .dot-dest { fill: var(--rail); }
@media (prefers-reduced-motion: no-preference) {
  .empty-art .draw-in {
    stroke-dasharray: 300;
    stroke-dashoffset: 300;
    animation: draw-in 1s var(--ease) forwards;
  }
}
@keyframes draw-in { to { stroke-dashoffset: 0; } }

/* spinner - a short dotted arc with a dot traveling it, echoing the brand motif */
#spinner {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1500;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-3);
  padding: 12px 20px; font-size: var(--fs-base); display: flex; align-items: center; gap: 10px;
}
#spinner[hidden] { display: none; }
.spinner-motif { width: 22px; height: 22px; overflow: visible; }
.spinner-track { stroke: var(--line); }
.spinner-dot {
  fill: none; stroke: var(--accent); stroke-width: 3; stroke-linecap: round;
  stroke-dasharray: 8 40;
  animation: spinner-travel 1.1s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .spinner-dot { animation: none; opacity: 1; stroke-dashoffset: 0; }
}
@keyframes spinner-travel { to { stroke-dashoffset: -48; } }

.icon { width: 1em; height: 1em; vertical-align: -0.125em; }

.leaflet-div-icon { background: transparent; border: 0; }
.pin-svg {
  width: 36px; height: 36px; display: grid; place-items: center;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .5));
}
.pin--origin { color: var(--arc); }
.pin--dest { color: var(--ink); }
.pin--hub { color: var(--hub); }
.pin--hub-rec { color: var(--rail); }

.legend {
  position: absolute; bottom: 12px; inset-inline-start: var(--space-4); z-index: 1000; font-size: var(--fs-xs);
  color: var(--dim); padding: var(--space-2) 11px; display: flex; flex-wrap: wrap; gap: 4px 12px;
  box-shadow: var(--shadow-1);
}
.legend-item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.swatch { width: 10px; height: 10px; border-radius: 50%; display: inline-block; background: currentColor; }

/* destination search + ARIA combobox */
.search { position: relative; margin: 2px 0 9px; }
.search input {
  width: 100%; background: var(--panel-2); border: 1px solid var(--line); color: var(--ink);
  border-radius: var(--radius-sm); padding-block: var(--space-2); padding-inline: 10px 30px;
  font-size: var(--fs-md); font-family: inherit;
}
.search input:focus { outline: none; }
.search input:focus-visible { border-color: var(--accent); }
.search .mag { position: absolute; inset-inline-end: 9px; top: 8px; color: var(--dim); font-size: var(--fs-md); pointer-events: none; }
.search--origin input { padding-inline-end: 10px; }

.aclist {
  position: absolute; inset-inline: 0; top: calc(100% + 4px); z-index: 1200;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm);
  max-height: 236px; overflow-y: auto; overscroll-behavior: contain; box-shadow: var(--shadow-2);
  list-style: none; margin: 0; padding: 0;
}
.aclist[hidden] { display: none; }
.aitem { padding: var(--space-2) 10px; font-size: var(--fs-base); cursor: pointer; border-bottom: 1px solid var(--line); line-height: 1.3; }
.aitem:last-child { border-bottom: 0; }
@media (hover: hover) {
  .aitem:hover { background: var(--panel-2); }
}
.aitem--active {
  background: var(--panel-2);
  border-inline-start: 2px solid var(--accent);
  padding-inline-start: 8px;
}
.aitem--empty { color: var(--dim); cursor: default; }
.aitem-type { display: block; font-size: var(--fs-xs); color: var(--dim); text-transform: uppercase; letter-spacing: .4px; margin-top: 1px; }

@media (pointer: coarse) {
  .aitem {
    padding-block: 12px; min-height: 44px;
    display: flex; flex-direction: column; justify-content: center;
  }
}

/* view-transition polish where supported - no-op elsewhere */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: .2s; }
}

/* Leaflet skin - stock white popups/controls are the most visibly unstyled surface in the
   app by default; theme every chrome element off the same 8 palette tokens. */
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line); box-shadow: var(--shadow-2);
  border-radius: var(--radius-sm);
}
.leaflet-popup-content { font-family: inherit; font-size: var(--fs-base); line-height: var(--lh-snug); margin: 10px 14px; }
.leaflet-popup-close-button { color: var(--dim) !important; font-size: 16px; padding: 6px; }
.leaflet-bar a { background: var(--panel); color: var(--ink); border-bottom-color: var(--line); }
@media (hover: hover) {
  .leaflet-bar a:hover { background: var(--panel-2); }
}
.leaflet-control-attribution {
  background: color-mix(in srgb, var(--panel) 85%, transparent);
  color: var(--dim);
}
.leaflet-control-attribution a { color: var(--accent); }

@media (pointer: coarse) {
  .leaflet-touch .leaflet-bar a { width: 44px; height: 44px; line-height: 44px; }
}

/* "Detailed map" toggle - a real <button> inside a .leaflet-bar shell so it matches the zoom
   control's chrome, not an <a> (Leaflet's own control links aren't focusable/actionable the
   way a semantic button is). */
.hh-detail-control { display: flex; }
.hh-detail-btn {
  font-family: inherit; cursor: pointer; border: 0; background: var(--panel); color: var(--ink);
  width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; padding: 0;
}
@media (hover: hover) {
  .hh-detail-btn:hover { background: var(--panel-2); }
}
.hh-detail-btn[aria-pressed="true"] { color: var(--accent); }
@media (pointer: coarse) {
  .hh-detail-btn { width: 44px; height: 44px; }
}

/* Optional live OpenStreetMap layer (map.js setDetailedMap) - one cartography, so dark themes
   get a CSS filter instead of a second tile set. Scoped by data-map-scheme, not data-theme
   directly: that attribute already collapses all 8 themes down to the two color schemes this
   filter actually needs to key off. */
[data-map-scheme="dark"] .hh-osm-tile {
  filter: invert(1) hue-rotate(180deg) saturate(0.7) brightness(0.9);
}

/* inputs */
#origin { font-family: var(--font-num); text-transform: uppercase; letter-spacing: .06em; }

/* kills iOS focus-zoom (Safari zooms in on any focused input under 16px). Matched on
   (pointer: coarse) too, not just the width breakpoint - a landscape phone (e.g. 812x375) is
   wider than 760px but is still a touch device with the exact same zoom-on-focus behavior, so
   width alone let it slip through. */
@media (max-width: 760px), (pointer: coarse) {
  .row input, .row select, .field-input, .search input, .lang-filter { font-size: 1rem; }
}

/* ================================================================== responsive */
/* Zero @media rules existed before this file - this is the real mobile rework: the
   control panel collapses to a compact top bar, and results become a swipe-friendly
   bottom sheet instead of a 360px fixed panel that doesn't fit a 375px screen. */
@media (max-width: 760px) {
  #controls {
    top: 0; inset-inline: 0; width: auto;
    border-radius: 0; border-left: 0; border-right: 0; border-top: 0;
    /* Capped well under half the screen, not 62vh - the map is the point of this app, and a
       top bar plus a results sheet that each claimed most of the viewport left it fully
       hidden behind both panels the moment a plan rendered (see #results below). The form is
       still there in full, just an internal scroll away, same as it already was at 62vh. */
    max-height: 38vh; max-height: 38dvh;
    /* manifest.webmanifest declares display:standalone (installable), and index.html's
       viewport meta now has viewport-fit=cover to actually activate env() - without both
       halves this padding silently resolves to 0 and does nothing. Mirrors the existing
       safe-area-inset-bottom on #results below; this bar had never had its own top counterpart,
       so an installed PWA on a notched phone could render its title/lang/theme buttons under
       the notch or status bar. */
    padding-top: calc(var(--space-4) + env(safe-area-inset-top));
  }
  #controls h1 { font-size: var(--fs-base); }

  #legend { display: none; } /* redundant with the on-screen labels at this width */

  #results {
    top: auto; inset-inline: 0; bottom: 0; width: auto;
    /* Peek height by default: tall enough for the hero recommendation, short enough that it
       can't ever meet #controls' own cap above (38vh + 40vh <= 78vh, always leaving a real
       strip of map visible). #sheet-toggle (rendered by results.js, only for a real plan)
       expands this to the full option list on demand via .results-expanded. */
    max-height: 40vh; max-height: 40dvh;
    /* Re-states the base transform/opacity transition (not just adds to it - a `transition`
       shorthand here would otherwise fully replace, not merge with, the desktop rule above)
       plus the new max-height animation for the peek<->expanded toggle. */
    transition: transform .28s var(--ease), opacity .28s var(--ease), max-height .22s var(--ease);
    border-radius: var(--radius) var(--radius) 0 0;
    border-bottom: 0;
    transform: translateY(100%);
    padding-top: var(--space-2);
    padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom));
    overscroll-behavior: contain;
  }
  #results.show { transform: translateY(0); }
  #results.results-expanded { max-height: 72vh; max-height: 72dvh; }
  #results::before {
    content: ""; display: block; width: 36px; height: 4px; border-radius: 999px;
    background: var(--line); margin: 0 auto var(--space-2);
  }

  #spinner { display: none; } /* the sheet carries loading state on mobile - no duplicate indicator */

  .row { flex-wrap: wrap; }
}

/* Bottom-sheet expand/collapse control - renderPlan() only (nothing to expand in the empty/
   loading/error states). display:none by default and only switched on inside the mobile
   @media block below - this order matters: both rules share one class' worth of specificity,
   so whichever comes LATER in the file wins the tie regardless of viewport. The base rule has
   to come first, or the mobile override loses on every screen size including mobile. */
.sheet-toggle {
  display: none; width: 100%; justify-content: center;
  margin: 0 0 var(--space-3);
}
.sheet-toggle .icon { transform: rotate(90deg); transition: transform .15s var(--ease); }
.sheet-toggle[aria-expanded="true"] .icon { transform: rotate(-90deg); }
@media (prefers-reduced-motion: reduce) {
  #results { transition: none; }
  .sheet-toggle .icon { transition: none; }
}
@media (max-width: 760px) {
  .sheet-toggle { display: flex; }
}

@media (max-width: 420px) {
  .rec-hero-value { font-size: 2rem; }
  #controls, #results { padding-inline: var(--space-3); }
}

/* ================================================================== RTL
   Mechanical logical-property sweep: every offset/padding/margin above already uses
   inset-inline-*/padding-inline/margin-inline, so it mirrors automatically under dir="rtl"
   with no [dir="rtl"] overrides needed. The handful of things logical properties can't
   reach — transforms, letter-spacing (severs Arabic glyph joining), bidi isolation for
   embedded Latin fragments (IATA codes), and icon mirroring — are called out explicitly
   here. The Leaflet map itself stays LTR internally (tile math, popups); this section only
   affects page chrome. */
[dir="rtl"] #results:not(.show) { transform: translateX(-400px); }
/* #results.show { transform: translateX(0) } is already direction-neutral - no override needed */

[dir="rtl"] h1,
[dir="rtl"] .results-title,
[dir="rtl"] .results-subtitle { letter-spacing: 0; }

.rec-route, .opt-name, .leg, .results-subtitle { unicode-bidi: isolate; }

[dir="rtl"] .icon--arrow,
[dir="rtl"] .empty-art { transform: scaleX(-1); }

@media (max-width: 760px) {
  [dir="rtl"] #results:not(.show) { transform: translateY(100%); }
}

/* Translated place labels drawn over the label-free base tiles (map.js renderGeoLabels).
   Names come from Intl.DisplayNames for the active locale; the boxes are non-interactive so
   map clicks pass through. A dark translucent pill keeps them legible on every theme's base. */
.geo-label { background: none; border: 0; }
.geo-label > span {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  pointer-events: none;
  font: 600 11px/1.15 var(--font, system-ui, -apple-system, sans-serif);
  color: #eef2f6;
  background: rgba(12, 15, 20, 0.55);
  padding: 1px 6px;
  border-radius: 5px;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.55);
}
.geo-label--continent > span {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: rgba(12, 15, 20, 0.42);
}

/* With one world (no wrap), a wide viewport at min zoom shows void beside the map. Tint it to
   the scheme so it reads as intentional margin, not a black bar. light-dark() follows the
   color-scheme every [data-theme] block sets. */
.leaflet-container { background: light-dark(#dbe2e8, #0b0f13); }
