v0.4.0 — geo redesign + #33 bootstrap fix, backup/air-gapped fixes, feature-matrix e2e gate

Validated by a new 8-scenario feature-matrix e2e on a real backend (#18) — RWO
enforcement, HA, CCM, geo, rolling minor upgrade, etcd backup/restore, pre-baked
images, air-gapped — now a required MR gate. It surfaced every fix below.
Closes #33.

BREAKING — geo templates and the CNI add-on:
- The `geo-multilocation` flavor is gone. One `cluster-template-geo.yaml` now
  declares 3 sites; whether they share a location or straddle datacenters is a
  per-site property, not a different cluster shape.
- Geo variables are per-site: GEO_SITE{1,2,3}_{LOCATION,IMAGE_ID,EXTNET_ID}
  replace GEO_LOCATION, GEO_PRIMARY_LOCATION, GEO_PAIRED_LOCATION,
  VCO_IMAGE_ID_PRIMARY, VCO_IMAGE_ID_PAIRED, VCO_PRIMARY_EXTNET_ID and
  VCO_PAIRED_EXTNET_ID.
- Geo site keys are positional (site-1..3), independent of how cloudspaces map
  to locations — so CAPI failure-domain names and the per-site DNS records
  <cluster>-site-N.<zone> change.
- The `cni-geo` bundle is gone: one geo-tuned `cni.yaml` (veth_mtu 1350) serves
  every cluster, and the geo/ccm/backup/airgapped templates label clusters
  `cni: calico` instead of `cni: calico-geo`.

Fixes:
- geo (#33): `kubeadm init` on the bootstrap node no longer waits on the
  health-aware control-plane endpoint — the init node pins the endpoint name to
  its local apiserver in /etc/hosts, so wait-control-plane succeeds and the
  post-init addon phases (kube-proxy, CoreDNS) actually run. A cross-datacenter
  geo cluster whose bootstrap CP landed outside the primary location used to
  hang until the 60m timeout.
- geo: the endpoint A record is published only once a site is genuinely healthy
  (NXDOMAIN until then) instead of being parked at the primary cloudspace IP, so
  the first address anyone resolves is always a live control plane — no stale
  placeholder for a caching resolver or a pinned CAPI client to latch onto.
- backup: snapshots run as root, status.backup records the newest uploaded
  snapshot (key + time), and the restore path accepts a full age identity file
  rather than only a bare AGE-SECRET-KEY line.
- air-gapped: ships a seeded zero-egress registry image, and ensuring the
  registry's certificate tolerates a 400 "already exists" from the backend.

Also: new `capvco-imageresolve` CLI (resolve a vm-image id by name, per
location). CRD schemas are unchanged — contract stays v1beta2; the base and
prebaked templates are untouched.