/* Itinerary Map — feature-flagged UI (issue #732 Part B, see
   js/features/itinerary-map/; moved from its own trip tab into a view of
   the Itinerary tab by issue #993). Hidden by default; itinerary-map.js only
   reveals the #itin-view-toggle-map-btn toggle button once
   FEATURE_ITINERARY_MAP is on — see
   itineraryMapEnabled() there. Safe to leave this markup in index.html
   indefinitely even while the feature is off, same convention as
   css/features/weather-forecast.css's .feature-weather. */
.feature-itinerary-map {
  display: none;
}

.map-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

.map-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

.map-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--tap-min);
  padding: var(--space-sm) var(--space-lg);
  font-size: var(--fs-sm);
  border: 2px solid var(--df-ink);
  border-radius: var(--radius-pill);
  cursor: pointer;
  background: var(--df-panel);
  color: var(--df-ink-soft);
  font-family: inherit;
  transition: background var(--dur-feedback) var(--ease-out), color var(--dur-feedback) var(--ease-out), border-color var(--dur-feedback) var(--ease-out);
}

.map-chip.sel {
  background: var(--df-ink);
  border-color: var(--df-ink);
  color: var(--df-panel);
}

.map-day-route-btn {
  min-height: var(--tap-min);
  padding: var(--space-sm) var(--space-lg);
  border-radius: var(--radius-pill);
  border: 2px solid var(--df-ink);
  background: var(--df-panel);
  color: var(--df-ink);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  text-decoration: none;
}

.map-day-route-btn[aria-disabled="true"] {
  opacity: 0.4;
  pointer-events: none;
}

/* ==================== Legend (issue #1004) ====================
   Lives in the toolbar, not floating over the canvas — the stop-list
   overlay already claims a canvas corner (and the mobile bottom-sheet
   variant claims the whole bottom edge), so a second floating card would
   have nowhere left to dock without colliding with it. A plain relative-
   positioned dropdown here sits outside the map's stacking context
   entirely, so none of #1002's pane z-index concerns apply. */
.map-legend {
  position: relative;
}

.map-legend-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--tap-min);
  padding: var(--space-sm) var(--space-lg);
  font-size: var(--fs-sm);
  border: 2px solid var(--df-ink);
  border-radius: var(--radius-pill);
  cursor: pointer;
  background: var(--df-panel);
  color: var(--df-ink);
  font-family: inherit;
  font-weight: 600;
}

.map-legend-panel {
  position: absolute;
  z-index: 30;
  bottom: calc(100% + var(--space-xs));
  left: 0;
  width: max-content;
  max-width: min(260px, calc(100vw - 2 * var(--space-md)));
  padding: var(--space-sm) var(--space-md);
  background: var(--df-panel);
  border: 2px solid var(--df-ink);
  border-radius: var(--df-radius-card);
  box-shadow: var(--df-press);
}

.map-legend-section + .map-legend-section {
  margin-top: var(--space-sm);
  padding-top: var(--space-sm);
  border-top: 1.5px solid var(--df-hair);
}

.map-legend-heading {
  margin-bottom: var(--space-xs);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--df-ink-soft);
}

.map-legend-items {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.map-legend-item {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--fs-xs);
  color: var(--df-ink);
}

.map-legend-item i {
  width: 1.25em;
  text-align: center;
  color: var(--df-ink-soft);
}

.map-legend-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.map-legend-swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 9px;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.55);
}

.map-legend-note {
  margin: var(--space-xs) 0 0;
  font-size: var(--fs-xs);
  color: var(--df-ink-soft);
}

/* vh first, dvh override right after — same fallback order as every other
   viewport-sized surface in this app (css/style.css). A fixed, generous
   height rather than a computed "100dvh minus chrome" figure: the chip
   rows above it can wrap to a second line on a narrow phone, and a precise
   subtraction would drift out of sync with that. */
.map-canvas-wrap {
  position: relative;
  width: 100%;
  height: 60vh;
  height: 60dvh;
  min-height: 320px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--df-panel);
}

@media (min-width: 681px) {
  .map-canvas-wrap {
    height: 70vh;
    height: 70dvh;
  }
}

.map-canvas {
  width: 100%;
  height: 100%;
}

/* MapLibre's zoom buttons default to 29px — below --tap-min. */
.map-canvas .maplibregl-ctrl-group button {
  min-width: var(--tap-min);
  min-height: var(--tap-min);
}

.map-canvas .maplibregl-ctrl-attrib {
  font-size: var(--fs-xs);
}

/* Controls and popups sit above the stop-list overlay (z-index 620 below),
   same ordering Leaflet's panes gave: pins < overlay < popup < controls. */
.map-canvas .maplibregl-popup {
  z-index: 700;
}

.map-canvas .maplibregl-ctrl-top-left,
.map-canvas .maplibregl-ctrl-top-right {
  z-index: 1000;
}

.map-skeleton-overlay {
  position: absolute;
  inset: 0;
  z-index: 5;
}

/* A stop's pin. Built as a DOM element wrapped by a maplibregl.Marker (not an image marker) so both its
   fill (group — day, or an idea's ideaGroup) and its glyph (category) read
   straight off existing --vn-*/PLACE_TYPES tokens (js side reads
   getComputedStyle, never a new palette) with no extra asset request.
   Issue #1004 swapped which channel carries which: colour used to be
   category alone, with nothing encoding which stops shared a day/group —
   see js/features/itinerary-map/itinerary-map.js's mapGroupColor()/
   mapCategoryIconClass(). */
.map-pin-wrap {
  /* maplibre-gl.css (loaded async from the CDN) is what makes this element
     absolutely positioned and so shrink-wrapped; until it applies, or if it
     ever fails to, a block-level wrapper stretches to its container and the
     pin inside reads as a rectangle (#2174). Own the size here so the
     marker is 28px square whatever the library stylesheet's state. */
  width: 28px;
  height: 28px;
}

.map-pin {
  box-sizing: border-box;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid var(--df-panel);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.map-pin-icon {
  font-size: 13px;
  color: #fff;
  /* Group hues span light-mode-dark to dark-mode-light swings (light-dark()
     tokens, css/style.css) — a plain white glyph would lose contrast against
     the lighter end of that range, so a dark outline via text-shadow keeps
     it legible against every hue/theme combination rather than picking a
     per-hue icon color. */
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.55);
}

/* Quick Ideas (day = null, parentId = null) — outlined, not filled, so a
   pin reads as "not yet on a day" at a glance next to a scheduled stop's
   solid fill (issue #732 Part B's own layer spec) — now carrying the group
   hue on its border/glyph instead of the fill (#1004), since the fill
   channel there is reserved for "hollow vs. solid" rather than colour. */
.map-pin-idea {
  background: var(--df-panel) !important;
  border-width: 3px;
}

.map-pin-idea .map-pin-icon {
  text-shadow: none;
}

/* Printed day number — only rendered at all once a trip has more days than
   the group colour ramp has hues (renderItineraryMap()'s dayBadge flag),
   so a repeated hue is never mistaken for the same day. Absent from every
   pin on a normal-length trip, keeping the common case uncluttered. */
.map-pin-day-badge {
  position: absolute;
  right: -3px;
  bottom: -3px;
  min-width: 14px;
  height: 14px;
  padding: 0 2px;
  border-radius: 999px;
  background: var(--df-panel);
  border: 1.5px solid var(--df-ink);
  font-size: 8px;
  font-weight: 700;
  line-height: 11px;
  text-align: center;
  color: var(--df-ink);
}

.map-popup .maplibregl-popup-content {
  padding: var(--space-md);
  background: var(--df-panel);
  color: var(--df-ink);
  font-size: var(--fs-sm);
}

.map-popup.maplibregl-popup-anchor-bottom .maplibregl-popup-tip { border-top-color: var(--df-panel); }
.map-popup.maplibregl-popup-anchor-top .maplibregl-popup-tip { border-bottom-color: var(--df-panel); }
.map-popup.maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--df-panel); }
.map-popup.maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--df-panel); }
.map-popup .maplibregl-popup-close-button { color: var(--df-ink); min-width: var(--tap-min); min-height: var(--tap-min); }

.map-popup-title {
  font-weight: 600;
  margin-bottom: var(--space-2xs);
}

.map-popup-actions {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

.map-popup-actions a,
.map-popup-actions button {
  font-size: var(--fs-xs);
}

/* ==================== Stop-list overlay (issue #993) ====================
   Always floats over the map canvas — never a split layout, at any width
   (Decision 2 of #993). Populated by renderMapStopList() (js/features/
   itinerary-map/itinerary-map.js). Empty and unstyled (no border/shadow of
   its own to show) when its innerHTML is blank — see that function's own
   "no empty card" rule. */
.map-stop-list {
  position: absolute;
  /* Issue #1002: .map-canvas (the MapLibre container) is position:relative
     with no z-index of its own, which per CSS2.1 stacking rules means it
     does NOT form a stacking context — MapLibre's internal layers/controls
     (marker 600 as it was under Leaflet, popup 700 and controls 1000 set
     above) compare directly against this sibling instead of staying
     boxed inside .map-canvas. A low z-index here (the old value was 10) lost
     to the tile pane outright, so the overlay was hidden under the map the
     whole time and only showed through the transparent gaps a zoom
     animation's tile fade-in briefly leaves.
     620 sits above the marker pane (600, so pins never paint over the card)
     but below the tooltip/popup panes (650/700) and the 1000 controls, so an
     open popup or the zoom control stays visible above the overlay rather
     than getting buried under it. */
  z-index: 620;
  background: var(--df-panel);
  border: 2px solid var(--df-ink);
  border-radius: var(--df-radius-card);
  box-shadow: var(--df-press);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Desktop/wide default: docked to the left edge, vertically centered —
     The zoom control sits top-left and the attribution control is added
     top-right at map-build time (renderItineraryMap(), js/features/
     itinerary-map/itinerary-map.js, `addControl(..., 'top-right')`) specifically so
     neither top corner is contested and this card can sit clear of both
     without needing to know either control's exact pixel height. */
  top: 50%;
  left: var(--space-md);
  transform: translateY(-50%);
  width: min(320px, calc(100% - 2 * var(--space-md)));
  max-height: calc(100% - 2 * var(--space-md));
}

.map-stop-list:empty {
  display: none;
}

.map-stop-list-header {
  flex-shrink: 0;
  border-bottom: 2px solid var(--df-ink);
}

.map-stop-list-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  min-height: var(--tap-min);
  padding: var(--space-sm) var(--space-md);
  border: none;
  background: transparent;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--df-ink);
  cursor: pointer;
}

.map-stop-list-toggle i {
  transition: transform var(--dur-state) var(--ease-out);
}

/* Collapsed: shrinks to the compact header bar alone — max-height:0 (not
   display:none) so the collapse itself can travel via the shared motion
   tokens (see .map-stop-list-body's own transition below); overflow:hidden
   clips it and visibility:hidden takes it out of the tab order while
   collapsed, so a keyboard user can't Tab into an invisible row. The
   chevron flips to point back down at what expanding it would reveal. */
.map-stop-list.collapsed .map-stop-list-body {
  max-height: 0;
  overflow: hidden;
  /* Not just visually clipped — genuinely out of the tab order while
     collapsed, or a keyboard user could Tab into an invisible row. */
  visibility: hidden;
}
.map-stop-list.collapsed .map-stop-list-toggle i {
  transform: rotate(-90deg);
}

.map-stop-list-body {
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Collapse/expand travels via max-height, not display:none, so it can use
     the shared motion tokens — resolved to its finished state (not hidden)
     under reduced motion in style.css's own reduced-motion block, which is
     also where the toggle chevron's own rotation is answered. 600px is a
     generous fixed ceiling (bigger than any real stop list needs to
     animate against) — a transition on max-height still travels correctly
     with a target taller than the actual content. */
  max-height: 600px;
  transition: max-height var(--dur-state) var(--ease-out);
}

.map-stop-list-group + .map-stop-list-group {
  border-top: 1.5px solid var(--df-hair);
}

.map-stop-list-day-header {
  position: sticky;
  top: 0;
  padding: var(--space-xs) var(--space-md);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--df-ink-soft);
  background: var(--df-panel);
}

.map-stop-list-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: var(--tap-min);
  padding: var(--space-xs) var(--space-md);
  cursor: pointer;
  transition: background var(--dur-feedback) var(--ease-out);
}

.map-stop-list-row:hover,
.map-stop-list-row:focus-visible {
  background: var(--surface-2);
}
.map-stop-list-row:focus-visible {
  outline: 2px solid var(--vn-teal);
  outline-offset: -2px;
}

.map-stop-list-row.highlighted {
  background: var(--vn-teal-light, var(--surface-2));
}

/* Not tappable — no marker to pan to yet (issue #762's lazy backfill, or a
   permanent Nominatim miss). */
.map-stop-list-row-unlocated {
  cursor: default;
  opacity: 0.6;
}

.map-stop-list-order {
  flex-shrink: 0;
  min-width: 1.25em;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--df-ink-soft);
  text-align: right;
}

.map-stop-list-dot {
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid;
}

/* Issue #1004 — the row's own category glyph, next to its (now group-
   coloured) dot above: the same colour/icon split as the pin it points at,
   so the list and the canvas never disagree about what a stop is. */
.map-stop-list-cat-icon {
  flex-shrink: 0;
  font-size: var(--fs-sm);
  color: var(--df-ink-soft);
}

.map-stop-list-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-sm);
}

.map-stop-list-time,
.map-stop-list-hint {
  flex-shrink: 0;
  font-size: var(--fs-xs);
  color: var(--df-ink-soft);
}

/* Mobile: a floating bottom sheet instead of a docked side card — full
   width, pinned to the bottom edge of the canvas, rounded top corners only.
   The bottom edge is otherwise clear (attribution lives top-right, same as
   desktop — see the .map-stop-list rule above), so this doesn't need its
   own carve-out to avoid covering it. */
@media (max-width: 680px) {
  .map-stop-list {
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    transform: none;
    width: 100%;
    max-height: min(50%, 360px);
    border-radius: var(--df-radius-card) var(--df-radius-card) 0 0;
    border-bottom: none;
  }
}

/* prefers-reduced-motion is handled entirely in JS for the map canvas itself
   (itinerary-map.js reads prefersReducedMotion() before choosing
   fitBounds/easeTo's own animate option, and before scrollIntoView()'s own
   behavior when a stop-list row is highlighted) — MapLibre's pan/zoom
   transitions are canvas animations, not something this
   stylesheet can target. The stop-list overlay's own collapse/expand is a
   travelling effect this stylesheet DOES own, so it's answered in
   style.css's shared reduced-motion block instead of duplicating one here —
   see the comment there for why that block, not this file, is the single
   source for every other travelling effect in the app. */
