/* triplo — fullscreen map, floating chrome.
   Every color, radius, shadow, and easing derives from the :root tokens below;
   map JS reads the --cat-* custom properties too, so they are the single source
   of the category palette. */

@font-face {
  font-family: "Overpass";
  font-style: normal;
  font-weight: 700 800;
  font-display: swap;
  src: url("/static/fonts/overpass.woff2") format("woff2");
}

:root {
  --bg: #f2f5f2;
  --surface: #ffffff;
  --surface-rgb: 255, 255, 255; /* channel triplet behind surface-tinted washes */
  --surface-glass: rgba(255, 255, 255, 0.84);
  --ink: #17211d;
  --ink-rgb: 23, 33, 29; /* channel triplet behind every ink-tinted wash */
  --muted: #5f6f68;
  --line: rgba(var(--ink-rgb), 0.1);
  --brand: #0f766e;
  --brand-deep: #0a4f4a; /* text/link tone on --bg; light in the dark scheme */
  --brand-hover: #0a4f4a; /* primary-button hover bg: must hold white text in both schemes */
  --brand-tint: #e4f1ef;
  --danger: #c03b2d;
  --scrim: rgba(10, 16, 13, 0.35); /* overlay backdrops: stays dark in both schemes */
  --marker-ring: #ffffff; /* map-marker strokes/rings against the tile layer */
  --warn-bg: #fff7ed;
  --warn-line: #fed7aa;
  --warn-ink: #9a3412;
  --warn-strong: #b45309;
  --cat-sight: #2563eb;
  --cat-food: #d97706;
  --cat-sleep: #7c3aed;
  --cat-other: #64748b;
  --radius-lg: 18px;
  --radius-md: 12px;
  --radius-pill: 999px;
  --shadow-1: 0 1px 2px rgba(var(--ink-rgb), 0.06), 0 4px 14px rgba(var(--ink-rgb), 0.08);
  --shadow-2: 0 2px 6px rgba(var(--ink-rgb), 0.08), 0 16px 40px rgba(var(--ink-rgb), 0.18);
  --blur-glass: blur(14px) saturate(1.4);
  --ease-drop: cubic-bezier(0.2, 0.9, 0.3, 1.15);
  --dur-1: 140ms;
  --dur-2: 240ms;
  --z-controls: 20;
  --z-nav: 30;
  --z-card: 40;
  --z-overlay: 50;
  /* Bottom-chrome geometry, shared by every rule that stacks above the dock
     (drawers, flyouts, locate). --dock-clear = dock pill height + gap. */
  --dock-clear: 56px;
  --panel-w: 372px;
  --panel-shove: 384px; /* --panel-w + edge margins: how far side chrome slides */
  /* Breakpoint contract: 760px splits mobile sheet chrome from desktop side
     panels (max-width: 760px / min-width: 761px). JS mirrors the mobile query
     as window.triploMqMobile. Panel width tokens above (--panel-w / --panel-shove)
     size the desktop drawers and the locate shove. */

  --font-display: "Overpass", system-ui, -apple-system, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* Dark scheme: token overrides only — components derive from the tokens.
   Ink flips to a light green-grey, surfaces to green-tinted near-blacks;
   category and warn hues brighten to hold contrast on dark surfaces. */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #101613;
    --surface: #1a211d;
    --surface-rgb: 26, 33, 29;
    --surface-glass: rgba(24, 31, 27, 0.8);
    --ink: #e6ece8;
    --ink-rgb: 230, 236, 232;
    --muted: #9aa8a1;
    --brand: #12968c;
    --brand-deep: #6fd0c7;
    --brand-hover: #10847b;
    --brand-tint: rgba(20, 168, 158, 0.16);
    --danger: #e0685a;
    --scrim: rgba(0, 0, 0, 0.55);
    --marker-ring: #1c2420;
    --warn-bg: rgba(240, 160, 75, 0.14);
    --warn-line: rgba(240, 160, 75, 0.3);
    --warn-ink: #f0b070;
    --warn-strong: #f0a04b;
    --cat-sight: #5b8bf7;
    --cat-food: #f0a04b;
    --cat-sleep: #a78bfa;
    --cat-other: #94a3b8;
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 14px rgba(0, 0, 0, 0.45);
    --shadow-2: 0 2px 6px rgba(0, 0, 0, 0.45), 0 16px 40px rgba(0, 0, 0, 0.6);
  }
}

/* Glass needs backdrop-filter; fall back to solid panels where unsupported. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root { --surface-glass: var(--surface); --blur-glass: none; }
}

/* Outdoor / high-contrast: solid panels over live map tiles (no glass wash).
   html.outdoor is toggled from Settings (triplo:outdoor); prefers-contrast
   matches system “more contrast” without a visit to Settings. */
html.outdoor {
  --surface-glass: var(--surface);
  --blur-glass: none;
}
@media (prefers-contrast: more) {
  :root {
    --surface-glass: var(--surface);
    --blur-glass: none;
  }
}


* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: var(--bg);
  line-height: 1.5;
}

/* Map pages own the viewport; the map is the page. */
body.page-map { overflow: hidden; overscroll-behavior: none; }

a { color: var(--brand-deep); }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.01em;
}

button {
  font: inherit;
  cursor: pointer;
  border: none;
  border-radius: var(--radius-md);
  padding: 9px 16px;
  background: var(--brand);
  color: #fff;
  font-weight: 600;
}

@media (hover: hover) {
  button:hover { background: var(--brand-hover); }
}

/* An anchor that looks like the primary button (e.g. the Shortcut install link). */
.btn-link {
  display: inline-block;
  border-radius: var(--radius-md);
  padding: 11px 18px;
  background: var(--brand);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
}
@media (hover: hover) {
  .btn-link:hover { background: var(--brand-hover); }
}

button.secondary { background: rgba(var(--ink-rgb), 0.06); color: var(--ink); }
@media (hover: hover) {
  button.secondary:hover { background: rgba(var(--ink-rgb), 0.12); }
}
button.danger { background: var(--danger); }

button.link {
  background: transparent;
  color: var(--brand-deep);
  padding: 2px 4px;
  font-size: 13px;
  font-weight: 600;
}
@media (hover: hover) {
  button.link:hover { background: rgba(var(--ink-rgb), 0.07); }
}
button.link.danger { color: var(--danger); }

/* In-flight state for hx-disabled-elt targets; per-control rules may tighten it. */
button[disabled] { opacity: 0.55; cursor: default; }

/* Shared loading affordance: a small brand ring next to "Loading…" text.
   htmx-indicator variants stay hidden until htmx marks the request in flight. */
.loading-hint { display: flex; align-items: center; gap: 8px; }
.spinner {
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--brand-tint);
  border-top-color: var(--brand);
  animation: spinner-turn 700ms linear infinite;
}
@keyframes spinner-turn {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; }
}
.spinner.htmx-indicator { display: none; }
.htmx-request .spinner.htmx-indicator, .htmx-request.spinner.htmx-indicator { display: inline-block; }

.icon-btn {
  background: transparent;
  color: var(--muted);
  width: 30px;
  height: 30px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  flex: none;
}
@media (hover: hover) {
  .icon-btn:hover { background: rgba(var(--ink-rgb), 0.07); color: var(--ink); }
}
.icon-btn svg { width: 15px; height: 15px; display: block; }

input, select, textarea {
  font: inherit;
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
}

input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

:is(button, a, [tabindex]):focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin: 12px 0 4px;
  color: var(--muted);
}

.muted { color: var(--muted); }
.error { color: var(--danger); font-size: 13.5px; }
.hint { font-size: 12px; color: var(--muted); }

/* ---- wordmark ----
   Lowercase Overpass (open-source Highway Gothic) with the dot of the i
   replaced by a teal pin-dot: markup is tr<i>ı</i>plo (dotless ı). */
.wordmark {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 19px;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
}
.wordmark i { font-style: normal; position: relative; }
.wordmark i::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 0.2em;
  height: 0.2em;
  margin-left: -0.1em;
  border-radius: 50%;
  background: var(--brand);
}

/* ---- floating nav pill ---- */
.float-nav {
  position: fixed;
  top: max(14px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 6px 8px 6px 16px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-1);
  white-space: nowrap;
}
.float-nav .wordmark { margin-right: 12px; }

/* Non-map pages keep the identical pill, stuck to the top of the scroll. */
body:not(.page-map) .float-nav {
  position: sticky;
  top: 14px;
  left: auto;
  transform: none;
  margin: 14px auto 0;
  width: fit-content;
}

.pill-link {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border-radius: var(--radius-pill);
  text-decoration: none;
  color: var(--muted);
  font-weight: 600;
  font-size: 14px;
  line-height: 1;
}
@media (hover: hover) {
  .pill-link:hover { color: var(--ink); background: rgba(var(--ink-rgb), 0.05); }
}
.pill-link.active { color: var(--brand-deep); background: var(--brand-tint); }
.pill-ic { display: none; }
.pill-ic svg { width: 18px; height: 18px; display: block; }

/* The map pages' top search bar: a button styled as a search field that
   opens the spotlight overlay. */
.nav-searchbar {
  display: flex;
  align-items: center;
  gap: 8px;
  width: min(340px, calc(100vw - 170px));
  padding: 8px 12px;
  background: rgba(var(--ink-rgb), 0.05);
  color: var(--muted);
  border-radius: var(--radius-pill);
  font-size: 14px;
  line-height: 1;
  text-align: left;
}
@media (hover: hover) {
  .nav-searchbar:hover { background: rgba(var(--ink-rgb), 0.1); color: var(--ink); }
}
.nav-searchbar svg { width: 15px; height: 15px; display: block; flex: none; }
.nav-search-hint {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav-searchbar kbd {
  flex: none;
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--surface);
  font-size: 11px;
  color: var(--muted);
}

/* Settings floats apart from the pill as a small corner icon. */
.settings-corner {
  position: fixed;
  top: max(14px, env(safe-area-inset-top));
  right: max(14px, env(safe-area-inset-right));
  z-index: var(--z-nav);
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  font: inherit;
  padding: 0;
}
@media (hover: hover) {
  .settings-corner:hover { background: var(--surface); color: var(--ink); }
}
.settings-corner svg { width: 18px; height: 18px; display: block; }
.pill-search kbd {
  font: 600 11px/1 var(--font-mono);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 2px 5px;
  color: var(--muted);
}

/* ---- non-map page column ---- */
.content { padding: 28px 20px 48px; max-width: 720px; margin: 0 auto; }

.placeholder { text-align: center; padding: 64px 16px; }
.placeholder h1 { margin: 0 0 8px; }

/* ---- auth ---- */
.auth { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.auth-box { width: 100%; max-width: 380px; text-align: center; }
.auth-box .wordmark { font-size: 42px; display: inline-block; }
.auth-box .tagline { color: var(--muted); margin: 6px 0 24px; }
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  padding: 24px;
  text-align: left;
}
.card form button { width: 100%; margin-top: 16px; }
.card.sent { text-align: center; }

/* ---- fullscreen map ---- */
/* #trip-map is the public trip page's container (public-trip.js); the
   authenticated shell uses #map in both modes. */
.page-map #map,
.page-map #trip-map {
  position: fixed;
  inset: 0;
  height: 100vh;
  height: 100dvh;
  width: 100%;
  z-index: 0;
  /* Trap Leaflet's internal z-indexes (up to 1000) inside the map's own
     stacking context so they can never float above the pill/card/overlay. */
  isolation: isolate;
  background: #e3ebe7;
}

/* ---- bottom dock (map pages): the nav pill's bottom-center counterpart.
   "Your pins" grows the drawer upward out of the dock — its head switches
   between the pin list and collections management. ---- */
.dock {
  position: fixed;
  bottom: max(16px, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-controls);
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 6px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-1);
  white-space: nowrap;
}
.dock-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  /* Fixed height: the count badge is taller than the icon/label line, so a
     content-sized button would grow when the badge appears. */
  height: 36px;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink);
  font-weight: 600;
  font-size: 14px;
  line-height: 1;
}
@media (hover: hover) {
  .dock-btn:hover { background: rgba(var(--ink-rgb), 0.05); }
}
a.dock-btn { text-decoration: none; }
.dock-btn[aria-expanded="true"] { color: var(--brand-deep); background: var(--brand-tint); }
.dock-btn svg { width: 16px; height: 16px; display: block; }
.dock-sep { width: 1px; height: 20px; background: var(--line); flex: none; }
.dock-count {
  min-width: 19px;
  padding: 4px 6px;
  display: grid;
  place-items: center;
  background: var(--brand);
  color: #fff;
  font: 700 11px/1 var(--font-display);
  border-radius: var(--radius-pill);
}
.dock-count[hidden] { display: none; }


/* Single-button side pill (notifications, bottom left). */
button.dock {
  gap: 7px;
  /* Match the main dock pill's overall height (6px pad + 36px buttons). */
  height: 48px;
  padding: 0 15px;
  color: var(--ink);
  font-weight: 600;
  font-size: 14px;
  line-height: 1;
}
@media (hover: hover) {
  button.dock:hover { background: var(--surface); }
}
button.dock[aria-expanded="true"] { color: var(--brand-deep); }
button.dock svg { width: 16px; height: 16px; display: block; }
.dock-left { left: max(16px, env(safe-area-inset-left)); transform: none; }

/* ---- flyouts (notifications, trips): anchored glass panels that grow
   upward out of their side pill ---- */
.flyout {
  position: fixed;
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + var(--dock-clear));
  z-index: 21; /* transient panels stack one above the drawers/sheet */
  width: 336px;
  max-width: calc(100vw - 28px);
  max-height: min(55vh, calc(100dvh - 160px));
  display: flex;
  flex-direction: column;
  padding: 14px 14px 10px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  transition: transform var(--dur-2) ease, opacity var(--dur-2) ease, visibility 0s;
}
.flyout-left { left: max(16px, env(safe-area-inset-left)); transform-origin: 0 100%; }
/* The trips picker and settings: right-side panels. On desktop they take the
   full map height (stacking over the trip panel when one is open — pick, then
   it dismisses); on mobile they anchor above the dock like other flyouts. */
.flyout.trips-side,
.flyout.settings-side { right: max(14px, env(safe-area-inset-right)); transform-origin: 100% 100%; }
@media (min-width: 761px) {
  .flyout.trips-side,
  .flyout.settings-side {
    top: max(64px, calc(env(safe-area-inset-top) + 50px));
    bottom: 14px;
    width: var(--panel-w);
    max-height: none;
  }
}
/* Stay rendered while hidden so open/close can transition (like .drawer). */
.flyout[hidden] {
  display: flex;
  transform: translateY(14px) scale(0.97);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: transform var(--dur-2) ease, opacity var(--dur-2) ease, visibility 0s var(--dur-2);
}
.flyout-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.flyout-head h2 { margin: 0; font-size: 16px; }
.flyout-panel { overflow-y: auto; overflow-x: clip; min-height: 0; padding-bottom: 4px; }

/* ---- floating drawer (pin list / route controls) ---- */
.drawer {
  position: fixed;
  top: max(74px, calc(env(safe-area-inset-top) + 74px));
  left: max(14px, env(safe-area-inset-left));
  z-index: var(--z-controls);
  width: 332px;
  max-width: calc(100vw - 28px);
  max-height: calc(100dvh - 100px);
  display: flex;
  flex-direction: column;
  padding: 14px 14px 8px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  transition: transform var(--dur-2) ease, opacity var(--dur-2) ease, visibility 0s;
}
.drawer:not(.open) {
  transform: translateX(-18px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: transform var(--dur-2) ease, opacity var(--dur-2) ease, visibility 0s var(--dur-2);
}
/* Map-page variant: anchored to the bottom dock row, growing upward. It
   hugs the left edge on desktop so the panel never covers the middle of the
   map (routes and pin clusters center there). */
.drawer.drawer-dock {
  top: auto;
  left: max(14px, env(safe-area-inset-left));
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + var(--dock-clear));
  transform: none;
  transform-origin: 0 100%;
  width: var(--panel-w);
  max-height: min(60vh, calc(100dvh - 160px));
}
.drawer.drawer-dock:not(.open) {
  transform: translateY(14px) scale(0.97);
}
/* Side-panel variants: pins open as a full-height LEFT panel, the trip as a
   full-height RIGHT panel — both can be open at once, and both stay floating
   (margins, rounded corners, shadow), never hard-edged sidebars. They start
   below the top chrome line. */
@media (min-width: 761px) {
  .drawer.drawer-dock.drawer-pins,
  .drawer.drawer-dock.drawer-trip {
    top: max(64px, calc(env(safe-area-inset-top) + 50px));
    bottom: 14px;
    max-height: none;
  }
  .drawer.drawer-dock.drawer-trip {
    left: auto;
    right: max(14px, env(safe-area-inset-right));
    transform-origin: 100% 100%;
  }
  /* The open left panel claims the bottom-left corner: slide the
     notifications bell out from under it — and its flyout too, so the
     panel never detaches from the bell it grows out of. */
  body:has(.drawer-pins.open) .dock-left {
    left: calc(max(16px, env(safe-area-inset-left)) + var(--panel-shove));
  }
  body:has(.drawer-pins.open) .flyout-left {
    left: calc(max(16px, env(safe-area-inset-left)) + var(--panel-shove));
  }
}
.drawer-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.drawer-head h2 { margin: 0; font-size: 16px; }
/* The trip drawer's pinned route figures (distance · time · pins), refreshed
   out-of-band by every #route-results swap. Collapses when routeless. */
.trip-stats {
  display: flex;
  gap: 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand-deep);
  padding: 0 2px;
}
.trip-stats:has(span) { padding-bottom: 8px; }
/* The pins drawer's body swap is CSS-only off data-view: "pins" is the
   grouped pin list, "manage" is one collection's management panel. That way
   pins-filter.js can keep toggling `hidden` on the (hidden) pin list without
   leaking into the manage view. */
.drawer-pins[data-view="manage"] #pin-controls,
.drawer-pins[data-view="manage"] #pin-panel,
.drawer-pins[data-view="manage"] .new-collection { display: none; }
.drawer-pins:not([data-view="manage"]) #collections-panel { display: none; }
/* Trip mode: the pins drawer is list-only — collection management (the
   per-group Edit buttons, the create form, the manage panel) and the manage
   view itself are pins-mode features. The "← Pins map" back link only makes
   sense with a trip to leave. */
body.mode-trip #pin-drawer .col-manage,
body.mode-trip #pin-drawer .new-collection,
body.mode-trip #pin-drawer #collections-panel { display: none; }
body:not(.mode-trip) .pins-map-back { display: none; }
/* The spotlight's near-the-route category chips only mean something with a
   route to search along. */
body:not(.mode-trip) .spotlight-cats { display: none; }
.drawer .back { display: inline-block; font-size: 13px; text-decoration: none; color: var(--muted); }
/* The drawer's back control may be a button (← Trips opens the picker) —
   strip the default button chrome so it reads like the link it replaces. */
.drawer button.back { background: transparent; padding: 0; border-radius: 0; font-weight: 600; }
@media (hover: hover) {
  .drawer button.back:hover { background: transparent; color: var(--ink); }
}
.drawer .trip-title { font-size: 17px; margin: 2px 0 0; overflow-wrap: anywhere; }
/* The trip picker looks like the panel title until you interact with it. */
.trip-picker {
  display: block;
  margin: 2px 0 0;
  padding: 2px 4px 2px 0;
  border: none;
  background: transparent;
  font: 700 17px/1.3 var(--font-body);
  color: var(--ink);
  max-width: 230px;
  cursor: pointer;
}
@media (hover: hover) {
  .trip-picker:hover { color: var(--brand-deep); }
}
/* The scrollable region; content must stay in the DOM while the drawer is
   closed (the map reads #pins-data from inside it). */
/* Vertical scrollers only: stray horizontal overhang (e.g. the row menus'
   negative touch-target margins) must not make the whole panel pan sideways —
   .route-chips is the one strip that scrolls horizontally, on its own. */
.drawer #pin-panel, .drawer .drawer-scroll { overflow-y: auto; overflow-x: clip; min-height: 0; padding-bottom: 6px; }

/* ---- pin filter controls ---- */
.pin-controls { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; flex: none; }
.pin-controls[hidden] { display: none; }
/* Trip drawer variant: sits between the route form and the results. */
.trip-pin-controls { margin: 2px 0 12px; padding-top: 10px; border-top: 1px solid var(--line); }
.pin-controls-row { display: flex; align-items: center; gap: 8px; }
.pin-chips { display: flex; flex-wrap: wrap; gap: 6px; flex: 1; }
.pin-search { background: rgba(var(--surface-rgb), 0.75); }
.pin-sort { width: auto; flex: none; padding: 7px 9px; font-size: 13px; background: rgba(var(--surface-rgb), 0.75); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 11px;
  border-radius: var(--radius-pill);
  background: rgba(var(--ink-rgb), 0.05);
  color: var(--ink);
  border: 1px solid transparent;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1;
}
@media (hover: hover) {
  button.chip:hover { background: rgba(var(--ink-rgb), 0.1); }
}
.chip .dot { width: 9px; height: 9px; margin: 0; }
.spotlight-paste { display: flex; margin: 2px 0 0; }
.spotlight-paste .chip svg { width: 14px; height: 14px; }

/* ---- bulk import ---- */
.import-page form { margin-top: 14px; }
.import-url-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.import-url-row input {
  flex: 1;
  min-width: 0;
}
.import-url-row button {
  flex: none;
  padding-inline: 16px;
}
.import-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 6px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.import-or::before,
.import-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}
.import-file-form button {
  width: 100%;
  margin-top: 10px;
}

/* Review: destination + bulk tools + dense place list. */
.import-review-head h2 { margin-bottom: 4px; }
.import-review-head .muted { margin: 0 0 4px; }
.import-review-head .hint { margin: 0 0 12px; }
.import-source {
  color: var(--brand-deep);
  font-weight: 600;
}
.import-dest {
  margin: 4px 0 2px;
}
.import-dest select {
  width: 100%;
}
.import-link-offer {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 10px 0 2px;
  font-size: 13px;
  color: var(--muted);
  cursor: pointer;
}
.import-link-offer input {
  margin-top: 2px;
  flex-shrink: 0;
}
.sync-flash {
  margin: 0 0 12px;
  padding: 8px 10px;
  border-radius: 10px;
  background: var(--brand-tint);
  color: var(--brand-deep);
  font-size: 13px;
  font-weight: 600;
}
.import-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px 14px;
  flex-wrap: wrap;
  margin: 12px 0 2px;
  font-size: 13px;
}
.import-toolbar-select {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.import-bulk-cat {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-weight: 400;
  color: var(--muted);
}
.import-bulk-cat span {
  white-space: nowrap;
}
.import-bulk-cat select {
  width: auto;
  min-width: 7rem;
  font: inherit;
  font-size: 13px;
  padding: 6px 10px;
}

/* Each ready row: checkbox | (name + category on one line, meta below).
   Global input/select { width:100% } is overridden so the category column
   never collapses onto the name field on narrow phones. */
.import-rows {
  margin: 8px 0 0;
  border-top: 1px solid var(--line);
}
.import-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 0;
  border-bottom: 1px solid var(--line);
}
.import-row:last-child { border-bottom: 0; }
.import-check {
  position: relative;
  flex: none;
  width: 22px;
  height: 22px;
  margin: 9px 0 0; /* align with name input centerline */
  display: block;
  cursor: pointer;
}
.import-check input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: pointer;
  z-index: 1;
}
.import-check-box {
  display: block;
  width: 22px;
  height: 22px;
  border: 1.5px solid rgba(var(--ink-rgb), 0.28);
  border-radius: 7px;
  background: var(--surface);
  box-shadow: inset 0 1px 0 rgba(var(--surface-rgb), 0.8);
  transition: background var(--dur-1) ease, border-color var(--dur-1) ease, box-shadow var(--dur-1) ease;
}
.import-check input:checked + .import-check-box {
  background: var(--brand);
  border-color: var(--brand);
  box-shadow: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='M3.5 8.2 6.6 11.2 12.5 4.8'/></svg>");
  background-size: 14px 14px;
  background-repeat: no-repeat;
  background-position: center;
}
.import-check input:focus-visible + .import-check-box {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}
.import-check-empty {
  width: 22px;
  height: 22px;
  margin-top: 9px;
  flex: none;
}
.import-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.import-top {
  display: flex;
  align-items: stretch;
  gap: 8px;
  min-width: 0;
}
.import-top input[type="text"] {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  padding: 8px 10px;
  font-size: 14px;
  font-weight: 500;
  text-overflow: ellipsis;
}
.import-top .import-cat,
.import-row select.import-cat {
  flex: 0 0 auto;
  width: 6.5rem;
  max-width: 34%;
  padding: 8px 8px;
  font-size: 13px;
  color: var(--ink);
  background: var(--bg);
}
.import-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding-left: 2px;
  font-size: 12px;
  line-height: 1.35;
  min-height: 1.2em;
}
.import-meta .import-coords,
.import-meta .import-dupe,
.import-meta .import-matched {
  display: inline;
}
.import-coords {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.import-address,
.import-note {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.import-note {
  font-style: italic;
}
.import-matched { font-size: 12px; }
.import-dupe {
  font-size: 12px;
  color: var(--muted);
  font-weight: 600;
}
.import-pending { opacity: 0.72; }
.import-pending-name {
  flex: 1;
  min-width: 0;
  padding: 8px 2px;
  font-size: 14px;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.import-pending-status {
  flex: none;
  padding: 8px 2px;
  font-size: 12px;
}
.import-skipped { opacity: 0.78; }
/* Confirm bar sticks so a long list doesn't bury it. Solid surface + an
   opaque lift above the hairline so scrolling rows never ghost through. */
.import-actions {
  position: sticky;
  bottom: 0;
  z-index: 5;
  margin: 8px -18px 0; /* bleed to card-block padding edge */
  padding: 14px 18px 14px;
  background: var(--surface);
  border-top: 1px solid var(--line);
  /* Opaque mask (not a translucent shadow) — kills mid-row bleed. */
  box-shadow: 0 -20px 0 0 var(--surface);
}
.import-actions button {
  width: 100%;
  box-shadow: var(--shadow-1);
}
.import-review .warn {
  margin: 0 0 10px;
  padding: 8px 10px;
  border-radius: var(--radius-md);
  background: var(--warn-bg);
  border: 1px solid var(--warn-line);
  color: var(--warn-ink);
  font-size: 13px;
}

/* ---- place photos (downloaded from the share source) ---- */
.popup-photo {
  display: block;
  width: 100%;
  max-height: 130px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  margin-bottom: 6px;
}
.pin-photo {
  width: 100%;
  max-height: 140px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  margin-bottom: 2px;
}
.editor-photo {
  margin: 0 0 10px;
  position: relative;
}
.editor-photo img {
  width: 100%;
  max-height: 150px;
  object-fit: cover;
  border-radius: var(--radius-md);
  display: block;
}
.editor-photo[hidden] { display: none; }
.editor-photo-remove {
  position: absolute;
  top: 6px;
  right: 6px;
  padding: 3px 8px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border-radius: var(--radius-pill);
}

/* Alternative matches offered in the confirm-shared-place editor. */
.share-candidates {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 4px 0 8px;
}
.share-candidates .share-candidate {
  font-size: 0.85rem;
  text-align: left;
}
.chip[aria-pressed="false"] { opacity: 0.45; }
.chip[aria-pressed="false"] .dot { background: var(--muted); }

/* ---- category dots ---- */
.dot { width: 12px; height: 12px; border-radius: 50%; flex: none; background: var(--cat-other); }
.dot-sight { background: var(--cat-sight); }
.dot-food { background: var(--cat-food); }
.dot-sleep { background: var(--cat-sleep); }
.dot-other { background: var(--cat-other); }

/* ---- pin list ---- */
.pin-list h2 {
  font: 700 11.5px/1 var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  margin: 8px 0 2px;
}
.pin-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 6px;
  border-bottom: 1px solid var(--line);
}
.pin-row[hidden] { display: none; }
.pin-row .dot { margin-top: 4px; }

/* Pins-list rows are a compact disclosure: a reset header button (dot + name +
   caret) that expands to reveal the category, notes, and actions. The base
   .pin-row flex layout above still serves the survivor and plan rows. */
.pin-list .pin-row { display: block; padding: 0; }
/* Keep filter/search hiding working: this rule and .pin-row[hidden] share
   specificity, so the pins-list variant needs its own [hidden] override. */
.pin-list .pin-row[hidden] { display: none; }
.pin-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 6px;
  background: transparent;
  color: var(--ink);
  border: none;
  border-radius: var(--radius-md);
  font: inherit;
  text-align: left;
}
@media (hover: hover) {
  .pin-head:hover { background: rgba(var(--surface-rgb), 0.65); }
}
.pin-head .dot { margin: 0; }
.pin-name {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  font-size: 14.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Collapsed rows carry a second muted line (category · note snippet) so the
   list informs without expanding; the full notes stay in .pin-detail. */
.pin-head-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.pin-head-meta .pin-name { flex: none; }
.pin-sub {
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pin-caret { flex: none; width: 16px; height: 16px; color: var(--muted); transition: transform var(--dur-1) ease; }
.pin-head[aria-expanded="true"] .pin-caret { transform: rotate(90deg); }
.pin-detail {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 0 6px 10px 28px;
}
.pin-detail[hidden] { display: none; }
.pin-detail .notes { font-size: 13px; color: var(--ink); overflow-wrap: anywhere; }
.pin-detail .pin-actions { flex-wrap: wrap; justify-content: flex-end; margin-top: 2px; }
.pin-detail .pin-goto { margin-right: auto; }
/* overflow-wrap inherits: a pasted URL in a name or note wraps instead of
   escaping the row (the panel's overflow-x clip would truncate it unread). */
.pin-meta { display: flex; flex-direction: column; flex: 1; min-width: 0; overflow-wrap: anywhere; }
.pin-meta strong { font-size: 14.5px; }
.pin-meta .muted { font-size: 12px; }
.pin-meta .detour-badge { display: inline-block; padding: 0 5px; margin-left: 2px; border: 1px solid var(--line); border-radius: 999px; font-size: 11px; white-space: nowrap; }
.pin-meta .notes { font-size: 13px; color: var(--ink); margin-top: 2px; }
.pin-actions { display: flex; align-items: center; gap: 6px; flex: none; }
.pin-actions a { text-decoration: none; font-size: 15px; }

/* ---- nav-app menu: the "▾" beside Navigate listing the non-preferred
   apps. A native <details>; ui.js adds light-dismiss. ---- */
.nav-more { position: relative; display: inline-flex; }
.nav-more summary { list-style: none; cursor: pointer; color: var(--brand); font-weight: 600; padding: 0 2px; }
.nav-more summary::-webkit-details-marker { display: none; }
.nav-more-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 128px;
  padding: 8px 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-2, var(--shadow-1));
}
.nav-more-menu a { text-decoration: none; font-size: 13px; white-space: nowrap; }
/* Row menus reuse the disclosure but hold buttons too (remove / reorder). */
.nav-more-menu .link {
  text-align: left;
  font-size: 13px;
  white-space: nowrap;
  padding: 2px 0;
}
.nav-more-menu .link:disabled { opacity: 0.4; }
.row-menu summary {
  color: var(--muted);
  font-size: 15px;
  letter-spacing: 0.05em;
  padding: 0 4px;
}
@media (hover: hover) {
  .row-menu summary:hover { color: var(--ink); }
}

/* Icon sizing for the SVG glyphs that replaced the old text glyphs
   (▾ ⋯ ⓘ ⚠ ✓ ↗ ← − +) — the shared icon set carries no width/height. */
.nav-more summary svg { width: 15px; height: 15px; display: block; }
.nights-ctl .link svg { width: 13px; height: 13px; display: block; }
.drive-warn svg,
.picked-note svg,
.ext-ic { width: 12px; height: 12px; display: inline-block; vertical-align: -2px; }
.back svg { width: 13px; height: 13px; display: inline-block; vertical-align: -2px; }

/* ---- long-press: map action menu, insert hint, press feedback ---- */
/* Pointer-gated hint copy: "Click …" on fine pointers, "Press and hold …"
   on touch. */
.if-coarse { display: none; }
@media (pointer: coarse) {
  .if-fine { display: none; }
  .if-coarse { display: inline; }

  /* Fingers need ≥40px targets (the today day-nav already enforces 44).
     Grow the hit areas, not the visuals. */
  .icon-btn { width: 40px; height: 40px; }
  .nav-more summary { padding: 10px 8px; margin: -10px -6px; }
  .nights-ctl .link { padding: 10px 8px; margin: -10px -4px; }
  .trip-card-act { padding: 8px 10px; top: 3px; right: 3px; }
}

/* No text-selection or iOS callout from a long-press on the maps. */
#map,
#trip-map {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* ---- confirm popover: the styled stand-in for window.confirm. ui.js builds
   it on demand, anchors it under the trigger on desktop, and the mobile rule
   below pins it above the bottom edge instead. ---- */
.confirm-backdrop { position: fixed; inset: 0; z-index: calc(var(--z-overlay) + 3); }
.confirm-pop {
  position: fixed;
  z-index: calc(var(--z-overlay) + 3);
  width: min(320px, calc(100vw - 24px));
  padding: 14px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  animation: confirm-in var(--dur-1) var(--ease-drop);
}
.confirm-pop p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.45; }
.confirm-pop .actions { display: flex; gap: 8px; justify-content: flex-end; }
.confirm-pop .actions button { font-size: 13px; padding: 7px 14px; }
@keyframes confirm-in {
  from { transform: translateY(4px) scale(0.97); opacity: 0; }
  to { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .confirm-pop { animation: none; }
}
@media (max-width: 760px) {
  .confirm-pop {
    left: 12px !important;
    right: 12px;
    top: auto !important;
    bottom: calc(12px + env(safe-area-inset-bottom));
    width: auto;
  }
}

/* Place preview card (map drop confirm) — still #map-menu for JS. */
.map-menu {
  position: fixed;
  z-index: var(--z-card);
  display: flex;
  flex-direction: column;
  min-width: 200px;
  max-width: min(280px, calc(100vw - 24px));
  padding: 12px;
  gap: 6px;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}
.map-menu[hidden] { display: none; }
.map-menu-title {
  font: 700 15px/1.25 var(--font-display);
  color: var(--ink);
  padding: 0 4px;
}
.map-menu-sub {
  margin: 0;
  padding: 0 4px 4px;
  font-size: 12.5px;
}
.map-menu-actions {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.map-menu-btn {
  border: none;
  background: none;
  text-align: left;
  padding: 9px 12px;
  font: inherit;
  font-size: 14.5px;
  color: var(--ink);
  border-radius: var(--radius-md);
  cursor: pointer;
}
@media (hover: hover) {
  .map-menu-btn:hover { background: var(--brand-tint); color: var(--ink); }
}
.map-menu-btn.map-menu-add-stop {
  background: var(--brand);
  color: #fff;
  font-weight: 600;
  text-align: center;
}
@media (hover: hover) {
  .map-menu-btn.map-menu-add-stop:hover {
    background: var(--brand-hover);
    color: #fff;
  }
}
.map-menu-btn.map-menu-dismiss { color: var(--muted); font-size: 13px; }

/* Temporary preview pin while the place card is open. */
.map-preview-pin svg { display: block; overflow: visible; }
.map-preview-pin path {
  fill: var(--brand);
  stroke: var(--marker-ring);
  stroke-width: 2;
  opacity: 0.85;
}

.insert-hint {
  position: fixed;
  top: calc(max(14px, env(safe-area-inset-top)) + 58px);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-card);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-1);
  font-size: 13.5px;
  white-space: nowrap;
}
.insert-hint[hidden] { display: none; }

/* ---- empty-map coach: first-pin CTA when the library is empty ---- */
.map-coach {
  position: fixed;
  top: calc(max(14px, env(safe-area-inset-top)) + 72px);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-card);
  width: min(360px, calc(100vw - 32px));
  padding: 14px 16px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  font-size: 14px;
  line-height: 1.45;
}
.map-coach[hidden] { display: none; }
.map-coach p { margin: 0 0 12px; }
.map-coach-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.map-coach-actions .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 8px 14px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--brand);
  color: #fff;
  font: inherit;
  font-weight: 600;
  font-size: 13.5px;
  text-decoration: none;
  cursor: pointer;
}
@media (hover: hover) {
  .map-coach-actions .btn:hover { background: var(--brand-hover); color: #fff; }
}
.map-coach-actions .btn.secondary {
  background: rgba(var(--ink-rgb), 0.06);
  color: var(--ink);
}
@media (hover: hover) {
  .map-coach-actions .btn.secondary:hover { background: rgba(var(--ink-rgb), 0.12); color: var(--ink); }
}
.map-coach-actions .link { margin-left: auto; }


/* ---- share toast: "Added <pin>" with one-tap Edit/Undo after a share ---- */
.share-toast {
  position: fixed;
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + var(--dock-clear) + 10px);
  left: 50%;
  transform: translateX(-50%);
  z-index: calc(var(--z-overlay) + 2);
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: min(92vw, 480px);
  padding: 10px 12px 10px 16px;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-1);
  font-size: 13.5px;
  animation: share-toast-in 0.25s ease-out;
}
.share-toast-text {
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  white-space: normal;
}
.share-toast-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}
.share-toast.leaving { opacity: 0; transition: opacity 0.25s ease-out; }
/* The offline notice reuses the share-toast shell; only the tone differs. */
.app-toast { color: var(--brand-deep); }
@keyframes share-toast-in {
  from { transform: translate(-50%, 10px); opacity: 0; }
  to { transform: translate(-50%, 0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .share-toast { animation: none; }
  .share-toast.leaving { transition: none; }
}

/* The growing ring under a touch press, removed when it fires or cancels. */
.press-ring {
  position: absolute;
  z-index: 800; /* above the Leaflet panes (they top out at 700) */
  width: 44px;
  height: 44px;
  margin: -22px 0 0 -22px;
  border: 3px solid var(--brand);
  border-radius: 50%;
  background: rgba(15, 118, 110, 0.18);
  box-shadow: 0 0 0 6px rgba(15, 118, 110, 0.12), 0 0 18px rgba(15, 118, 110, 0.35);
  pointer-events: none;
  /* 450ms matches map-shared long-press DURATION — ring stays readable to fire. */
  animation: press-ring 0.45s ease-out forwards;
}
@keyframes press-ring {
  from { transform: scale(0.35); opacity: 1; }
  70% { opacity: 0.95; }
  to { transform: scale(1.2); opacity: 0.75; }
}

/* ---- floating editor card ---- */
.editor-pop {
  position: fixed;
  z-index: var(--z-card);
  width: 304px;
  padding: 16px;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  /* Short viewports (landscape phones): the card must never push its
     Save/Cancel row off-screen. The inner form scrolls — not the card
     itself, whose ::before caret would be clipped by overflow. */
  display: flex;
  flex-direction: column;
  max-height: calc(100dvh - 88px);
}
.editor-pop .pin-editor {
  overflow-y: auto;
  min-height: 0;
  overscroll-behavior: contain;
}
.editor-pop[hidden] { display: none; }
/* The caret stem connecting the card to its map point. */
.editor-pop::before {
  content: "";
  position: absolute;
  top: var(--caret-y, 50%);
  width: 14px;
  height: 14px;
  background: var(--surface);
  transform: translateY(-50%) rotate(45deg);
}
.editor-pop[data-side="right"]::before { left: -7px; }
.editor-pop[data-side="left"]::before { right: -7px; }
.editor-pop[data-side="none"]::before { display: none; }
.editor-pop.opening { animation: pop-in var(--dur-2) var(--ease-drop); }
@keyframes pop-in {
  from { opacity: 0; transform: scale(0.92); }
  to { opacity: 1; transform: scale(1); }
}
.sheet-grab { display: none; }

.editor-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.pin-editor h2 { margin: 0; font-size: 17px; }
.pin-editor .error {
  margin: 10px 0 0;
  background: rgba(192, 59, 45, 0.08);
  padding: 8px 10px;
  border-radius: var(--radius-md);
}
.pin-editor .notice,
.pin-editor .awaiting-hint {
  margin: 10px 0 0;
  font-size: 13px;
  color: var(--brand-deep);
  background: var(--brand-tint);
  padding: 8px 10px;
  border-radius: var(--radius-md);
}
.pin-editor .awaiting-hint[hidden] { display: none; }

/* Selectable chips: category picker, collection checkboxes (view-only render
   disabled), and settings radio groups all share one hidden-input + chip look. */
.cat-picker, .col-picker { border: none; margin: 12px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.cat-picker legend, .col-picker legend {
  padding: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 6px;
}
.cat-opt, .col-opt, .radio-option { position: relative; margin: 0; }
.cat-opt input, .col-opt input, .radio-option input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 1px;
  height: 1px;
}
.cat-opt .chip, .col-opt .chip, .radio-option .chip { cursor: pointer; }
@media (hover: hover) {
  .cat-opt:hover .chip, .col-opt:hover .chip, .radio-option:hover .chip { background: rgba(var(--ink-rgb), 0.1); }
}
.cat-opt input:checked + .chip,
.col-opt input:checked + .chip,
.radio-option input:checked + .chip {
  background: var(--brand-tint);
  border-color: var(--brand);
  color: var(--brand-deep);
}
.col-opt input:disabled + .chip { cursor: default; opacity: 0.55; }
.cat-opt input:focus-visible + .chip,
.col-opt input:focus-visible + .chip,
.radio-option input:focus-visible + .chip { outline: 2px solid var(--brand); outline-offset: 2px; }
.trip-collections { margin: 12px 0 0; }

.radio-group { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 14px; }

.coords { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 12px; }
.coords input {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  background: rgba(var(--ink-rgb), 0.035);
  border-color: transparent;
  padding: 7px 9px;
}
.pin-editor .actions { display: flex; gap: 8px; margin-top: 14px; }
.pin-editor .actions button { flex: 1; }

/* ---- spotlight (geocode search overlay) ---- */
.spotlight { position: fixed; inset: 0; z-index: var(--z-overlay); }
.spotlight[hidden] { display: none; }
.spotlight-backdrop { position: absolute; inset: 0; background: var(--scrim); }
.spotlight-panel {
  position: relative;
  margin: min(18vh, 160px) auto 0;
  width: min(560px, calc(100vw - 32px));
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  overflow: hidden;
  animation: pop-in var(--dur-2) var(--ease-drop);
  /* Short viewports (landscape phones): shrink the results instead of
     pushing the panel's bottom off-screen. */
  display: flex;
  flex-direction: column;
  max-height: calc(100dvh - min(18vh, 160px) - 16px);
}
.spotlight-panel .geocode-results { flex: 0 1 auto; min-height: 0; }
.spotlight-row { display: flex; align-items: center; gap: 10px; padding: 4px 16px; }
.spotlight-row svg { width: 18px; height: 18px; color: var(--muted); flex: none; }
/* Desktop closes via Escape / backdrop; the × exists for the fullscreen
   mobile takeover, which has no visible backdrop to tap. */
.spotlight-close { display: none; }
.spotlight-row input { border: none; background: transparent; font-size: 17px; padding: 13px 0; border-radius: 0; }
.spotlight-row input:focus { outline: none; }
/* One clear affordance is enough — the panel has its own × on mobile and
   Escape elsewhere; webkit's native blue cancel blob fights both. */
.spotlight-row input::-webkit-search-cancel-button { -webkit-appearance: none; display: none; }
.geocode-results { max-height: 320px; overflow-y: auto; border-top: 1px solid var(--line); }
.geocode-results:empty { display: none; }
.geocode-msg { margin: 0; padding: 14px 16px; font-size: 14px; }
.geocode-list { list-style: none; margin: 0; padding: 6px; }
.spotlight-label {
  margin: 8px 16px 0;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
}
.geocode-result,
.spotlight-pin {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--ink);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  font-size: 14px;
  font-weight: 500;
}
.spotlight-pin { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.geocode-result.hl,
.spotlight-pin.hl { background: var(--brand-tint); color: var(--brand-deep); }
@media (hover: hover) {
  .geocode-result:hover,
  .spotlight-pin:hover { background: var(--brand-tint); color: var(--brand-deep); }
}
.spotlight-cats { display: flex; gap: 6px; padding: 0 16px 12px; }
.geocode-dist { display: block; font-size: 12px; font-weight: 500; color: var(--muted); }
.geocode-result.hl .geocode-dist { color: var(--brand-deep); }
@media (hover: hover) {
  .geocode-result:hover .geocode-dist { color: var(--brand-deep); }
}
/* Quick-add actions under a search result: stacked captioned rows, like
   the map popup menu — the caption is the only explanation touch gets. */
.geocode-actions { display: grid; gap: 2px; padding: 0 12px 10px; }
.geocode-actions .link { font-size: 13px; font-weight: 600; text-align: left; }
/* Armed add (map-page.js): the banner names what a pick will do, and the
   per-result quick-adds hide — their verbs would fight the armed role. */
.spotlight-armed {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 6px 0 0;
  padding: 7px 12px;
  border: 1px solid var(--brand);
  border-radius: var(--radius-md);
  background: var(--brand-tint);
  color: var(--brand-deep);
  font-size: 13px;
  font-weight: 600;
}
.spotlight-armed[hidden] { display: none; }
.spotlight-armed .spotlight-armed-cancel { margin-left: auto; flex: none; }
body.arming-place .geocode-actions { display: none; }
.geocode-actions .link small {
  display: block;
  font-weight: 400;
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.35;
}
.geocode-actions .picked-note { padding: 2px 0 4px; }
/* One-line legend above the corridor list (its row verbs explain themselves
   only in tooltips otherwise). */
.action-legend { margin: 2px 0 6px; font-size: 12px; line-height: 1.4; }

/* ---- temp pin ("the drop") ---- */
.temp-pin svg { display: block; overflow: visible; }
.temp-pin path { fill: var(--brand); stroke: var(--marker-ring); stroke-width: 2; }
.temp-pin circle { fill: var(--marker-ring); }
.temp-pin.drop svg { animation: temp-drop 220ms var(--ease-drop); }
@keyframes temp-drop {
  0% { transform: translateY(-16px); opacity: 0; }
  60% { transform: translateY(2px); opacity: 1; }
  100% { transform: translateY(0); }
}

/* Race-style START/FINISH endpoint drops (built inline in map-page.js). */
.sf-pin svg {
  display: block;
  overflow: visible;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.25));
}

/* ---- library pin markers: a colored disc with the category glyph inside a
   40px hit box (map-page.js pinIcon). The stylesheet stays the single source
   of the category palette. ---- */
.pin-marker { background: none; border: none; }
.pin-marker .pin-disc {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin: 7px; /* centers the disc in the 40px hit box */
  border-radius: 50%;
  background: var(--cat-other);
  border: 2px solid var(--marker-ring);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
  color: #fff;
  transition: transform var(--dur-1) ease;
}
.pin-marker .pin-disc svg { width: 14px; height: 14px; display: block; }
.pin-cat-sight .pin-disc { background: var(--cat-sight); }
.pin-cat-food .pin-disc { background: var(--cat-food); }
.pin-cat-sleep .pin-disc { background: var(--cat-sleep); }
/* Trip-mode base pool: smaller, lighter — the route keeps the foreground. */
.pin-marker--faint .pin-disc {
  width: 18px;
  height: 18px;
  margin: 11px;
  opacity: 0.65;
  border-width: 1.5px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}
.pin-marker--faint .pin-disc svg { width: 10px; height: 10px; }
/* Region-scale zoom shrinks the discs; street-scale zoom reveals the name
   labels (map-page.js toggles the container classes on zoomend). */
.pins-far .pin-marker .pin-disc { transform: scale(0.62); }
.leaflet-tooltip.pin-tip {
  display: none;
  background: rgba(var(--surface-rgb), 0.92);
  border: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  border-radius: 6px;
  padding: 1px 6px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
}
.leaflet-tooltip.pin-tip::before { display: none; }
.show-pin-labels .leaflet-tooltip.pin-tip { display: block; }

/* ---- teardrop markers: brand .night-pin = stay (N{k}), ink .wp-pin = S/E/·,
   category .pick-pin = plan pick (✓) ---- */
.night-pin svg,
.wp-pin svg,
.pick-pin svg { display: block; overflow: visible; }
.night-pin path { fill: var(--brand); stroke: var(--marker-ring); stroke-width: 2; }
/* Off-route stay: the muted "–" teardrop, pairing the drawer's grey badge. */
.night-pin.night-off path { fill: var(--muted); }
.wp-pin path { fill: var(--ink); stroke: var(--marker-ring); stroke-width: 2; }
/* Plan picks: the stay teardrop shape in the pick's category colour, so an
   added place is unmistakably "in the trip" next to the faint base dots. */
.pick-pin path { fill: var(--cat-other); stroke: var(--marker-ring); stroke-width: 2; }
.pick-pin.cat-sight path { fill: var(--cat-sight); }
.pick-pin.cat-food path { fill: var(--cat-food); }
.pick-pin.cat-sleep path { fill: var(--cat-sleep); }
.night-pin .night-num,
.wp-pin .night-num,
.pick-pin .night-num {
  position: absolute;
  top: 7px;
  left: 0;
  width: 30px;
  text-align: center;
  color: #fff;
  font: 700 13px/1 var(--font-display);
}
/* Two-character night glyphs (N1, N12) need a slightly smaller face. */
.night-pin .night-num { font-size: 11px; letter-spacing: -0.02em; top: 8px; }
/* Pin-linked stops carry their category as a filled dot inside the SVG head
   (inline fill from the JS palette) — never a ring, which reads as a
   selection highlight. */

/* ---- itinerary (the drawer's single chronological timeline) ---- */
.itinerary {
  padding-bottom: 10px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--line);
}
.itinerary h3 {
  margin: 0 0 4px;
  font: 700 11.5px/1 var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--brand-deep);
}

/* Day groups: soft glass cards with a disclosure header. The header is a
   tinted brand band (same voice as the dock's expanded state) so days read
   as clear section breaks between the white pin rows, not as more rows. */
/* No overflow:hidden here: the rows' ▾ nav menus must escape the section
   box. The head band rounds its own corners against the section radius. */
.itin-day {
  margin: 8px 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(var(--surface-rgb), 0.45);
}
.itin-head {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 9px 10px;
  background: var(--brand-tint);
  border: 0;
  border-radius: calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px) 0 0;
  text-align: left;
  cursor: pointer;
  /* NB: --font-display, not a --font-body — the latter doesn't exist, and an
     undefined var() voids the whole font shorthand at computed-value time. */
  font: 700 12.5px/1.3 var(--font-display);
  color: var(--brand-deep);
}
/* Seam between the band and the day's rows while it's open. */
.itin-head[aria-expanded="true"] { border-bottom: 1px solid var(--line); }
/* Collapsed: the band is the whole section — round the bottom corners too. */
.itin-head[aria-expanded="false"] { border-radius: calc(var(--radius-md) - 1px); }
.itin-head-static { cursor: default; border-bottom: 1px solid var(--line); }
.itin-head .pin-caret {
  width: 14px;
  height: 14px;
  flex: none;
  transition: transform var(--dur-1) ease;
  transform: rotate(90deg);
}
.itin-head[aria-expanded="false"] .pin-caret { transform: rotate(0deg); }
.itin-head .drive-warn {
  margin-left: auto;
  white-space: nowrap;
}
.itin-body { padding: 0 4px 4px; }
.itin-body .plan-row.en-route { margin-left: 12px; }

/* Start / Destination rows: a small flag dot echoing the map's endpoint
   markers (map-page.js startFinishIcon colors). */
.itin-flag {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--marker-ring);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08);
}
.itin-flag.start { background: #6abf69; }
.itin-flag.finish { background: #e57368; }

/* Endpoint and via rows carry a single action — keep it on the same line
   instead of the full-width second line the control-heavy stay rows need.
   (Triple selector outweighs the .route-stop-row .pin-actions default.) */
.pin-row.itin-endpoint .pin-actions,
.pin-row.itin-via .pin-actions {
  flex-basis: auto;
  margin-left: auto;
}
/* ...unless a stay parked on the endpoint brings the full control set. */
.pin-row.itin-endpoint .pin-actions:has(.nights-ctl) {
  flex-basis: 100%;
  margin-left: 0;
}

.route-stop-row { cursor: pointer; flex-wrap: wrap; }
@media (hover: hover) {
  .route-stop-row:hover { background: rgba(var(--surface-rgb), 0.65); }
}
.route-stop-row .pin-actions {
  flex-basis: 100%;
  justify-content: flex-end;
}
.role-badge {
  flex: none;
  min-width: 22px;
  height: 22px;
  padding: 0 7px;
  border-radius: 999px;
  font: 700 10.5px/22px var(--font-display);
  letter-spacing: 0.02em;
  text-align: center;
  white-space: nowrap;
}
.role-start,
.role-dest {
  background: rgba(var(--ink-rgb), 0.12);
  color: var(--ink);
}
.role-stop {
  background: transparent;
  color: var(--brand-deep);
  box-shadow: inset 0 0 0 1.5px var(--brand);
}
.role-stay {
  background: var(--brand);
  color: #fff;
}
.chip-pin {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.02em;
}
.route-stop-flash {
  animation: route-stop-flash 1.5s ease-out;
}
@keyframes route-stop-flash {
  0% { background: var(--brand-tint); }
  100% { background: transparent; }
}
.popup-role { font-size: 12.5px; margin: 2px 0 6px; }

/* ---- plan rows (stays and picks inside the itinerary) ---- */
.plan-row { cursor: pointer; flex-wrap: wrap; }
@media (hover: hover) {
  .plan-row:hover { background: rgba(var(--surface-rgb), 0.65); }
}
/* Night and corridor rows carry many controls (stepper, reorder, nav links,
   picks); they get a full-width second line instead of crushing the name
   column. (Pins-list rows use the compact disclosure layout above instead.) */
.survivor-list .pin-row { flex-wrap: wrap; padding: 8px 6px; }
.plan-row .pin-actions,
.survivor-list .pin-actions {
  flex-basis: 100%;
  justify-content: flex-end;
  margin-top: 0;
  font-size: 12px;
}
/* The three pick verbs read as tappable mini-chips on one compact line. */
.survivor-list .pin-actions .link {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 10px;
}
.plan-row .pin-actions a,
.survivor-list .pin-actions a { font-size: 12px; }
.plan-row .pin-actions a[title^="Open in Google Maps"],
.survivor-list .pin-actions a[title^="Open in Google Maps"] { font-size: 14px; }
/* Itinerary rows fit on ONE line since the ⋯ menus slimmed the action set
   (stepper · Navigate ▾ · ⋯ at most): badge, name, inline muted meta that
   truncates first, actions pinned right. Survivor rows keep the two-line
   layout above — their three pick chips don't share a line with a name. */
.itinerary .pin-row {
  flex-wrap: nowrap;
  align-items: center;
  padding: 5px 6px;
}
.itinerary .pin-row .dot { margin-top: 0; }
.itinerary .pin-meta {
  flex-direction: row;
  align-items: baseline;
  gap: 6px;
  overflow: hidden;
}
.itinerary .pin-meta strong {
  flex: none;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.itinerary .pin-meta .muted {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The one-line rows have no room for decorative meta: the dot / role badge
   already carries the category, so non-stay rows drop the muted text
   entirely rather than showing a clipped fragment. Stay rows keep it — the
   night dates are real information — on its own line under the name, so
   neither the name nor the date range truncates for the other. */
.itinerary .pin-meta .muted { display: none; }
.itinerary .pin-row:has(.night-badge) .pin-meta .muted,
.itinerary .pin-row[data-stay="1"] .pin-meta .muted {
  display: block;
  max-width: 100%;
}
.itinerary .pin-row:has(.night-badge) .pin-meta,
.itinerary .pin-row[data-stay="1"] .pin-meta {
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
}
.itinerary .pin-row .pin-actions,
.itinerary .pin-row .pin-actions:has(.nights-ctl) {
  flex: none;
  flex-basis: auto;
  margin-left: auto;
  margin-top: 0;
}
/* The ▾ menu leads with the preferred app, so the standalone Navigate link
   is redundant width on these one-line rows (both panel widths are tight). */
.itinerary .pin-actions > a[title^="Navigate here"] { display: none; }
.night-badge {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  font: 700 12px/1 var(--font-display);
  display: flex;
  align-items: center;
  justify-content: center;
}
.night-badge.off-route { background: var(--muted); }
.picked-note { flex: none; font-size: 12px; font-weight: 600; color: var(--brand-deep); }
.trip-dates { display: flex; align-items: center; gap: 10px; margin: 10px 0 4px; }
.trip-dates label { font-size: 13px; font-weight: 600; color: var(--muted); margin: 0; }
.trip-dates input[type="date"] {
  flex: 1;
  padding: 6px 10px;
  font-size: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
}
.trip-export { display: flex; align-items: baseline; gap: 8px; margin-top: 10px; font-size: 13px; }
.trip-export a { font-weight: 700; }
.trip-export .muted { font-size: 12px; }
.plan-total { font: 500 11.5px/1 var(--font-body); text-transform: none; letter-spacing: 0; color: var(--muted); }
.nights-ctl { display: inline-flex; align-items: center; gap: 4px; }
.nights-ctl .link { padding: 0 4px; font-size: 15px; }
.nights-ctl .link:disabled { opacity: 0.3; pointer-events: none; }
.nights-count { font-size: 12px; color: var(--muted); white-space: nowrap; }
/* Insert affordances: a thin connector line through the gap with a small
   round "+" (arms the position) and its search twin (arms + spotlight)
   riding on it — the old full-width dashed rows cost ~55px per gap. */
.insert-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px dashed var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--muted);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
}
@media (hover: hover) {
  .insert-slot:hover { border-color: var(--brand); color: var(--brand-deep); }
}
.insert-slot.armed {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-deep);
}
/* A slot and its search twin share the gap row; the twin opens the spotlight. */
.insert-slot-wrap {
  position: relative;
  display: flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  padding: 2px 0;
}
.insert-slot-wrap::before {
  content: "";
  position: absolute;
  left: 8px;
  right: 8px;
  top: 50%;
  border-top: 1px dashed var(--line);
}
.insert-slot-wrap > * { position: relative; }
/* Desktop armed hint (mobile closes the drawer and floats its own hint). */
.insert-slot-wrap:has(.insert-slot.armed)::after {
  content: "Tap the map, a pin, or a search result to insert here";
  flex-basis: 100%;
  text-align: center;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--brand-deep);
  padding-bottom: 2px;
}
.insert-slot-search {
  flex: none;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px dashed var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
}
.insert-slot-search svg { width: 13px; height: 13px; }
@media (hover: hover) {
  .insert-slot-search:hover { border-color: var(--brand); color: var(--brand-deep); }
}
/* Standalone endpoint slots ("+ start" / "+ destination"): compact pills. */
.insert-slot.insert-endpoint {
  width: auto;
  border-radius: 999px;
  padding: 3px 12px;
  font-size: 12px;
  margin: 2px 0;
}
/* Empty-state / one-point "Set start" & "Set destination" actions. */
.itin-empty-actions { display: grid; gap: 6px; margin: 10px 0 2px; }
.itin-add-endpoint {
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--brand-deep);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
}
@media (hover: hover) {
  .itin-add-endpoint:hover { border-color: var(--brand); background: var(--brand-tint); }
}
.itin-add-endpoint.secondary { color: var(--muted); font-weight: 500; }
.itin-add-endpoint.armed {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-deep);
}
.night-up:disabled, .night-down:disabled { opacity: 0.3; pointer-events: none; }
.drive-warn { font-weight: 700; color: var(--warn-strong); }
.plan-subhead { padding: 8px 10px 0; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }

/* ---- locate me (one-shot geolocation) ---- */
.locate-btn {
  position: fixed;
  right: 12px;
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + 96px);
  z-index: var(--z-controls);
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--line);
  border-radius: 50%;
  box-shadow: var(--shadow-1);
  color: var(--ink);
}
.locate-btn svg { width: 20px; height: 20px; }
@media (hover: hover) {
  .locate-btn:hover { color: var(--brand-deep); }
}
.locate-btn[aria-pressed="true"] { color: #4285f4; }
.locate-btn.locating svg { animation: locate-pulse 1s ease-in-out infinite; }
.locate-btn:disabled { opacity: 0.4; }
/* The trip page's right panel claims the bottom-right corner: shove the
   locate button and Leaflet's zoom/attribution out from under it. */
@media (min-width: 761px) {
  body:has(.drawer-trip.open) .locate-btn {
    right: calc(max(14px, env(safe-area-inset-right)) + var(--panel-shove));
  }
  body:has(.drawer-trip.open) .leaflet-bottom.leaflet-right { right: calc(var(--panel-shove) + 2px); }
}
@keyframes locate-pulse {
  50% { opacity: 0.35; }
}

/* ---- Leaflet chrome ---- */
.leaflet-control-attribution {
  background: var(--surface-glass) !important;
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border-radius: 8px 0 0 0;
  color: var(--muted);
  font-size: 10px;
  padding: 2px 8px;
}
.leaflet-control-zoom { border: none !important; border-radius: var(--radius-md) !important; overflow: hidden; box-shadow: var(--shadow-1) !important; }
.leaflet-control-zoom a {
  background: var(--surface-glass) !important;
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  color: var(--ink) !important;
  border-bottom: 1px solid var(--line) !important;
}
.leaflet-control-zoom a:last-child { border-bottom: none !important; }
/* CARTO's dark_all basemap ships very murky — lift the tiles so water, land
   and roads separate without abandoning the dark look. On the pane, not the
   tiles: one composited filter, and the JS-drawn overlays (pins, route)
   keep their exact token colors. */
@media (prefers-color-scheme: dark) {
  .leaflet-tile-pane { filter: brightness(1.5) contrast(0.93) saturate(1.1); }
}
/* Leaflet's own CSS hardcodes white here — and loads after this sheet (the
   map pages pull it in via extraHead), so these need !important to win. */
.leaflet-popup-content-wrapper, .leaflet-popup-tip { background: var(--surface) !important; color: var(--ink) !important; }
.leaflet-popup-content-wrapper { border-radius: var(--radius-md); box-shadow: var(--shadow-2); }
.leaflet-container a.leaflet-popup-close-button { color: var(--muted) !important; }
/* Leaflet clamps popup width; without wrapping, an unbroken URL in a pin's
   notes paints past the card onto the map tiles. */
.leaflet-popup-content { font-family: inherit; font-size: 13.5px; line-height: 1.5; margin: 12px 14px; overflow-wrap: anywhere; }
.popup-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 6px; align-items: center; }
.popup-actions a { text-decoration: none; font-weight: 600; font-size: 13px; }
/* Trip-mode pick popup: stacked captioned actions — the verbs alone are
   easy to mix up, so each row says what it does to the route. */
.popup-menu { display: grid; margin-top: 6px; min-width: 210px; }
.popup-row {
  display: block;
  width: 100%;
  text-align: left;
  padding: 6px 0;
  font-weight: 600;
  font-size: 13px;
}
.popup-row + .popup-row { border-top: 1px solid var(--line); }
.popup-row small {
  display: block;
  font-weight: 400;
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.35;
}
/* The ▾ menu opens upward inside popups: the map container clips overflow,
   and popups anchor above their marker so upward always has room. */
.leaflet-popup .nav-more-menu { top: auto; bottom: calc(100% + 6px); }


/* Drive-time budget tick on the route line ("4 h"). Centered on its point;
   pointer-events off so the route's insert-stop click passes through. */
.drive-tick {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  color: var(--brand-deep);
  font: 600 10px/1 var(--font-display);
  padding: 3px 7px;
  white-space: nowrap;
  width: max-content;
  transform: translate(-50%, -50%);
  pointer-events: none;
  box-shadow: var(--shadow-1);
}

/* ---- feed ---- */
.feed-row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 4px; border-bottom: 1px solid var(--line); }
.feed-icon { font-size: 18px; width: 22px; text-align: center; flex: none; }
.feed-body { flex: 1; min-width: 0; }
.feed-title { font-size: 15px; }
.feed-detail { color: var(--muted); }
.feed-when { color: var(--muted); font-size: 12px; flex: none; white-space: nowrap; }
.feed-unplaced { margin-top: 6px; display: flex; align-items: center; gap: 12px; }
.btn-small {
  display: inline-block;
  background: var(--brand);
  color: #fff;
  text-decoration: none;
  padding: 4px 10px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
}
@media (hover: hover) {
  .btn-small:hover { background: var(--brand-hover); }
}
.small { font-size: 12px; text-decoration: none; }

/* ---- settings ---- */
.settings h1 { margin: 8px 0 4px; }
.card-block {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  padding: 18px;
  margin: 16px 0;
}
.card-block h2 { margin: 0 0 8px; font-size: 16px; }
.card-block form { margin-top: 12px; }
.token-box { background: var(--warn-bg); border: 1px solid var(--warn-line); border-radius: var(--radius-md); padding: 12px; margin: 10px 0; }
.token-box .warn { margin: 0 0 8px; color: var(--warn-ink); font-size: 13px; font-weight: 600; }
.token {
  display: block;
  word-break: break-all;
  font-family: var(--font-mono);
  font-size: 13px;
  background: var(--surface);
  padding: 8px;
  border-radius: 8px;
  border: 1px solid var(--line);
}
.steps { line-height: 1.7; padding-left: 20px; }
.steps code { background: rgba(var(--ink-rgb), 0.06); padding: 1px 5px; border-radius: 4px; font-size: 13px; }

/* ---- collections (grouping the pin list, managed inside the pins drawer) ---- */
/* A quiet one-line disclosure at the list's end; opening reveals the form. */
.new-collection { display: block; margin: 2px 0; flex: none; }
.new-collection summary {
  list-style: none;
  cursor: pointer;
  padding: 8px 6px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--brand);
  border-radius: var(--radius-md);
}
.new-collection summary::-webkit-details-marker { display: none; }
@media (hover: hover) {
  .new-collection summary:hover { background: rgba(var(--surface-rgb), 0.65); }
}
.new-collection-form { display: flex; gap: 8px; margin: 2px 0 6px; }
.new-collection-form input { flex: 1; background: var(--surface); }
.new-collection-form button { flex: none; }
/* Each collection is a disclosure group in the pin list: a head row (caret +
   name + count + badges + Edit) over that collection's pin rows. */
.pin-group { border-bottom: 1px solid var(--line); }
.pin-group:last-of-type { border-bottom: none; }
/* Filtering: groups with no surviving row disappear; the rest are forced open
   (display overrides [hidden]) without touching the user's manual collapse
   state underneath. */
.pin-list .pin-group.no-match { display: none; }
.pin-list.filtering .pin-group:not(.no-match) .col-pins[hidden] { display: block; }
.pin-list.filtering .pin-group:not(.no-match) .col-head .pin-caret { transform: rotate(90deg); }
.col-head-row { display: flex; align-items: center; gap: 10px; padding: 12px 4px; }
.col-head {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  margin-right: auto;
  padding: 0;
  background: transparent;
  border: none;
  color: var(--ink);
  font: inherit;
  text-align: left;
}
@media (hover: hover) {
  .col-head:hover { background: transparent; color: var(--brand-deep); }
}
.col-head .pin-caret { width: 14px; height: 14px; }
.col-head[aria-expanded="true"] .pin-caret { transform: rotate(90deg); }
.col-name {
  min-width: 0;
  font-weight: 600;
  font-size: 16px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.col-count { flex: none; font-size: 13px; }
.col-pins { padding: 0 4px 12px 18px; }
.col-pins[hidden] { display: none; }
.col-pins .muted { margin: 0 0 8px; font-size: 13px; }
#collections-panel .back { font-size: 13px; color: var(--muted); padding: 0; }
#collections-panel .panel-title {
  margin: 6px 0 2px;
  font-size: 18px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
#collections-panel .card-block {
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 14px 0 4px;
  margin: 10px 0 0;
  border-top: 1px solid var(--line);
}
.badge {
  flex: none;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(var(--ink-rgb), 0.08);
  color: var(--ink);
}
.badge.shared { background: var(--brand); color: #fff; }
.badge.viewer { background: rgba(var(--ink-rgb), 0.05); color: var(--muted); }
.rename { display: flex; gap: 8px; }
.rename input { flex: 1; background: var(--surface); }
.invite-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.invite-row:last-child { border-bottom: none; }
.invite-row strong { flex: none; min-width: 90px; }
.invite-row .token-box { flex-basis: 100%; }
.invite-actions { margin-left: auto; display: flex; gap: 12px; }
.member-list { list-style: none; padding: 0; margin: 0; }
.member-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.member-row:last-child { border-bottom: none; }
.member-email { margin-right: auto; word-break: break-all; }
.member-row select { padding: 4px 8px; border-radius: 8px; border: 1px solid var(--line); background: var(--surface); }
button.secondary.danger { color: var(--danger); border-color: var(--danger); background: transparent; }

/* ---- trip sharing dialog (modal, opened from the trip drawer head) ---- */
.drawer-actions { display: flex; align-items: center; gap: 4px; }
.drawer-actions .icon-btn { position: relative; }
.icon-badge {
  position: absolute;
  top: -2px;
  right: -2px;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--brand);
  color: #fff;
  font: 700 10px/15px var(--font-display);
  text-align: center;
}
/* One above the spotlight: both are full-screen overlays, and share must
   stay on top if the two ever coexist. */
.share-pop { position: fixed; inset: 0; z-index: calc(var(--z-overlay) + 1); }
.share-pop[hidden] { display: none; }
.share-backdrop { position: absolute; inset: 0; background: var(--scrim); }
.share-panel {
  position: relative;
  margin: 12vh auto 0;
  width: min(440px, calc(100vw - 32px));
  max-height: 72vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2, var(--shadow-1));
  padding: 16px 18px;
}
.share-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.share-head h2 { margin: 0; font-size: 16px; }
#trip-sharing .hint { font-size: 12px; color: var(--muted); margin: 8px 0; }

/* ---- trip mode: the today view inside the trip drawer ---- */
/* The trip drawer swaps between its route controls and the today panel via
   data-view, same mechanism as the pins drawer's list/manage flip. */
.drawer-trip[data-view="route"] .today-view { display: none; }
.drawer-trip[data-view="today"] .route-view { display: none; }
/* Horizontal swipe is claimed by day nav; vertical scroll stays free. */
#today-panel.today-view { touch-action: pan-y; }
.today-eyebrow {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--brand-deep);
}
.today-daynav {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 8px;
}
.today-daynav-label {
  flex: 1;
  text-align: center;
  min-width: 0;
}
.today-daynav .today-eyebrow {
  margin: 0;
}
.today-daynav .icon-btn {
  width: 44px;
  height: 44px;
}
.today-daynav .icon-btn:disabled {
  opacity: 0.35;
  cursor: default;
}
.today-daynav .icon-btn:disabled:hover {
  background: transparent;
  color: var(--muted);
}
.today-daynav .today-prev svg {
  transform: scaleX(-1);
}
.today-jump {
  display: inline-block;
  margin-top: 4px;
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-deep);
}
.today-hero h2 { margin: 2px 0 4px; font-size: 20px; }
.today .card-block { padding: 14px 16px; margin: 12px 0; }
.today-hero .muted { margin: 0 0 4px; }
.today-label {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.today-drive { margin: 6px 0 0; color: var(--muted); font-size: 14px; }
.today-drive .drive-warn { margin-left: 6px; }
.today-subhead { margin: 20px 0 8px; font-size: 15px; }
.today-stop {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  margin: 10px 0;
}
.today-stop .pin-meta { flex-direction: row; align-items: center; gap: 8px; }
/* Big tap targets: the primary Navigate link becomes a full button. */
.today-nav { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.today-stop .today-nav { margin-top: 0; flex: none; }
.today-nav > a:first-child {
  display: block;
  background: var(--brand);
  color: #fff;
  padding: 13px 22px;
  border-radius: var(--radius-md);
  font-weight: 700;
  font-size: 15px;
  text-align: center;
  text-decoration: none;
}
.today-hero .today-nav > a:first-child { flex: 1; }
@media (hover: hover) {
  .today-nav > a:first-child:hover { background: var(--brand-hover); }
}
/* The alt-apps caret is the split button's second half: a proper ≥44px tap
   target beside the full-width Navigate, not a bare glyph. */
.today-nav .nav-more summary {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  font-size: 16px;
  border-radius: var(--radius-md);
  background: var(--brand-tint);
  color: var(--brand-deep);
}
/* Alt-apps menu opens upward: #today-panel lives in a clipped drawer-scroll,
   and the Navigate row is near the bottom of the hero (often the last content
   before "Open plan"), so a downward menu gets cut off. Same trick as
   .leaflet-popup .nav-more-menu. */
.today-nav .nav-more-menu {
  top: auto;
  bottom: calc(100% + 6px);
}
.today-open { text-decoration: none; font-weight: 600; font-size: 13px; align-self: center; }
.today-tomorrow { padding: 14px 18px; margin: 10px 0; }
.today-tomorrow strong { font-size: 15px; }
/* Whole tomorrow card is a day-advance control — reset button chrome. */
button.today-tomorrow-nav {
  display: block;
  width: 100%;
  border: 1px solid var(--line);
  background: var(--surface);
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
  border-radius: var(--radius-lg);
}
@media (hover: hover) {
  button.today-tomorrow-nav:hover {
    border-color: var(--brand);
    background: var(--brand-tint);
  }
}
button.today-tomorrow-nav:active {
  background: var(--brand-tint);
}
.today .plan-open { width: 100%; margin-top: 16px; }
/* Directional enter animation after day swaps (dir via --today-dx). */
.today.today-anim-in { animation: today-in 160ms ease-out; }
@keyframes today-in {
  from { opacity: 0; transform: translateX(var(--today-dx, 8px)); }
  to   { opacity: 1; transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .today.today-anim-in { animation: none; }
}
/* Tonight's stay on the map while trip mode is showing. */
.today-halo { animation: halo-pulse 1.6s ease-in-out infinite; }
/* The transient ring where a drawer row / search hit just flew to
   (map-page.js flashGoto); removed by timer or the next map touch. */
.goto-halo { animation: halo-pulse 1.6s ease-in-out infinite; }
@keyframes halo-pulse {
  0%, 100% { stroke-opacity: 0.9; }
  50% { stroke-opacity: 0.25; }
}

/* ---- trips picker (card sheet) ---- */
.new-trip { display: flex; gap: 8px; margin: 12px 0 14px; }
.new-trip input { flex: 1; background: var(--surface); }
.new-trip button { flex: none; }
.trip-cards {
  list-style: none;
  padding: 0 0 2px;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.trip-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-1) ease, box-shadow var(--dur-1) ease;
}
@media (hover: hover) {
  .trip-card:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); }
}
.trip-card.current { border-color: var(--brand); }
.trip-card-link {
  display: block;
  padding: 12px 14px 10px;
  text-decoration: none;
  color: inherit;
  border-radius: inherit;
}
.trip-card-eyebrow {
  display: block;
  margin-bottom: 2px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--brand-deep);
}
.trip-card-name {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 52px; /* room for the Delete/Leave corner action */
  font-weight: 600;
  font-size: 15px;
}
.trip-card-meta { display: block; margin-top: 2px; font-size: 12.5px; color: var(--muted); }
.trip-card-act { position: absolute; top: 11px; right: 13px; font-size: 12.5px; }
/* The miniature route ribbon: the trip's own object, drawn in the map's
   vocabulary — start/destination dots with the stops between them. */
.trip-ribbon { display: block; margin-top: 9px; }
.trip-ribbon path { stroke: var(--brand); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; fill: none; }
.trip-ribbon circle { fill: var(--surface); stroke: var(--brand); stroke-width: 1.6; }
.trip-ribbon .rb-end { fill: var(--brand); stroke: none; }
.trip-ribbon.empty path { stroke: var(--muted); opacity: 0.55; }

/* ---- trip route builder (inside the drawer) ---- */
#route-form { margin-bottom: 12px; flex: none; }
/* Category chips above the corridor list: filter + legend in one (they proxy
   the pins panel's #pin-controls chips, which own the state). */
/* The list-controls row: chips + sorts scroll horizontally in one strip;
   the ⓘ verb legend sits outside the scroller so its popover isn't clipped. */
.route-controls { display: flex; align-items: center; gap: 2px; margin: 8px 0 2px; }
.route-chips {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 2px 0;
}
.route-chips::-webkit-scrollbar { display: none; }
.route-chips .pin-chips { display: flex; flex-wrap: nowrap; gap: 6px; flex: none; }
.route-chips .chip { white-space: nowrap; flex: none; }
/* width:auto beats the global full-width select rule — these size to their
   label ("≤ 15 min"), not to the strip. */
.route-chips .route-sort { flex: none; width: auto; }
.info-pop summary { color: var(--muted); font-size: 14px; padding: 2px 6px; }
.info-pop .nav-more-menu { min-width: 240px; white-space: normal; }
.info-pop .nav-more-menu p { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--muted); }
.info-pop .nav-more-menu strong { color: var(--ink); }

/* Collapsible planning-tools cluster below the itinerary: the itinerary is
   what returning visits are for; the corridor/route form only leads while the
   trip has no route yet (server renders it open when < 2 waypoints). */
.route-settings { margin: 4px 0 12px; padding-top: 10px; border-top: 1px solid var(--line); }
.route-settings summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
}
@media (hover: hover) {
  .route-settings summary:hover { color: var(--ink); }
}
.route-settings[open] summary { margin-bottom: 8px; }
#route-form input[type="range"] { width: 100%; padding: 0; border: none; background: transparent; accent-color: var(--brand); }
#route-form .actions { display: flex; gap: 8px; margin-top: 12px; }
#route-form .actions button { flex: 1; }
.route-summary {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: center;
  padding: 10px 0;
  font-weight: 600;
  color: var(--brand-deep);
  border-bottom: 1px solid var(--line);
  margin-bottom: 8px;
}
.route-summary-controls {
  margin-left: auto;
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: 60%;
}
.route-summary .route-sort {
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  color: inherit;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 2px 6px;
}

/* ---- mobile (≤760px — see :root breakpoint contract / window.triploMqMobile) ---- */
@media (max-width: 760px) {
  /* Non-map pages: the pill drops to the bottom where thumbs live. */
  body:not(.page-map) .float-nav {
    position: fixed;
    top: auto;
    bottom: max(12px, env(safe-area-inset-bottom));
    left: 50%;
    transform: translateX(-50%);
    margin: 0;
    padding: 6px;
  }
  /* Map pages: the top edge reads [bell] [search bar] [settings], leaving
     the whole bottom strip to the dock and the sheet. The whole row is one
     notch smaller than desktop — phone chrome should sit light on the map. */
  .page-map .float-nav {
    top: max(12px, env(safe-area-inset-top));
    left: max(60px, calc(env(safe-area-inset-left) + 60px));
    right: max(62px, calc(env(safe-area-inset-right) + 54px));
    transform: none;
    padding: 4px;
  }
  .page-map .nav-searchbar {
    width: 100%;
    padding: 8px 11px;
    font-size: 13px;
  }
  .settings-corner {
    top: max(12px, env(safe-area-inset-top));
    width: 40px;
    height: 40px;
  }
  .float-nav .wordmark { display: none; }
  .pill-link { flex-direction: column; gap: 3px; padding: 7px 12px; }
  .pill-ic { display: block; }
  .pill-link .pill-label { font-size: 10px; }
  .nav-searchbar kbd { display: none; }
  /* The wordmark and kbd are hidden here, so the pill can give the search
     hint the room the desktop reserves for them — no more ellipsis. */
  .nav-searchbar { width: min(340px, calc(100vw - 48px)); }

  .content { padding-bottom: 110px; }

  /* Import review footer: solid pad from CTA down past the bottom nav pill. */
  .import-actions {
    /* Sit the button above the pill; padding-bottom paints solid over the rest. */
    bottom: 0;
    margin-left: -18px;
    margin-right: -18px;
    padding-bottom: calc(max(12px, env(safe-area-inset-bottom)) + 78px);
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  }

  .drawer {
    width: min(85vw, 340px);
    max-height: calc(100dvh - 160px);
  }

  /* The dock owns the bottom edge (the nav pill moved up top); the sheet
     and the other floating panels grow from just above it. Same one-notch
     shrink as the top row. */
  .dock {
    bottom: max(12px, env(safe-area-inset-bottom));
    padding: 4px;
  }
  .dock-btn {
    height: 44px;
    padding: 0 11px;
    font-size: 13px;
    touch-action: none;
  }
  .dock-btn svg { width: 15px; height: 15px; }
  /* 44px buttons + 4px pad×2 + gap above panels that stack on the dock. */
  :root { --dock-clear: 64px; }
  .locate-btn {
    bottom: calc(max(12px, env(safe-area-inset-bottom)) + var(--dock-clear) + 10px);
    /* Above low sheets so it stays tappable; dock stays clear underneath. */
    z-index: calc(var(--z-card) + 1);
  }
  /* The notifications bell leaves the bottom-left (no room beside the dock
     pill) and becomes a corner circle left of the search bar. */
  .dock.dock-left {
    top: max(12px, env(safe-area-inset-top));
    bottom: auto;
    left: max(12px, env(safe-area-inset-left));
    z-index: var(--z-nav);
    width: 40px;
    height: 40px;
    padding: 0;
    display: grid;
    place-items: center;
  }
  .dock.dock-left .dock-count {
    position: absolute;
    top: -3px;
    right: -3px;
  }
  /* Both dock drawers become one floating bottom sheet: JS keeps at most one
     open at a time, and data-snap names its resting height. The sheet still
     floats above the dock row rather than fusing to the screen edge. */
  /* The sheet snap tokens, shared by the dock drawers and the sheet-styled
     flyouts below. ui.js mirrors them in SHEET_TOKENS — change both. Full
     stops below the top search pill, like the desktop panels starting below
     the chrome line. */
  :root {
    --sheet-bottom: calc(max(12px, env(safe-area-inset-bottom)) + var(--dock-clear));
    --snap-peek: 112px;
    --snap-half: 48dvh;
    --top-reserve: max(60px, env(safe-area-inset-top) + 54px);
    --snap-full: calc(100dvh - var(--sheet-bottom) - var(--top-reserve));
  }
  .drawer.drawer-dock {
    left: 12px;
    width: calc(100vw - 24px);
    bottom: var(--sheet-bottom);
    height: var(--sheet-h, var(--snap-half));
    max-height: var(--snap-full);
    overflow: hidden; /* peek clips the body panels */
    transition: transform var(--dur-2) ease, opacity var(--dur-2) ease,
      height var(--dur-2) ease, visibility 0s;
  }
  .drawer.drawer-dock:not(.open) {
    transition: transform var(--dur-2) ease, opacity var(--dur-2) ease, visibility 0s var(--dur-2);
  }
  .drawer.drawer-dock[data-snap="peek"] { --sheet-h: var(--snap-peek); }
  .drawer.drawer-dock[data-snap="half"] { --sheet-h: var(--snap-half); }
  .drawer.drawer-dock[data-snap="full"] { --sheet-h: var(--snap-full); }
  /* Mid-drag the finger owns the height — no tween fighting it. */
  .drawer.drawer-dock.dragging { transition: opacity var(--dur-2) ease; }
  /* The sheet's overflow:hidden clips downward menus — flip them up, like
     .leaflet-popup / .today-nav already do. */
  .drawer.drawer-dock .nav-more-menu {
    top: auto;
    bottom: calc(100% + 6px);
  }
  /* Except the ⓘ legend: it sits at the TOP of the survivor list, where up
     means clipping against the scroll area — down over the rows stays
     fully visible. */
  .drawer.drawer-dock .info-pop .nav-more-menu {
    top: calc(100% + 6px);
    bottom: auto;
  }

  /* The grab bar keeps its 44x4 look, but the touch target is the sheet's
     whole top strip — full width, 32px tall, bleeding over the drawer's own
     padding so it costs no extra layout height. */
  .drawer.drawer-dock .sheet-grab {
    display: grid;
    place-items: center;
    flex: none;
    align-self: stretch;
    width: auto; /* out-specificity the generic 44px .sheet-grab below */
    height: 32px;
    margin: -12px -14px -6px;
    background: none;
    touch-action: none;
    cursor: grab;
  }
  .drawer.drawer-dock .sheet-grab::after {
    content: "";
    width: 44px;
    height: 4px;
    border-radius: 2px;
    background: var(--brand);
    opacity: 0.45;
  }
  .drawer.drawer-dock .drawer-head { touch-action: none; }
  .flyout {
    width: min(85vw, 340px);
    bottom: var(--sheet-bottom);
    max-height: calc(100dvh - 180px);
  }
  /* The notifications panel drops down from the bell's corner instead of
     growing up from a bottom pill. */
  .flyout.flyout-left {
    left: 12px;
    right: auto;
    top: calc(max(14px, env(safe-area-inset-top)) + 56px);
    bottom: auto;
    max-height: min(60dvh, calc(100dvh - 140px));
    transform-origin: 0 0;
  }

  /* The trips picker and settings match the pins/trip sheet: floating above
     the dock, rounded all around, full width, same grab strip, same drag
     snaps (ui.js). They open at natural content height — data-snap takes
     over once the user drags. */
  .flyout.trips-side,
  .flyout.settings-side {
    left: 12px;
    right: auto;
    bottom: var(--sheet-bottom);
    width: calc(100vw - 24px);
    max-width: none;
    height: var(--sheet-h, auto);
    max-height: var(--snap-full);
    overflow: hidden; /* peek clips to the grab + head */
    z-index: var(--z-card);
  }
  .flyout.trips-side[data-snap="peek"],
  .flyout.settings-side[data-snap="peek"] { --sheet-h: var(--snap-peek); }
  .flyout.trips-side[data-snap="half"],
  .flyout.settings-side[data-snap="half"] { --sheet-h: var(--snap-half); }
  .flyout.trips-side[data-snap="full"],
  .flyout.settings-side[data-snap="full"] { --sheet-h: var(--snap-full); }
  .flyout.trips-side .flyout-head,
  .flyout.settings-side .flyout-head { touch-action: none; }
  /* Height snaps animate while open; the [hidden] rule keeps its delayed
     visibility for the close fade. */
  .flyout.trips-side:not([hidden]),
  .flyout.settings-side:not([hidden]) {
    transition: transform var(--dur-2) ease, opacity var(--dur-2) ease,
      height var(--dur-2) ease, visibility 0s;
  }
  .flyout.trips-side .sheet-grab,
  .flyout.settings-side .sheet-grab {
    display: grid;
    place-items: center;
    flex: none;
    align-self: stretch;
    width: auto;
    height: 32px;
    margin: -12px -14px -6px;
    background: none;
    touch-action: none;
    cursor: grab;
  }
  .flyout.trips-side .sheet-grab::after,
  .flyout.settings-side .sheet-grab::after {
    content: "";
    width: 44px;
    height: 4px;
    border-radius: 2px;
    background: var(--brand);
    opacity: 0.45;
  }
  /* Mid-drag the finger owns the height — no tween fighting it. */
  .flyout.trips-side.dragging,
  .flyout.settings-side.dragging { transition: opacity var(--dur-2) ease; }

  /* The editor card becomes a bottom sheet. */
  .editor-pop {
    left: 0;
    right: 0;
    top: auto;
    bottom: 0;
    width: 100%;
    height: var(--sheet-h, var(--snap-half));
    max-height: calc(100dvh - max(24px, env(safe-area-inset-top) + 12px));
    overflow: hidden;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    padding: 10px 16px calc(16px + env(safe-area-inset-bottom));
    animation: none;
    transition: height var(--dur-2) ease;
  }
  .editor-pop[data-snap="peek"] { --sheet-h: var(--snap-peek); }
  .editor-pop[data-snap="half"] { --sheet-h: var(--snap-half); }
  .editor-pop[data-snap="full"] {
    --sheet-h: calc(100dvh - var(--top-reserve));
  }
  .editor-pop.dragging { transition: none; }
  .editor-pop.opening { animation: sheet-in var(--dur-2) ease-out; }
  @keyframes sheet-in {
    from { transform: translateY(40%); opacity: 0.4; }
    to { transform: translateY(0); opacity: 1; }
  }
  .editor-pop::before { display: none; }
  /* The grabber is the caret stem, rotated. */
  .sheet-grab {
    display: block;
    width: 44px;
    height: 4px;
    border-radius: 2px;
    background: var(--brand);
    opacity: 0.45;
    margin: 0 auto 10px;
  }
  .editor-pop > .sheet-grab {
    display: grid;
    place-items: center;
    align-self: stretch;
    width: auto;
    height: 32px;
    margin: -10px -16px -6px;
    background: none;
    opacity: 1;
    touch-action: none;
    cursor: grab;
  }
  .editor-pop > .sheet-grab::after {
    content: "";
    width: 44px;
    height: 4px;
    border-radius: 2px;
    background: var(--brand);
    opacity: 0.45;
  }

  /* The header stat strip carries the route figures on phones; the in-list
     summary row would repeat them. display:none keeps #survivor-count in the
     DOM for map-page.js. */
  .route-summary { display: none; }

  /* Pins sheet: the chrome above the first row shrinks so half snap shows
     pins, not controls. The count heading is redundant with the dock badge
     and the collection counts — display:none keeps it in the DOM (ui.js
     syncBadge and pins-filter.js read and rewrite it). */
  .drawer-pins #pin-count { display: none; }
  .drawer-pins .drawer-head { margin-bottom: 4px; }
  .pin-controls { gap: 6px; }
  .pin-controls-row {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    padding: 2px 0;
  }
  .pin-controls-row::-webkit-scrollbar { display: none; }
  .pin-controls-row .pin-chips { flex-wrap: nowrap; flex: none; }
  .pin-controls-row .chip { white-space: nowrap; flex: none; }
  .pin-search { padding: 8px 10px; }
  .col-head-row { padding: 6px 2px; }
  .pin-head { padding: 7px 6px; }
  /* Survivor rows: the ▾ menu leads with the preferred app, so the
     standalone Navigate link is redundant width here. */
  .survivor-list .pin-actions > a[title^="Navigate here"] { display: none; }

  /* Spotlight becomes a fullscreen takeover: the phone keyboard plus a
     floating card leaves no useful map anyway, so the search gets the whole
     screen and the results list all the remaining height. */
  .spotlight-panel {
    margin: 0;
    width: 100vw;
    height: 100dvh;
    max-height: none;
    border-radius: 0;
    animation: none;
  }
  .spotlight-row { padding-top: max(8px, env(safe-area-inset-top)); }
  /* Your-pins hits stay content-sized; the places list soaks up the rest. */
  .spotlight-panel .geocode-results { max-height: none; }
  .spotlight-panel #spotlight-pins { flex: 0 0 auto; }
  .spotlight-panel #geocode-results { flex: 1 1 auto; }
  .spotlight-close { display: grid; }

  /* Settings sheet: the cards-in-a-sheet double chrome flattens into a
     grouped list — dividers between sections instead of nested surfaces. */
  .flyout.settings-side .card-block {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 14px 2px;
    margin: 0;
    border-bottom: 1px solid var(--line);
  }
  .flyout.settings-side .card-block:last-child { border-bottom: 0; }
}

/* ---- short viewports (landscape phones) ---- */
/* The desktop side-panel layout applies (width > 760px) but the height is
   phone-sized: a ~310px panel can't afford desktop chrome. Pull the panels
   up under the nav pill and shave their padding so the height goes to
   content; the panels' own max-height clamps already keep bottoms on-screen. */
@media (min-width: 761px) and (max-height: 500px) {
  .drawer.drawer-dock.drawer-pins,
  .drawer.drawer-dock.drawer-trip,
  .flyout.trips-side,
  .flyout.settings-side {
    top: max(60px, calc(env(safe-area-inset-top) + 46px));
    padding: 10px 12px 6px;
  }
  .drawer-head,
  .flyout-head { margin-bottom: 4px; }
  .spotlight-panel { margin-top: 10px; max-height: calc(100dvh - 20px); }
}

/* Measured menu direction (ui.js onNavMoreToggle): the static up/down
   defaults above assume where a row sits inside its clipped scroll area —
   wrong for rows near the opposite edge (an up-flipped menu on the sheet's
   first row loses its top items). The measured side wins; kept at the end
   of the file to out-cascade the equal-specificity static rules. */
.nav-more[data-menu-side="down"] > .nav-more-menu { top: calc(100% + 6px); bottom: auto; }
.nav-more[data-menu-side="up"] > .nav-more-menu { top: auto; bottom: calc(100% + 6px); }
/* ui.js caps max-height when neither side fully fits — scroll, don't clip. */
.nav-more[data-menu-side] > .nav-more-menu { overflow-y: auto; }

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