AnomalyBrowsingService.TryProject fell back to `new SportCode(0)` when an anomaly's
event was missing — but SportCode throws for 0, which would blow up the whole
feed/dashboard for that row. Anomalies have an FK to events so it was dead in
practice, but an orphaned row now degrades gracefully (skipped, like a row with
unparseable evidence). Closes the flagged latent crash.
- TryProject returns false when the event lookup misses; +1 test.
Adds a detector-kind chip row to the anomaly feed (SuspensionFlip / SteamMove /
SuspensionFreeze / OverroundCompression), multi-select like the sport filter — so
with four detectors live you can slice the feed to a single signal type. The kind
set lives on AnomalyFilter and filters in-memory alongside severity/sport, persisted
via AnomalyBrowsingState like the other filters.
- AnomalyFilter.Kinds + AnomalyBrowsingService in-memory Where clause; feed chip
row + ToggleKind/KindLabel; en/ru resx (Anomaly.Filter.Kind).
- 2 tests: kind-filtered subset + no-filter returns all kinds.