# 08-May-2026 — GPX downloads, per-list unsubscribe redirect, drop runtime zone warning [#18](https://github.com/nbarrett/ngx-ramblers/issues/18)

## [build 640](https://github.com/nbarrett/ngx-ramblers/actions/runs/25527166309) — [commit 2ba17f6](https://github.com/nbarrett/ngx-ramblers/commit/2ba17f6ac4b9ec21ef0c4c31913ca0ae2bc6ba55)

_____

Three independent strands bundled per request.

## Walks: GPX route download in related links

Adds a `relatedLinkShowGpx` toggle on the Walk Config "Related Links" tab (default true) and renders a "Download GPX route" link when a walk has a `gpxFile`. The link resolves remote URLs vs `resourceRelativePathForAWSFileName` correctly and uses `originalFileName` (or `awsFileName` fallback) as the download attribute, sitting alongside the existing OS Maps / Meetup / What3Words / Venue links.

## Mail: branded unsubscribe per-list redirect (ref #18)

Replaces Brevo's default footer unsubscribe placeholder with a server-issued `UNSUBSCRIBE_URL` injected into `memberMergeFields` at campaign-creation time. The URL points at a new `GET /api/mail/unsubscribe/from-list` endpoint that:

- looks the email up in Brevo to confirm it is on the target list (rejecting stale links cleanly with a 400)
- builds a signed unsubscribe token tied to the listId via `buildUnsubscribeToken`
- redirects to the site's branded unsubscribe page with the token

\`contactUsParentSegment\` is now exported so create-campaign can use it to derive the redirect path. The previous footer template ("If you wish to unsubscribe from our emails, click {here}") is no longer set, since campaigns now carry the URL via the merge field, giving recipients a one-click route into the branded flow that keeps the listId context.

## Cloudflare: drop runtime zone warning (ref #256)

The Inbound Forwarding tab carried two runtime warnings (domain mismatch, stale deploy) added in #256. Both relied on \`cloudflareZoneBaseDomain\` and \`perEnvironmentDbZoneId\` exposed via \`nonSensitiveCloudflareConfig\`, but the underlying signal was never truthful: \`secrets.ts:117\` always pins the encrypted env var's \`baseDomain\` to the global, and the deployed child's own Mongo doesn't carry a copy of staging's \`environments\` doc, so \`perEnvironmentDbZoneIdForRunningApp\` returned empty in production anyway. Setting a per-env zoneId in staging didn't clear the warning even after several deploys.

The presence or absence of a per-env Cloudflare zoneId is itself the whole contract:

- per-env zoneId set -> standalone domain -> rules go to that zone
- no per-env zoneId -> subdomain of the central baseDomain -> rules go to the central zone

Both states are correct by construction; the only invalid configuration ("standalone domain but no per-env zoneId") is a setup-time mistake, not a runtime concern, and belongs in Environment Management validation (follow-up).