The coord-validation and antimeridian-unwrap logic added in the last
pass had been duplicated across map.ts, trail_manager.ts, and
info_map.ts. Move both into map_helpers.ts and import from there so
there is one authoritative implementation.
- Heatmap: filter points with invalid coords or out-of-range intensity
so malformed rows can't distort the layer.
- InfoMap: validate coords for range (not just NaN) and use an epsilon
when comparing positions, so float noise doesn't cause needless
re-renders or accept bogus data.
- TrailManager: getTrailCenter returns null for empty input instead of
the ambiguous (0, 0) null-island; callers guard accordingly.
Convert app.js, info_map.js, error_boundary.js, and time_ago_hook.js
to TypeScript with proper typing for Phoenix hooks, Leaflet, DOM
interactions, and timer management. Update esbuild hex package to
~> 0.10 and binary to 0.25.4. Entry point changed to app.ts.