aa4c24b974
Adds the listener-side fan-in for the speaker-avatar reaction overlay (T1 #3): recentReactions: StateFlow<Map<String, List<RoomReaction>>> onReactionEvent(event, nowSec, windowSec = 30) evictReactions(olderThanSec) The platform layer is the source — amethyst observes LocalCache for kind=7 with #a=[roomATag] and pipes events here. The 30-s window constant lives next to SPEAKING_TIMEOUT_MS so the staleness configuration is one place. Caller-driven tick (no internal timer inside the VM) keeps the lifecycle aligned with the Composable. Test: * onReactionEventGroupsByTargetAndEvictsOnTick — two reactions on bob arrive within the window; tick advances past the window; overlay clears.