LIVE https://todo-ocaml.bfj.oncarlos.com a24ca85

## Checks

- `make test`: 16 Alcotest tests pass (form parsing, Unicode trimming,
  escaping, and the request handler against an in-memory database).
- Shared suite against `dist/app --listen 127.0.0.1`: 67 passed, 3 skipped
  (the two JS-only edit tests with JS off, and the screenshot with JS off).
- `scripts/boot-check.sh dist/app-linux-arm64`: /healthz and /api/version
  200, clean exit on SIGTERM.
- Deployed with `make deploy`; `/api/version` reports `a24ca85`.
- Shared suite against https://todo-ocaml.bfj.oncarlos.com: 67 passed,
  3 skipped.

## Review fixes (a24ca85)

1. **Unbounded request heads.** `bin/main.ml` now runs cohttp-eio's
   per-connection loop itself instead of `Cohttp_eio.Server.run` (which
   hard-codes `max_size:max_int`). Each connection reads through a byte
   budget: 64 KiB for a request line plus headers, then 1 MiB + 64 KiB
   per request after that, with at most 256 connections. Checked locally:
   a 10 MB header line and 100,000 small headers both get
   `431 Request Header Fields Too Large`, and the process stays at about
   17 MB RSS.
2. **Edits lost on re-render.** `app.js` keeps an open editor across a
   response: the same item stays in edit mode with its unsaved text,
   focus and selection, and losing focus during the swap doesn't count
   as a save. Checked in Chromium with a toggle response held back while
   another item was being edited.
3. **Unicode whitespace.** `Form.trim` strips Unicode White_Space (plus
   U+FEFF) and is used for both adding and editing. A title of only
   non-breaking spaces adds nothing, and editing to one deletes the item.
4. **SQLite from source.** `tools/sqlite.sh` downloads the SQLite 3.54.1
   amalgamation once into `.cache/src` (checked against sqlite.org's
   SHA3-256) and compiles it into `.cache/sqlite-<arch>`. The `sqlite3`
   opam package finds it through `pkg-config`. `make clean-deps` deletes
   those builds and keeps the tarball. The shipped binary contains SQLite
   3.54.1 (source id 2026-10-16), not the arm64 root's 3.46.1.

## Notes

- The shipped binary is static glibc (aarch64), built in the emulated
  arm64 root with Debian's OCaml 5.3.0 as `ocaml-system`. The linker
  warns about `getpwnam`, `gethostbyname` and the like (pulled in by
  OCaml's `unix` library). The app never calls them.
- `make clean-deps && make build-arm64` took 20.5 minutes here: SQLite
  and every OCaml library compiled under qemu. After that, a rebuild of
  the app alone takes about 15 seconds.
- docs-t1 has no `pkg-config`, so the x86 build unpacks Debian's
  `pkgconf` into `.cache/` with `apt-get download` (see README).
