1. Explicit WKProcessPool — static shared instance assigned to
WKWebViewConfiguration. Prevents any future divergence even
though iOS 15+ shares by default.
2. Scroll-preserving content upgrade — when articleContent updates
(partial → full), uses JavaScript DOM replacement instead of
loadHTMLString. Captures window.scrollY before swap, restores
after. No visible flash or scroll jump. Falls back to full
reload if JS replacement fails.
3. No unnecessary reloads — coordinator tracks lastHTML. Only
loads if content actually changed. First article open = full
page load (lastHTML is nil). Content upgrade = DOM swap
(lastHTML exists, new content is different).
4. Clean separation — isUpgrade flag distinguishes first load
from content upgrade. First load uses loadHTMLString (needs
full <html> document). Upgrade uses innerHTML replacement
(preserves scroll, CSS, page state).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>