882a6bb032
Follow-up to the merged title-autofocus fix (#301). Confirmed via Chrome DevTools on the live app: a residual reload jitter remained — the document renders progressively (measured height 17729 -> 32185, collapsing mid-swap), and the restore fired TOO EARLY (twice, at partial heights) because it only checked "is the target reachable", not "has the layout settled". While the doc grew, scroll-anchoring dragged the position and the second restore yanked it back (the jitter), landing ~6000px off. - restoreScrollPosition now polls the document height and restores ONCE the height has been stable for HEIGHT_STABLE_MS (400ms) AND the target is reachable; the MAX_RESTORE_WAIT_MS (5s) timeout is the only fallback that clamps. Removed the restoreStartRef shared budget; idempotency is now the `pollTimerRef !== null` guard (a running wait suppresses a second trigger). - The two-trigger wiring (early on-mount for the offline path + post-swap) is unchanged; both call the now-settle-waiting, idempotent restore. - A shadow simulation on the live page confirmed the new logic fires once, accurately (vs the old two-fire-plus-drift). - Tests updated for the stable-height timing; (k) rewritten to pin the idempotency guard (mutation-verified); (d3) added for "waits until height stops changing". Tradeoff: on progressively-rendering pages the position now appears once the layout settles (~0.5-2s) in one smooth move, instead of an early-but-jittery, often-inaccurate restore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>