Context
The blank-on-cold-start is a whole-page compositing freeze in iOS-26 WKWebView (Edge/Chrome on iOS); Safari works (system WebKit handles the compositing; WKWebView is more sensitive to first-paint compositing-layer pressure). JS can't detect a compositing freeze (paint is recorded — FCP fires — but never composited to screen), so the Paint-Timing auto-reload (#221) doesn't fire. Pivot from recover to prevent: reduce what gets composited at first paint.
What composites before any JS runs (the freeze is at the consent gate, pre-initApp)
#map { isolation: isolate } — a full-screen composited layer present from first paint (empty until the map inits).
- static
#offline-banner — position: fixed + transform: translateY(-100%) → a composited (off-screen) layer.
Changes (aggressive: strip all first-paint compositing)
- style.css: remove
isolation: isolate from #map. Safe: it only contained the hillshade overlay's mix-blend-mode: multiply, which blends against the opaque base tiles beneath it regardless (multiply against the white body behind a full-screen map is a no-op).
- index.html / main.ts: remove the static #offline-banner from the HTML and lazy-create it in
updateOfflineBanner() only when actually offline — so it's not in the DOM at first paint.
If this stops the freeze, we've confirmed first-paint compositing pressure was the trigger and can re-introduce containment more surgically if needed. Refs #220, #218.
Context
The blank-on-cold-start is a whole-page compositing freeze in iOS-26 WKWebView (Edge/Chrome on iOS); Safari works (system WebKit handles the compositing; WKWebView is more sensitive to first-paint compositing-layer pressure). JS can't detect a compositing freeze (paint is recorded — FCP fires — but never composited to screen), so the Paint-Timing auto-reload (#221) doesn't fire. Pivot from recover to prevent: reduce what gets composited at first paint.
What composites before any JS runs (the freeze is at the consent gate, pre-initApp)
#map { isolation: isolate }— a full-screen composited layer present from first paint (empty until the map inits).#offline-banner—position: fixed+transform: translateY(-100%)→ a composited (off-screen) layer.Changes (aggressive: strip all first-paint compositing)
isolation: isolatefrom #map. Safe: it only contained the hillshade overlay'smix-blend-mode: multiply, which blends against the opaque base tiles beneath it regardless (multiply against the white body behind a full-screen map is a no-op).updateOfflineBanner()only when actually offline — so it's not in the DOM at first paint.If this stops the freeze, we've confirmed first-paint compositing pressure was the trigger and can re-introduce containment more surgically if needed. Refs #220, #218.