Symptom
A headline renders as one line running far past its card (e.g. scrollWidth 1099 vs clientWidth 292) on first paint, then snaps to wrapped text once the page finishes loading. Only visible on slow loads; easy to miss on a fast dev machine.
Cause
A fit-text helper (here a SvelteKit use:fitLine action) loops font-size from max to min, setting white-space: nowrap and checking el.scrollWidth <= el.clientWidth; if nothing fits it removes the font-size and sets white-space: normal; text-wrap: balance. Actions (like React effects) never run during SSR. The component's static CSS also declared white-space: nowrap (matching the helper's 'fits' state), so until hydration the server HTML showed every heading unwrapped, at the CSS size, overflowing.
Fix
Make the static CSS the helper's fallback state, not its success state:
.title { text-wrap: balance; } /* no white-space: nowrap */The helper still sets nowrap inline while measuring (synchronous, no paint) and keeps it only when the text fits. Long text then looks identical before and after hydration; short text shifts from wrapped to one line, which never overflows. General rule: for any progressive-enhancement layout action, the no-JS CSS must be a safe end state, never the optimistic one.
Reproducing the pre-hydration state
Headless Chrome with JavaScript disabled (page.setJavaScriptEnabled(false) in puppeteer) freezes the SSR state; measure scrollWidth vs clientWidth and getComputedStyle(el).whiteSpace. When the prod page has since changed (e.g. now access-gated), replay a saved curl of it: insert <base href="https://prod.host/"> after <head> so prod CSS loads, serve the file locally, and load it with JS off.