/* 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 */
  --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;              /* Voyager water tint — no white flash */
  --map-ref:   #8a989e;
  --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;
  --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;
  --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;
  --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;
  --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;
  --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;
  --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); }

/* 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; }
}

/* ---------------------------------------------------------------- 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; }

/* 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; }

/* 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; }

.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; }
}

/* 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%); }
}
