Rows are created once and kept. Each frame only sets the fill's
transform: scaleX(), which the compositor handles without layout, so the
bars now glide with the curves instead of stepping every 500ms. Text is
refreshed at 10Hz — faster is unreadable and each change relayouts its
row. Truncation measures through one cached context; the throwaway
canvas per row per frame is gone. Measured: 0.007ms per bar update,
0.011ms per text update, down from 0.64ms per rebuild.