### C FFI / SDK surface

All `wai_*` functions return a `wai_status_t`:

| code | meaning |
|---|---|
| `WAI_OK` (0) | success |
| `WAI_ERR_INVALID_ARG` (1) | null pointer / mismatched length / out-of-range dimension |
| `WAI_ERR_CODEC` (2) | codec-level failure (details in `wai_last_error()`) |
| `WAI_ERR_CONTAINER` (3) | bad magic / truncated envelope / unparseable manifest JSON |
| `WAI_ERR_PANIC` (4) | internal Rust panic caught at the boundary (a wrapper bug — never UB into the C caller) |

Every FFI function is wrapped in `std::panic::catch_unwind` so Rust
panics never unwind into C (UB). `av1_decode` validates header bounds
to prevent libdav1d's C-level `abort()`. `wai_last_error()` is per-
thread, populated on any non-OK return.

