diff --git a/src/components/DirectionsPanel.jsx b/src/components/DirectionsPanel.jsx
index d2f7db4..794cad6 100644
--- a/src/components/DirectionsPanel.jsx
+++ b/src/components/DirectionsPanel.jsx
@@ -1,263 +1,299 @@
-import { useEffect } from "react"
-import { ArrowUpDown, Plus, X, Footprints, Bike, Car, Shield, AlertTriangle, Zap } from "lucide-react"
-import { useStore } from "../store"
-import LocationInput from "./LocationInput"
-import ManeuverList from "./ManeuverList"
-
-const TRAVEL_MODES = [
- { id: "auto", label: "Drive", Icon: Car },
- { id: "foot", label: "Foot", Icon: Footprints },
- { id: "mtb", label: "MTB", Icon: Bike },
- { id: "atv", label: "ATV", Icon: Car },
- { id: "vehicle", label: "4x4", Icon: Car },
-]
-
-const BOUNDARY_MODES = [
- { id: "strict", label: "Strict", Icon: Shield, title: "Avoid barriers" },
- { id: "pragmatic", label: "Cross", Icon: AlertTriangle, title: "Cross with penalty" },
- { id: "emergency", label: "Ignore", Icon: Zap, title: "Ignore barriers" },
-]
-
-export default function DirectionsPanel({ onClose }) {
- const routeStart = useStore((s) => s.routeStart)
- const routeEnd = useStore((s) => s.routeEnd)
- const routeMode = useStore((s) => s.routeMode)
- const boundaryMode = useStore((s) => s.boundaryMode)
- const routeResult = useStore((s) => s.routeResult)
- const routeLoading = useStore((s) => s.routeLoading)
- const routeError = useStore((s) => s.routeError)
- const stops = useStore((s) => s.stops)
- const userLocation = useStore((s) => s.userLocation)
- const geoPermission = useStore((s) => s.geoPermission)
-
- const setRouteStart = useStore((s) => s.setRouteStart)
- const setRouteEnd = useStore((s) => s.setRouteEnd)
- const setRouteMode = useStore((s) => s.setRouteMode)
- const setBoundaryMode = useStore((s) => s.setBoundaryMode)
- const computeRoute = useStore((s) => s.computeRoute)
- const clearRoute = useStore((s) => s.clearRoute)
- const setDirectionsMode = useStore((s) => s.setDirectionsMode)
- const addStop = useStore((s) => s.addStop)
- const removeStop = useStore((s) => s.removeStop)
- const reorderStops = useStore((s) => s.reorderStops)
-
- // Auto-fill origin with GPS if available and origin is empty
- useEffect(() => {
- if (!routeStart && geoPermission === "granted" && userLocation) {
- setRouteStart({
- lat: userLocation.lat,
- lon: userLocation.lon,
- name: "Your location",
- source: "gps",
- })
- }
- }, [routeStart, geoPermission, userLocation, setRouteStart])
-
- // Auto-compute route when both endpoints are set
- useEffect(() => {
- if (routeStart && routeEnd) {
- computeRoute()
- }
- }, [routeStart?.lat, routeStart?.lon, routeEnd?.lat, routeEnd?.lon])
-
- const handleSwap = () => {
- const tempStart = routeStart
- const tempEnd = routeEnd
- setRouteStart(tempEnd)
- setRouteEnd(tempStart)
- }
-
- const handleClose = () => {
- clearRoute()
- setDirectionsMode(false)
- onClose?.()
- }
-
- const handleAddStop = () => {
- // Insert a stop between origin and destination
- // For now, this adds to the stops array
- // The UI will show intermediate stops
- }
-
- // Multi-stop support: show intermediate stops from the stops array
- const intermediateStops = stops.slice(1, -1) // Everything except first and last
-
- return (
-
- {/* Header */}
-
-
- Directions
-
-
-
-
-
-
- {/* Origin/Destination inputs with swap button */}
-
- {/* Origin */}
-
-
- {/* Swap button - positioned between inputs */}
-
-
-
-
- {/* Intermediate stops (for multi-stop routes) */}
- {intermediateStops.map((stop, idx) => (
-
- {
- if (place) {
- const newStops = [...stops]
- newStops[idx + 1] = { ...newStops[idx + 1], ...place }
- reorderStops(newStops)
- } else {
- removeStop(stop.id)
- }
- }}
- placeholder="Stop"
- icon="stop"
- fieldId={`stop-${idx}`}
- />
-
- ))}
-
- {/* Destination */}
-
-
- {/* Add stop button */}
- {routeStart && routeEnd && stops.length < 10 && (
-
-
- Add stop
-
- )}
-
-
- {/* Travel mode selector */}
-
- {TRAVEL_MODES.map((m) => {
- const active = routeMode === m.id
- return (
- setRouteMode(m.id)}
- className="flex-1 flex items-center justify-center gap-1 py-2 text-xs rounded-lg transition-colors"
- style={{
- background: active ? "var(--accent-muted)" : "var(--bg-overlay)",
- color: active ? "var(--accent)" : "var(--text-tertiary)",
- }}
- title={m.label}
- >
-
- {m.label}
-
- )
- })}
-
-
- {/* Boundary mode selector (only for non-auto modes) */}
- {routeMode !== "auto" && (
-
- {BOUNDARY_MODES.map((m) => {
- const active = boundaryMode === m.id
- return (
- setBoundaryMode(m.id)}
- className="flex-1 flex items-center justify-center gap-1 py-1.5 text-xs rounded-lg transition-colors"
- style={{
- background: active ? "var(--accent-muted)" : "var(--bg-overlay)",
- color: active ? "var(--accent)" : "var(--text-tertiary)",
- }}
- title={m.title}
- >
-
- {m.label}
-
- )
- })}
-
- )}
-
- {/* Loading indicator */}
- {routeLoading && (
-
-
-
- Finding route...
-
-
- )}
-
- {/* Error message */}
- {routeError && (
-
- {routeError}
-
- )}
-
- {/* Route summary and maneuvers */}
- {routeResult && !routeLoading && (
-
-
-
- )}
-
- {/* Hint when waiting for input */}
- {!routeStart && !routeEnd && !routeLoading && (
-
-
- Enter addresses, paste coordinates, or click the map
-
-
- )}
-
- )
-}
+import { useEffect } from "react"
+import { ArrowUpDown, Plus, X, Footprints, Bike, Car, Shield, AlertTriangle, Zap } from "lucide-react"
+import { useStore } from "../store"
+import LocationInput from "./LocationInput"
+import ManeuverList from "./ManeuverList"
+
+const TRAVEL_MODES = [
+ { id: "auto", label: "Drive", Icon: Car },
+ { id: "foot", label: "Foot", Icon: Footprints },
+ { id: "mtb", label: "MTB", Icon: Bike },
+ { id: "atv", label: "ATV", Icon: Car },
+ { id: "vehicle", label: "4x4", Icon: Car },
+]
+
+const BOUNDARY_MODES = [
+ { id: "strict", label: "Strict", Icon: Shield, title: "Avoid barriers" },
+ { id: "pragmatic", label: "Cross", Icon: AlertTriangle, title: "Cross with penalty" },
+ { id: "emergency", label: "Ignore", Icon: Zap, title: "Ignore barriers" },
+]
+
+export default function DirectionsPanel({ onClose }) {
+ const routeStart = useStore((s) => s.routeStart)
+ const routeEnd = useStore((s) => s.routeEnd)
+ const routeMode = useStore((s) => s.routeMode)
+ const boundaryMode = useStore((s) => s.boundaryMode)
+ const routeResult = useStore((s) => s.routeResult)
+ const routeLoading = useStore((s) => s.routeLoading)
+ const routeError = useStore((s) => s.routeError)
+ const stops = useStore((s) => s.stops)
+ const userLocation = useStore((s) => s.userLocation)
+ const geoPermission = useStore((s) => s.geoPermission)
+
+ const setRouteStart = useStore((s) => s.setRouteStart)
+ const setRouteEnd = useStore((s) => s.setRouteEnd)
+ const setRouteMode = useStore((s) => s.setRouteMode)
+ const setBoundaryMode = useStore((s) => s.setBoundaryMode)
+ const computeRoute = useStore((s) => s.computeRoute)
+ const clearRoute = useStore((s) => s.clearRoute)
+ const setDirectionsMode = useStore((s) => s.setDirectionsMode)
+ const addStop = useStore((s) => s.addStop)
+ const removeStop = useStore((s) => s.removeStop)
+ const reorderStops = useStore((s) => s.reorderStops)
+
+ // Auto-fill origin with GPS if available and origin is empty
+ useEffect(() => {
+ if (!routeStart && geoPermission === "granted" && userLocation) {
+ setRouteStart({
+ lat: userLocation.lat,
+ lon: userLocation.lon,
+ name: "Your location",
+ source: "gps",
+ })
+ }
+ }, [routeStart, geoPermission, userLocation, setRouteStart])
+
+ // Auto-compute route when both endpoints are set
+ useEffect(() => {
+ if (routeStart && routeEnd) {
+ computeRoute()
+ }
+ }, [routeStart?.lat, routeStart?.lon, routeEnd?.lat, routeEnd?.lon])
+
+ const handleSwap = () => {
+ const tempStart = routeStart
+ const tempEnd = routeEnd
+ setRouteStart(tempEnd)
+ setRouteEnd(tempStart)
+ }
+
+ const handleClose = () => {
+ clearRoute()
+ setDirectionsMode(false)
+ onClose?.()
+ }
+
+ const handleAddStop = () => {
+ // For now, show a message - multi-stop UI is complex
+ // TODO: Implement full multi-stop UI
+ }
+
+ // Check if route has wilderness segments
+ const hasWilderness = routeResult?.summary?.wilderness_distance_km > 0
+
+ // Multi-stop support: show intermediate stops from the stops array
+ const intermediateStops = stops.slice(1, -1)
+
+ return (
+
+ {/* Header */}
+
+
+ Directions
+
+
+
+
+
+
+ {/* Origin/Destination inputs with swap button */}
+
+ {/* Origin */}
+
+
+ {/* Swap button - positioned between inputs */}
+
+
+
+
+ {/* Intermediate stops (for multi-stop routes) */}
+ {intermediateStops.map((stop, idx) => (
+
+ {
+ if (place) {
+ const newStops = [...stops]
+ newStops[idx + 1] = { ...newStops[idx + 1], ...place }
+ reorderStops(newStops)
+ } else {
+ removeStop(stop.id)
+ }
+ }}
+ placeholder="Stop"
+ icon="stop"
+ fieldId={`stop-${idx}`}
+ />
+
+ ))}
+
+ {/* Destination */}
+
+
+ {/* Add stop button - only show when route exists */}
+ {routeStart && routeEnd && stops.length < 10 && (
+
+
+ Add stop
+
+ )}
+
+
+ {/* Travel mode selector */}
+
+ {TRAVEL_MODES.map((m) => {
+ const active = routeMode === m.id
+ return (
+ setRouteMode(m.id)}
+ className="flex-1 flex items-center justify-center gap-1 py-2 text-xs rounded-lg transition-colors"
+ style={{
+ background: active ? "var(--accent-muted)" : "var(--bg-overlay)",
+ color: active ? "var(--accent)" : "var(--text-tertiary)",
+ }}
+ title={m.label}
+ >
+
+ {m.label}
+
+ )
+ })}
+
+
+ {/* Boundary mode selector (only for non-auto modes) */}
+ {routeMode !== "auto" && (
+
+ {BOUNDARY_MODES.map((m) => {
+ const active = boundaryMode === m.id
+ return (
+ setBoundaryMode(m.id)}
+ className="flex-1 flex items-center justify-center gap-1 py-1.5 text-xs rounded-lg transition-colors"
+ style={{
+ background: active ? "var(--accent-muted)" : "var(--bg-overlay)",
+ color: active ? "var(--accent)" : "var(--text-tertiary)",
+ }}
+ title={m.title}
+ >
+
+ {m.label}
+
+ )
+ })}
+
+ )}
+
+ {/* Loading indicator */}
+ {routeLoading && (
+
+
+
+ Finding route...
+
+
+ )}
+
+ {/* Error message - friendly text, no "offroute" */}
+ {routeError && (
+
+ {routeError.includes("No route") || routeError.includes("not found")
+ ? "No route found. Try a different start point or mode."
+ : routeError.includes("entry point")
+ ? "No roads found nearby — try Foot mode for trails."
+ : routeError}
+
+ )}
+
+ {/* Route legend - only shown when route has wilderness segment */}
+ {routeResult && hasWilderness && !routeLoading && (
+
+
+
+
+
+ Wilderness (on foot)
+
+
+
+
+
+ Road/Trail
+
+
+ )}
+
+ {/* Route summary and maneuvers */}
+ {routeResult && !routeLoading && (
+
+
+
+ )}
+
+ {/* Hint when waiting for input */}
+ {!routeStart && !routeEnd && !routeLoading && (
+
+
+ Enter addresses, paste coordinates, or click the map
+
+
+ )}
+
+ )
+}
diff --git a/src/components/Panel.jsx b/src/components/Panel.jsx
index a708734..98e9f16 100644
--- a/src/components/Panel.jsx
+++ b/src/components/Panel.jsx
@@ -90,10 +90,23 @@ export default function Panel({ onClearRoute }) {
const showEmptyState = panelState === 'IDLE' && !hasRoutePoints
const routesContent = directionsMode ? (
- {
- setDirectionsMode(false)
- onClearRoute?.()
- }} />
+ <>
+ {
+ setDirectionsMode(false)
+ onClearRoute?.()
+ }} />
+ {/* Show place card below directions when clicking map during routing */}
+ {selectedPlace && (
+
+ )}
+ >
) : (
<>