Looking into why the on-screen keyboard's [pressed-key highlight](/posts/the-interface-does-not-end-at-the-board#make-physical-input-visible) needed its own special-case surfaced something bigger: `/pve` and `/pvp` each carried their own copy of a physical-keyboard keydown parser, near-identical to each other and to a third, slightly different one already living inside `OnScreenKeyboard` for its own on-screen clicks. Three parsers doing the same job was the actual reason the highlight had needed a workaround in the first place.

The wayfinder map this went through carried its own execution rather than stopping at a locked spec, since the facts charting turned up were grounded enough that there was nothing left to hand off. The duel pages already passed all four of the same callbacks to `OnScreenKeyboard` for on-screen taps that their own keydown copies called directly for physical typing, and the two parsers weren't quite identical: the duel copies matched a typed letter against a validation regex, where the internal parser matched against the actual rendered keyboard rows, and only the internal parser guarded against a modifier key:

```typescript
if (e.metaKey || e.ctrlKey || e.altKey) return;
```

Its absence on the duel pages meant Ctrl+C during a live game both appended a literal "c" to the input and swallowed the browser's own copy shortcut, a real bug nobody had filed because copying text mid-game is not something anyone tries until they do.

`OnScreenKeyboard` became fully callback-driven, its old fallback imports of the daily store removed, so every page, daily included, now passes the same four callbacks explicitly rather than the component reaching for global state on its own. The two duplicate keydown parsers and the `disablePhysicalKeyboard` prop that had let one page opt out of them came out entirely, leaving one parser as the single source of physical-keyboard behavior across all three modes. Nothing about how the keyboard looks changed, so no gameplay recording needed a re-shoot, and the test suite for the component was rewritten around the new callback shape rather than patched, covering the modifier guard, Norwegian letter routing, and the dialog-aware Escape handling the three old copies had each implemented slightly differently.