Layout: agents now arrange in a horizontal grid (3-4 per row based on
viewport width) instead of stacking vertically. Position calculation
uses layoutPositions() which guarantees agents stay within the canvas.
Performance: render loop throttled to 30fps instead of uncapped 60fps.
Removed Math.random() from render path — building window lights now use
a deterministic hash that changes slowly. Canvas dimensions only resize
when clientWidth/clientHeight actually change.
Crash fix: clearRect at frame start prevents accumulated draw state.
setTransform instead of scale prevents compounding transforms. Animation
cleanup properly cancels requestAnimationFrame on unmount.