User-reported issues after the E5 build:
- useCrossfadeBackdrop: the static branch (one image / crossfade off /
reduced motion) painted #root but returned NO cleanup, so the backdrop
persisted after leaving the page (e.g. switching modules). Every painted
branch now returns clear() so unmount always resets the default backdrop.
- scheduler: give the tmdb_enrich job a proper label + description (was the
raw "tmdb_enrich" id) in EN/HU.
- CastStrip: cap crew names to ONE line (truncate) so the row of circular
photos sits on one baseline instead of going wavy on 2-line names.
- PlexInfo: lay Details / Keywords / Crew out as side-by-side boxes
(items-start grid) instead of full-width stacked panels with dead space;
narrower Crew grid for the boxed width; each panel independently hideable
from the customize menu (new Details/Keywords/Crew toggles). Tighter
vertical spacing.
- MultiSelectChips: reshape the trigger from a "+Add" button into an
Overseerr-style combi input — removable chips inside a typeable field that
filters the checklist, opening on focus (backspace removes the last chip).
- Discover / person rails: reduce the vertical gap between rails.
The faint HTPC art backdrop now slowly cross-dissolves through a pool of images
instead of showing one static art — the last E5 sprint.
- useCrossfadeBackdrop (replaces useArtBackdrop): a double buffer — the SETTLED
image lives on #root's background (the proven full-viewport layer); the next
image cross-fades in on a z-index:-1 overlay layer, then is committed and the
overlay reset. ⭐ #root is given `isolation: isolate` so it's a stacking context
— without it the z:-1 overlay paints BEHIND #root's own background and is
invisible (found + fixed live: a red test layer + a real TMDB backdrop both
only showed once #root was isolated).
- Art pool: the item's own art + its TMDB backdrops (E5 S2, rendered straight
from the TMDB CDN — the CSP allows it) + its collection siblings' art; the show
page adds related art; the PERSON page cross-dissolves through the person's
films' art (the roadmap's signature touch).
- HARD-OFF under prefers-reduced-motion (static, no overlay) + a new usePref
"Crossfade backdrop" toggle in the detail customize menu (fade 1.6s / 7s cycle).
Wired into PlexInfo, PlexShowView, PlexSeasonView, PlexPersonView. Tests:
plexBackdrop.dom.test.tsx (5: paint+isolate+overlay, reduced-motion off, single
image, animate=false, disable/unmount cleanup). Browser-E2E'd (isolation +
overlay render + a real backdrop crossfade + the toggle). Gate: vitest 347,
backend lane 403 (unchanged — FE only).
▶ E5 · Plex Experience epic COMPLETE (S1–S8).
The person page now leads with a TMDB bio, lazily fetched the first time it's
opened (off the S2-linked `tmdb_person_id`) and then served from the row:
- Migration 0072: ADD-only nullable person columns on plex_people — biography,
birthday, deathday, place_of_birth, known_for_department, also_known_as,
known_for ([{tmdb_id, media_type, title}]), tmdb_enriched_at.
- tmdb.py: TmdbClient.fetch_person (/person/{id}+combined_credits), parse_person
(bio + a popularity-ranked, deduped known_for), apply_person, enrich_person
(lazy one-shot, key+id guarded, commits, 404 stamps the watermark).
- people.py person_detail: loads the full person row, lazily enriches (best-effort
— never fails the page), serves the bio fields + resolves known_for to library
rows by guid (in-library only, popularity order).
- FE PlexPersonView: bio/birth-facts/aka hero + a "Known for" Carousel rail above
the existing Movies/Shows filmography; PlexPersonDetail type + i18n (en+hu).
Tests: test_plex_person_bio.py (6: parse / lazy-enrich guards / serve) + extended
PlexPersonView.dom.test.tsx + the unknown-name contract in test_plex_person.py.
Browser-E2E'd on Jim Carrey (live TMDB enrich → bio 1937 chars, born 1962-01-17
Newmarket, Known-for resolved). Gate: vitest 337, backend lane 402.