Adds a portal-bar to the REPL between tabbar and transcript: a save
button + chip strip showing all saved checkpoints for the active tab.
Click a chip to restore, click × to delete.
Per-tier strategy:
* c, python — call the tier's (portal-snapshot! NAME), then read the
JSON blob out of MEMFS (Emscripten/Pyodide FS) and stash it in the
encrypted vault entry. Restore reverses: hydrate MEMFS, then
(portal-load! NAME) merges the bindings into the live env.
* asm — no portal serializer in the WAT tier yet (would need a
Cheney-aware walk). Falls back to transcript replay: save snapshots
every successful prior input, restore reboots the tier and re-evals
them in order.
Plumbing:
* Worker bridge: new portal-save / portal-load message kinds wire
MEMFS reads/writes to the main thread.
* runner.js exposes portalSave / portalLoad — null when a tier
hasn't implemented portals (asm stays grey).
* C tier: replace EM_JS with extern + --js-library for js_lumbda_bend_call
(EM_JS-generated declaration was unreachable from wasmImports at
instantiate time, browsers threw "import object field ... not a
Function"). FS added to EXPORTED_RUNTIME_METHODS so JS can reach
pyodide.FS / Module.FS for MEMFS I/O.
Smoke-tested all three tiers headlessly: save → chip render → restore
round-trips clean on c / python / asm, zero console errors.