ci / bun-nix (pull_request) Successful in 24s
ci / docs-site (pull_request) Successful in 1m13s
ci / web (pull_request) Successful in 3m27s
android / android (pull_request) Successful in 4m2s
ci / rust-arm64 (pull_request) Successful in 4m40s
ci / rust (pull_request) Successful in 10m47s
Hyprland and wlroots both hardcoded portal `CursorMode::Metadata` whenever the session had negotiated the cursor channel, and never asked the backend what it supports. That is not a soft failure: xdg-desktop-portal's FRONTEND validates the requested mode against the backend's `AvailableCursorModes` and fails the call with `"Unavailable cursor mode %x"` before the backend ever sees it. So a cursor-forward session (desktop mouse mode) died at `select_sources`, surfacing as "pipeline build failed" and a black client, with `unavailable cursor mode 4` in the portal log. Field report 2026-08-14. MEASURED on .21 the same day, and it is worse than the report suggested: against a LIVE Hyprland 0.56.2 with xdg-desktop-portal-hyprland 1.4.1 and xdg-desktop-portal 1.22.1 — all current — `AvailableCursorModes` reads **3** (Hidden|Embedded) on both the backend impl interface and the frontend. xdph does not offer the metadata cursor at all, so this broke EVERY cursor-forward session on current Hyprland, not merely on old installs. Updating the portal would not have helped. xdpw is the same from the other end: its screencast.c refuses METADATA outright. pf-capture's own portal path has always negotiated (`choose_cursor_mode`); this restates that ladder in pf-vdisplay, which may not depend on pf-capture. The downgrade is graceful rather than merely survivable: with the portal on Embedded no `SPA_META_Cursor` arrives, so the host feeds the cursor channel nothing and a cursor-forward client draws nothing of its own — one pointer, not two. `PUNKTFUNK_PORTAL_CURSOR_MODE=auto|hidden|embedded|metadata` pins the preference for a backend that advertises a mode it implements badly, which negotiation cannot detect. It is a preference only: pins run the same ladder, so no value can re-create the refused request. The module is declared unconditionally so its ladder tests run on every CI leg rather than only the one that compiles `mod hyprland` — including a Linux-only test pinning our bit values against ashpd's enum, verified non-vacuous by planting a wrong discriminant (ashpd answers 4 for Metadata, the number in the report). The regression test uses 3, the bitfield measured on glass. Linux: 225 tests pass, clippy --all-targets -D warnings clean.