diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8ee00d839..98d32365f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,26 +1,10 @@
# Changelog
-- Local FM seek finds stations after wrapping around 87.5–108 MHz, covers both
- band edges, skips the starting station and rejects spectra from an earlier
- tune (Aditya, @Rohilalala, #1174).
-- Local RTL-SDR keeps USB reads queued in order and bounds pending decoder
- work. A stalled or overloaded decoder stops with an explicit error; stopping
- terminates its worker and rejects late audio. FM playback buffers short
- scheduling gaps, caps its audio queue at 256 ms, and reduces display updates
- and spectrum calculations while listening.
-
-- Add Bogotá (Tembici, 250 stations) and Santiago de Chile (Bike Itaú, 200
- stations) to the GBFS bikeshare registry, the layer's first stations in
- Colombia and Chile. Both are PBSC feeds like the Brazilian Bike Itaú cities,
- so no proxy change is needed. Both feeds also list depot stations on other
- continents (São Paulo, Madrid) as in service; stations outside a city's load
- radius are now dropped instead of drawn as live docks.
-
-- Add Barcelona's bike share to the GBFS registry: Bicing (543 stations, the
- city's PBSC system) and AMBici (237 e-bike stations across the metropolitan
- area, run by nextbike). Both are keyless GBFS 2.3 feeds listed in
- MobilityData's systems catalogue; AMBici's is CC0. `gbfs.nextbike.net` joins
- the proxy allowlist. These are the layer's first stations in Spain.
+- The tracked-aircraft descriptor now publishes `verticalRateMps`, on both the
+ civil and military layers, so a consumer reading `getTrackedInfo()` no longer
+ has to reach past it into the raw poll record for climb/descent. Reported,
+ never derived: an aircraft that has not transmitted a rate reads as `null`
+ rather than a level-flight `0`.
- Add nine Ontario transit feeds: TTC, MiWay, Hamilton HSR, Durham Region,
YRT/Viva, Guelph, Burlington, Barrie and Kingston, each carrying its own
diff --git a/SECURITY.md b/SECURITY.md
index cc3f27cfe..e53883548 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -73,7 +73,7 @@ The data proxies under `server/providers/` are written so the browser cannot tur
- **Sanitized errors** — internal error details are not echoed back to clients.
- **Coalesced OAuth refresh** and cached successful responses only (OpenSky).
- **Redacted debug logging.** The voice debug log (`.gev-logs/`, gitignored) strips API keys, bearer tokens, client secrets, and image data URLs before writing.
-- **Cross-site gate on cost/log endpoints.** The cost-bearing (`/api/realtime/token`, `/api/openai/hud-summary`, `/api/google/nearby-places`, `/api/google/text-search`), quota-bearing (`/api/mapillary/*`, with a per-IP limit on tiles) and log (`/api/realtime/debug-log`) endpoints refuse cross-site browser requests: a foreign or opaque `Origin`, a `Sec-Fetch-Site` other than `same-origin`/`none` (this is what blocks an `
`/navigation that carries no `Origin`), or any reverse-proxy/CDN forwarding header returns `403`. Non-browser loopback tools (the repo QA harnesses POST the token endpoint with no `Origin`) and the explicit `HOST=0.0.0.0` LAN opt-in keep working — the gate deliberately does not require a loopback remote address; that stricter requirement belongs only to the credential panel (`admitKeySetupRequest`). The Host check (`allowedHosts`, applied by `hostCheckPlugin` ahead of every route, since Vite's own check runs only after plugin middleware) rejects a rebinding `Host` before any handler runs, in every mode, and the gate is defense in depth behind it: the LAN opt-in keeps that check and accepts only IP addresses, `localhost` and names listed in `GEV_ALLOWED_HOSTS`. **Limit:** a name you list there is trusted like the app's own address, so list only names you control.
+- **Cross-site gate on cost/log endpoints.** The cost-bearing (`/api/realtime/token`, `/api/openai/hud-summary`, `/api/google/nearby-places`, `/api/google/text-search`), quota-bearing (`/api/mapillary/*`, with a per-IP limit on tiles, and `/api/tomtom/*`, backed by its persistent daily tile budget) and log (`/api/realtime/debug-log`) endpoints refuse cross-site browser requests: a foreign or opaque `Origin`, a `Sec-Fetch-Site` other than `same-origin`/`none` (this is what blocks an `
`/navigation that carries no `Origin`), or any reverse-proxy/CDN forwarding header returns `403`. Non-browser loopback tools (the repo QA harnesses POST the token endpoint with no `Origin`) and the explicit `HOST=0.0.0.0` LAN opt-in keep working — the gate deliberately does not require a loopback remote address; that stricter requirement belongs only to the credential panel (`admitKeySetupRequest`). The Host check (`allowedHosts`, applied by `hostCheckPlugin` ahead of every route, since Vite's own check runs only after plugin middleware) rejects a rebinding `Host` before any handler runs, in every mode, and the gate is defense in depth behind it: the LAN opt-in keeps that check and accepts only IP addresses, `localhost` and names listed in `GEV_ALLOWED_HOSTS`. **Limit:** a name you list there is trusted like the app's own address, so list only names you control.
- **Full Content-Security-Policy.** The dev and preview servers send a real CSP (`script-src 'self' 'unsafe-eval' blob:` plus the YouTube, Facebook and X scripts the Nepal event's embedded media loads and the Maps JavaScript API that Street View runs on, with a matching `frame-src` for their players and posts: no inline script and no other script origin; `blob:` covers the object URLs Cesium's bundled workers bootstrap through in the built app), alongside the existing `X-Frame-Options: DENY` and `frame-ancestors 'none'`. `'unsafe-eval'` stays because Knockout, bundled inside Cesium's widgets package, resolves the global object with `eval` at load time and the globe does not initialize without it; styles permit `'unsafe-inline'` and Google Fonts because Vite's dev client injects `