Merge visual feedback: a whole-canvas brightness flash, not a per-position particle burst
Context
Last item in the feature-gap backlog from the prior research pass: real Suika-style clones typically show some visual flourish (a particle burst, a scale pop) at the exact point two pieces merge, on top of a sound effect (decision 0035, already shipped). Investigated what a real per-position effect would require: resolve_all_collisions currently returns #(List(Entity), Int) (entities plus score gained) with no record of *where* any merge happened, only *that* score went up -- exposing merge positions would mean changing that function's return shape and threading it through update/tick, a real signature change cascading through pe.gleam's internals for a purely cosmetic feature.
Options considered
Decision
index.html gained a glemy-merge-flash CSS @keyframes (brightness pulse, 0.2s ease-out) applied via a .glemy-flash class on #glemy-canvas-wrap. game_ffi.mjs's triggerMergeFlash removes then re-adds that class (forcing a reflow via reading offsetWidth in between -- re-adding an already-present class doesn't restart a CSS animation on its own, which would make back-to-back merges within 0.2s only flash once instead of once each). game.gleam's play_sound_event now calls both play_merge_sound and trigger_merge_flash for pe.Merged, still the single place a SoundEvent becomes actual FFI calls.
Verification
161 Erlang / 184 JavaScript gleam test cases passing on both targets, unchanged (no new pure logic -- this is a direct extension of the already-tested Merged event path from decision 0035). Re-ran tools/browser_check.ts: still passes cleanly with zero page errors, confirming the new FFI call and CSS class manipulation don't throw or break the page; the existing click-and-hold sequence is too short (~500ms, decision 0035's cooldown is 0.5s) to reliably force a real merge, so this specifically confirms the code loads and runs without error rather than exercising the flash itself end-to-end -- a reasonable stopping point for a purely cosmetic feature, not extended further to force a merge scenario.
Consequences
Merges now have both audible and visible confirmation without any new pe.gleam state or signature changes. If a per-position particle effect is ever wanted, resolve_all_collisions/update/tick would need to be extended to expose merge positions -- a real, separate, larger decision, not something this change quietly paved the way for by half-building it.