Clicking "update & rebuild" (and cancel / respawn / purge) on /core.html
navigated to the bare `ok` response page instead of submitting async. The
C0R3 page (split out of the dashboard) carries `data-async` forms but its
bundle never had the global submit interceptor — that handler lived inline
in tabs.js, so only the dashboard bundle had it. The forms POSTed natively
and the browser followed the response.
Fix: lift the `data-async` submit handler out of tabs.js into a shared
`bindAsyncForms(onSuccess)` in common.js (which already owns the `form`
helper that builds these forms), and call it from both pages:
- tabs.js: `bindAsyncForms(() => refreshState())` — behaviour-preserving
(same handler, now imported).
- core.js: add a `refreshState()` (re-fetch /api/state + re-render) used
for the cold load and as the post-submit refresh, and call
`bindAsyncForms(() => refreshState())` at boot.
Forms with `data-no-refresh` (e.g. meta-update, which gets its update via
the meta SSE events) skip the refresh, same as before.