api_grade and api_day parsed the `date` query param with an unguarded
datetime.date.fromisoformat, so a malformed or non-calendar value
(notadate, 2026-13-40, 2026-02-30) raised ValueError and surfaced as a
500. Route the parse through a _parse_target_date helper that maps the
ValueError to HTTPException(422); absent/empty and valid dates behave
exactly as before. Add a route-level test asserting 422 on bad dates.
Steady-state usage of the Open-Meteo API is removed; it remains only as a dormant fallback. Geocoding -> local GeoNames + Nominatim; wind gusts -> Meteostat; history -> NASA POWER (curated cells seeded with keyless ERA5); recent+forecast -> NASA range + MET Norway. Plus a drift-check tool comparing NASA vs Open-Meteo. All keyless, no new infra. Backend suite green in-image (gate).