A self-hosted custom sync server for kobo devices.

feat: rewrite web UI as a Leptos CSR SPA + guard device-bound token deletion master

Replace the four include_str! HTML pages with a Leptos client-side-rendered SPA in web-ui/ (its own crate + workspace, wasm32 only). Built with Trunk, embedded into the server binary via rust-embed, and served with an index.html SPA fallback. /api/* and /covers now enforce auth per-handler and return 401 (not a redirect) so the SPA gates its own content via /api/me. Hand-written dark CSS, no Tailwind/CDN. The setup page's File System Access + sql.js device I/O stays in a JS glue module driven from Leptos. No change to the Kobo wire contract. Guard Kobo token deletion: DELETE /api/tokens/{id} returns 409 + the bound serial when the token is registered to a device, unless ?force=true is set (a confirmation, not a hard denial, so a lost device's token can still be revoked). The token list shows a device-bound badge (list_auth_tokens LEFT JOINs device_registrations). Nix: add crates.nix to build wasm-bindgen-cli at the exact locked version, build the bundle with craneLib.buildTrunkPackage, and inject it into web-ui/dist for every server crane build. Add deny.toml (explicit license allow-list) and publish=false so cargo-deny 0.20 passes.