Fourth step of the virtualization plan. Adds `useVirtualizer` wiring
with a DOM-measured `scrollMargin`, gated behind
`viewport.virtualizationEnabled`. Dormant in this release — no caller
flips the flag yet — so behavior is unchanged.
- Imports `useVirtualizer` from `@tanstack/react-virtual`. Fixed
per-kind estimates (`ROW_SIZE_ESTIMATES`) drive `estimateSize`. Keys
come from `row.key`, so row identity matches the renderRows model.
- `shouldVirtualize` requires all of: contract says enabled, a scroll
element ref is present, and there is at least one row. Otherwise the
render falls back to the direct `renderRows.map(...)` path from PR
#72.
- Measures `scrollMargin` via `ResizeObserver` on both the scroll
element and the timeline root, plus `scroll` and `resize` listeners,
all rAF-batched. Avoids hand-summed heights that drift when
composer/status/padding change.
- Virtualized path renders an absolute-positioned list inside a sized
container (`height = getTotalSize()`). `translateY` subtracts
`scrollMargin` so rows align to the timeline's own origin rather
than the scroll container's top.
This PR intentionally does *not* enable `measureElement` (PR #5) or
flip `virtualizationEnabled` for any layout (PR #6) — both rely on
this wiring landing first.