feat(hikes): forgiving map selection, photo lightbox, detail polish

Map interaction:
- Overview map: widen the canvas renderer hit-test (tolerance) so a route
  can be hovered/clicked from a comfortable margin instead of demanding a
  pixel-perfect click on the thin line.
- Detail map: drive the elevation cursor from a whole-map mousemove that
  snaps to the nearest track point within ~70 px (track cached in
  layer-point space, refreshed on zoom/move), instead of requiring the
  pointer to ride exactly on the trail. The hover pin now renders for
  map-sourced hovers too, and is recoloured to nord red as a distinct
  "you are here" marker. Trail polyline made non-interactive.

Detail page:
- Move the photo strip above the stats row and trim it (3:2 cards).
- Add a fullscreen lightbox: an expand button on each card opens the
  full-res image with prev/next, arrow keys, Esc, backdrop-close and a
  body-scroll lock; opening/stepping syncs the map + strip. The card's
  existing click (map-position sync) is preserved.
- Cap inline prose images at 680 px (centered) so they don't blow up to
  full width in the single-column layout on wider screens; the desktop
  two-column layout is unaffected.
This commit is contained in:
2026-05-22 12:36:06 +02:00
parent 896e42f5d9
commit bb1d494c48
6 changed files with 424 additions and 62 deletions
@@ -98,10 +98,15 @@
const L = await import('leaflet');
if (cancelled || !node.isConnected) return;
// `tolerance` widens the canvas renderer's hit-test radius around
// every polyline (hit = weight/2 + tolerance), so a route can be
// hovered/clicked from a comfortable margin instead of demanding a
// pixel-perfect click on the 4 px line.
const map = L.map(node, {
attributionControl: true,
zoomControl: true,
preferCanvas: true
preferCanvas: true,
renderer: L.canvas({ tolerance: 12 })
});
// Sensible default centre (mid-Switzerland) while the polyline
// layer is built up; `fitBounds` below overrides it once the