Scroll-scrubbed comparison timelines: play the two rows one after another, not in parallel
Problem: a Judgment-Labs-style two-row comparison timeline ("before" vs "with the product": blocks light up under a scan sweep, annotation cards pop) played both rows simultaneously on entering the viewport. The founder found it impossible to track two rows at once.
Fix that worked: pin the section (height ~280vh, sticky 100dvh stage) and drive it from scroll progress p. Row 1 gets t1 = seg(p,.04,.46)*T, row 2 gets t2 = seg(p,.54,.96)*T, with a dead zone between so there is a clear hand-off. Row 2 sits at opacity .38 (CSS transition) until p>0.5, then goes full. Keep one shared timeline function (same column geometry for both rows) and tag every animated element with its row (el.closest(".r2") ? 2 : 1) so render(p) just indexes X[row]/T[row]. Give each row its own scan hairline. Reduced motion and <860px render p=1 (final state) instead of scrubbing. Also expose a hash override (#sk=0..1) so headless screenshots can capture any frame synchronously, since headless Chrome does not run rAF/IO reliably.