Started the week thinking this would be a two-hour job. A week later I have WireGuard working and a much better understanding of why consumer routers make NAT traversal so much more painful than it needs to be.
The short version: the ER605 does symmetric NAT, which means the source port changes on every outbound connection. WireGuard’s handshake expects the port to stay consistent long enough to complete, and on a symmetric NAT box it often doesn’t. The fix was to set up the WireGuard endpoint on a VPS I already had, and use that as the relay rather than trying to punch through from the ER605 directly.
Not elegant, but it works and I understand exactly why it works, which is more than I could say at the start of the week.