Changes:
1. Article opens INSTANTLY — articleContent initialized from entry's
existing content in init(), not after API fetch. WebView renders
immediately with whatever we have. Full content swaps in silently
when getEntry returns (only if longer than current).
2. markAsRead is fire-and-forget — wrapped in detached Task inside
.task, does not block the content display chain. Toolbar syncs
from vm.entries immediately after.
3. CSS template pre-built as static string in ArticleHTMLBuilder.
Avoids rebuilding ~2KB of CSS on every article open. HTML builder
is a stateless enum with a single static method.
4. Removed isContentReady flag — no longer needed since content is
available from init. Spinner only shows if entry truly has no
content at all (rare edge case).
Flow is now:
tap → ArticleView created with entry.articleHTML →
WebView loads immediately → user can scroll →
background: markAsRead fires, getEntry fetches full content →
if full content is better, WebView updates silently
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>