The grid was leaving horizontal space unused for two reasons:
1. Width measurement was based on parentRef.clientWidth - PADDING*2,
which is fragile to padding/box-sizing/scrollbar mismatches and was
off by enough pixels in practice to drop a column. Replace with a
1px-tall normal-flow sentinel rendered inside the inner virtualizer
wrapper at the exact horizontal extent rows render at. ResizeObserver
on the sentinel gives the authoritative row width — no padding
subtraction, no scrollbar guesswork.
2. The flex layout with explicit per-cell px widths accumulated floor()
rounding error and let cells drift away from square. Switch to a CSS
grid with fixed-px tracks (`repeat(cols, ${cellSize}px)` + matching
`gridAutoRows`) so every track is exactly cellSize wide AND tall. By
construction `cols × cellSize + (cols-1) × gap == measured width`,
so the row fills edge-to-edge with cells that are guaranteed square.
PhotoThumbnail gains an opt-in `fill` prop the timeline uses to switch
its inline width/height to 100%, so the cell stretches to whatever the
parent grid track gives it. Heap sidebar / non-grid callers still get
explicit `size`-by-`size` square thumbnails as before.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>