The Category Picker
The drill-down menu where the player commits to a reason for rejection. The navigation state for one browsing session lives in a CategoryTreeNavigator, another plain C# model: it tracks the current path and any pending leaf selection, and exposes NavigateInto, NavigateUp, SelectLeaf, and ClearPending, firing events as the path and pending selection change. The whole category tree is derived from the error catalogue, not hand-built.
The picker is assembled from small pieces that each listen to the navigator: a breadcrumb of the current path, a list view that rebuilds rows for the current level, and confirm and back buttons. Clicking a folder navigates in; clicking a leaf marks it pending and enables confirm. Confirm writes the categorisation to the session and returns to the item list; back either steps up a level or, at the root, exits to the list.
Every browsing session is constructed with a filter behind a one-method interface. By default it's NoFilter, the player sees the whole catalogue and has to know which reason is correct. If the player has bought the smart-filter upgrade, circled leaves instead get an ApplicabilityFilter that only shows reasons actually applicable to that field, by definition reference or overlapping tag. General issues always use NoFilter.
Clicking an already-categorised row re-opens the picker with a fresh navigator at the root; the player drills down from the top each time rather than being dropped at their prior choice. The previous selection shows as a checkmark badge when they reach it. Confirming a different leaf overwrites it; backing out leaves the prior categorisation intact.