ROADMAP owner column 's8n' (was 'claude'). Audit-run-by lines in
docs/{11,14,16} reattributed to s8n. Removed CLAUDE.md memory ref
from docs/04 hosts-pin note.
34 KiB
14 — Theme Audit + Detail-Page Backdrop Diagnosis
Status: read-only audit, executed 2026-05-08 against
https://arrflix.s8n.ru (Jellyfin 10.10.3 on nullstone). The owner has
just rolled back to Cineplex v1.0.6 (the Netflix-faithful theme)
after a brief ElegantFin → NeutralFin experiment that was documented in
docs 04 §3e and 11 respectively. Reported issue: on detail pages the
backdrop image leaves a visible vertical black band on the left where
the title/info column sits. Owner asked for a forward plan, not a fix.
No state mutated. No POST to
/System/Configuration/branding, no edit to/jellyfin/jellyfin-web/index.html, no docker action. Read-only over SSH and against the public/Branding/Configuration
- authenticated
/System/Configuration/brandingendpoints.
1. Current state inventory
1a. Active theme
/System/Configuration/branding returns:
| Field | Value |
|---|---|
LoginDisclaimer |
"Welcome to ARRFLIX - Private invite only service" |
SplashscreenEnabled |
true |
CustomCss (size) |
25 225 chars (most of which is the embedded ARRFLIX wordmark data-URL — twice) |
Sole @import line:
@import url("https://cdn.jsdelivr.net/gh/MRunkehl/cineplex@v1.0.6/cineplex.css");
Cineplex itself transitively imports
cineplex@v1.0.5/finity-theme/finity-complete.css (its parent theme,
Finity by prism2001). This matters for the backdrop diagnosis below.
1b. CustomCss block inventory (every rule, in order)
!important declarations: 17. #E50914 occurrences: 0 in
CustomCss; 1 in web-overrides/index.html critical-path <style>.
ARRFLIX wordmark PNG: 235 × 85 px (aspect 2.765 : 1), embedded
as base64 data-URL on two selectors.
| # | Block | Selectors | Purpose | !important count |
|---|---|---|---|---|
| 1 | Cineplex import | @import |
Theme entry point | 0 |
| 2 | Cast/Crew hide | #castCollapsible, #guestCastCollapsible |
Drop reviewer cruft | 1 |
| 3a | ARRFLIX logo (img) | .adminDrawerLogo img |
content: replace src in admin drawer |
1 |
| 3b | ARRFLIX logo (div) | .pageTitleWithLogo |
background-image: for masthead <div> |
1 |
| 4 | Quick Connect hide | .btnQuick |
Belt-and-braces for the server-side disable in 04 §4g | 1 |
| 5 | Header icon hide | .headerSyncButton, .headerCastButton, .headerUserButton |
Keep only Search top-right | 3 |
| 6a | Slider thumbs (white) | .MuiSlider-thumb, .osdPositionSlider .MuiSlider-thumb, .osdVolumeSlider .MuiSlider-thumb, emby-slider .sliderThumb |
OSD scrubber + volume circles | 3 |
| 6b | Slider thumbs (focus halo) | .MuiSlider-thumb:hover/:active/.Mui-focusVisible |
Hover ring | 1 |
| 7a | Pure-black bg (vars) | :root { --primary-background-color/--background-color: #000 } |
Force shell vars to true black | 2 |
| 7b | Pure-black bg (wrappers) | html, body, .preload, .skinBody, .mainDrawerHandle |
Anti-flash on shell wrappers | 1 |
| 7c | Pure-black bg (containers) | .skinHeader, .skinHeader.semiTransparent, .skinHeader.skinHeader-withBackground, .mainAnimatedPages, #reactRoot, .dashboardDocument |
Container surfaces | 1 |
| 8 | Settings drawer hide | a[href*="mypreferencesmenu"], [to="/mypreferencesmenu.html"] and :has() parent variants × 7 |
Remove Settings link from drawer | 1 |
| 9 | Count-badge hide | .countIndicator |
Drop unwatched-episode badges | 1 |
1c. Critical-path inline <style> (in web-overrides/index.html)
Bind-mounted at /jellyfin/jellyfin-web/index.html, paints before the
SPA bundle loads CustomCss:
| Block | Effect |
|---|---|
:root { --primary-background-color: #000; --background-color: #000 } |
Pre-paint shell vars (no !important) |
html, body, .preload, .skinBody, .skinHeader, #reactRoot, .mainAnimatedPages { bg:#000 !important; color:#fff !important } |
Anti-flash + force colour |
.raised, .button-submit, .emby-button[type=submit], button[type=submit] { bg:#E50914 !important; color:#fff !important } |
Pre-paint Netflix-red on submits (login Sign-In) |
.splashLogo { animation: fadein .5s; width:30%; height:30%; bg-image:<ARRFLIX wordmark data-URL>; bg-size:contain; bg-position:center; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%) } |
The pre-bundle splash screen |
@media (min-device-width:992px) { .splashLogo { bg-image:<same ARRFLIX wordmark, full-res copy> } } |
Desktop variant (currently identical bytes — see §6) |
Plus 78 lines of inline <script> (ARRFLIX-SHIM) that locks
document.title, the favicon, and continuously hides any
mypreferencesmenu drawer entry that might be rendered after navigation.
None of the JS touches detail-page layout.
2. Detail-page backdrop diagnosis
2a. Selector hunt against the live JF 10.10.3 web bundle
docker exec jellyfin grep -oE against
/jellyfin/jellyfin-web/main.jellyfin.1ed46a7a22b550acaef3.css and
itemDetails-index-html.ca5f15ff794311af00a6.chunk.js returned the
canonical detail-page selector set:
| Selector | Where defined | Stock JF 10.10.3 layout |
|---|---|---|
.itemBackdrop |
main.jellyfin.<hash>.css |
height: 40vh; width: <inherited>; background-size: cover; background-attachment: fixed; position: relative; — only top 40vh of the page |
.layout-mobile .itemBackdrop |
same | background-attachment: scroll; background-position: top |
.layout-tv .itemBackdrop |
same | display: none |
.detailPageContent |
same | display: flex; flex-direction: column; padding-left: 32.45vw (LTR desktop) — i.e. the content column starts 32.45% from the left |
.detailPagePrimaryContainer |
same | display: flex; align-items: center; z-index: 2; desktop adds padding-left: 32.45vw |
.detailImageContainer .card |
same | position: absolute; top: -80%; left: 3.3%; width: 25vw (desktop) — the poster card sits in the LEFT column |
.detailLogo |
same | position: absolute; top: 10vh; right: 25vw; width: 25vw; height: 16vh; background-size: contain |
.detailRibbon |
same | desktop: height: 7.2em; margin-top: -7.2em (the gradient fade strip below backdrop) |
.itemBackdropProgressBar |
same | position: absolute; bottom:0; left:0; right:0 |
.detailPageWrapperContainer |
same | border-collapse: collapse |
There is no itemBackdropFader, no itemHeroSection, no
backdropHeroSection selector in the bundle. The owner's mental model of
"a fader covering the left column" doesn't match — the architecture is
positional offsets, not an overlay.
2b. What Cineplex/Finity overrides
grep -nE "itemBackdrop|detailPagePrimary|detailPageContent|detailLogo|detailImageContainer|detailRibbon|detailPageWrapper" /tmp/cineplex.css /tmp/finity.css shows:
cineplex.css — only two detail-page rules, both of them
mobile-only. No desktop override of .itemBackdrop.
/* line 577 */
.layout-mobile .itemBackdrop {
margin-top: 0rem;
mask-image: linear-gradient(to top, #fff0 1%, #000 15%, #000 80%, #fff0 100%);
}
finity-complete.css — Finity is where the detail-page layout is
heavily redesigned. Key block:
/* finity.css :root */
--detail-page-side-padding: 5%;
--detail-page-primary-width: 45%;
--detail-page-backdrop-offset: 17%; /* <-- THE BLACK BAND */
--detail-page-backdrop-width: 85vw;
--detail-page-mask-offset: 16%;
--detail-page-mask-width: 85vw;
--detail-page-content-offset: -65vh;
.layout-desktop .itemBackdrop {
background-attachment: scroll;
background-position: center;
background-size: cover;
height: 100vh; /* full viewport, NOT 40vh — Finity expands JF default */
width: 100%;
}
.backdropContainer {
height: 100vh;
left: var(--detail-page-backdrop-offset); /* 17% */
position: absolute;
top: 0;
width: var(--detail-page-backdrop-width); /* 85vw */
z-index: 0;
pointer-events: none;
}
.layout-desktop .backgroundContainer.withBackdrop {
background: url("https://raw.githubusercontent.com/prism2001/finity/main/assets/mask.png");
background-size: cover;
height: 100vh;
left: var(--detail-page-mask-offset); /* 16% */
width: var(--detail-page-mask-width); /* 85vw */
z-index: 1;
pointer-events: none;
}
.layout-desktop .detailImageContainer .card { display: none; } /* hide poster card */
2c. Root cause
The "black band on the left" is Finity's intentional design, not a Cineplex bug and not a JF stock layout artefact:
- Stock Jellyfin:
.itemBackdropisheight: 40vhand full-width (widthis inherited from the parent flow). The backdrop crops the top of the page, the info column lays out below it. No left band. - Finity: re-engineers the page so
.itemBackdropis100vhbut positions a separate.backdropContainerabsolutely atleft: 17% width: 85vw(so the right ~98% of the viewport gets the backdrop and the left 17vw / 17% is left clear). On top of that, a blurredmask.pngis overlaid atleft: 16%to fade the right edge of the remaining clear band into the backdrop — making the band look like a designed gradient sidebar, NOT a black bar.
The reason it currently reads as a hard black band rather than a soft gradient fade is the combination of two of our personal tweaks plus one Finity asset that may not be reaching the browser:
html, body, .preload, .skinBody, .mainDrawerHandle { bg:#000 !important }forces the underlying surface where the band sits to pure black. Finity's--theme-background-color: #181818is the intended surface — slightly less harsh.#reactRoot, .mainAnimatedPages, .dashboardDocument { bg:#000 !important }does the same for the SPA wrappers above the body.- The Finity mask overlay
(
.backgroundContainer.withBackdrop) loads its mask PNG fromraw.githubusercontent.com/prism2001/finity/main/assets/mask.png— on a LAN with no upstream proxy that should resolve, but if the browser blocks third-party image loads (some ad-blockers stripraw.githubusercontent.comrequests) the mask never paints and the 17vw band is unmasked. Worth a DevTools network-tab check before any CSS change.
Net: the backdrop is filling the right 85vw of the viewport. The
left 17vw is intentionally clear so the title/poster/info column has a
high-contrast surface to render on. Our bg:#000 !important rules turn
that intentionally-clear surface into a hard black band; without them
it would be #181818 with a soft gradient fade from the mask PNG.
2d. Forward-plan CSS (DO NOT APPLY)
If the goal is Netflix-style full-bleed backdrop with a left-side gradient overlay (info column floating over a darkened-but-visible backdrop), the proposed rule set is:
/* Detail-page backdrop: full-bleed + left gradient overlay
(proposal — not applied) */
/* 1. Stretch the backdrop container across the full viewport
instead of starting at 17vw */
.layout-desktop .backdropContainer {
left: 0 !important;
width: 100vw !important;
}
/* 2. Replace Finity's mask.png with a CSS-only linear gradient
that darkens the left 40-50vw and fades to transparent.
`.backgroundContainer.withBackdrop` is the overlay layer. */
.layout-desktop .backgroundContainer.withBackdrop {
background: linear-gradient(
90deg,
rgba(0, 0, 0, 0.95) 0%,
rgba(0, 0, 0, 0.85) 25%,
rgba(0, 0, 0, 0.55) 45%,
rgba(0, 0, 0, 0.20) 65%,
rgba(0, 0, 0, 0.00) 85%
) !important;
left: 0 !important;
width: 100vw !important;
}
/* 3. Drop the global black-bg force from the wrappers ON DETAIL
PAGES ONLY so the gradient composes against the actual
backdrop, not pure black. Scope by .itemDetailPage body class
that JF adds on detail routes. */
body.itemDetailPage,
body.itemDetailPage #reactRoot,
body.itemDetailPage .mainAnimatedPages {
background-color: transparent !important;
}
The 90deg, 95% → 0% gradient is the Netflix.com detail-page recipe:
opaque on the left where the title sits, fades to transparent by ~70vw
so the right side of the backdrop is visible at full brightness. Tune
the stop percentages once live — the sweet spot depends on
--detail-page-primary-width (Finity ships 45%).
Untested side-effect to watch for: Finity also hides the poster
card with .layout-desktop .detailImageContainer .card { display:none }.
That means we have NO poster in the left column today — the current
black band is empty space framing a clear logo + title block. The fix
above would put the title text directly over the backdrop, which is
fine on most artwork but may have legibility issues on bright/busy
backdrops. If owner wants the poster back, drop that Finity rule too.
2e. Screenshot reference
A capture of https://arrflix.s8n.ru/web/#/details?id=324f75b84f394a5d9b0749c0679f23b9
(Rick & Morty S01E01 "Pilot") with a hard browser reload would show:
- Top: ~17vw black/empty band on the left, Rick & Morty backdrop on the right ~83vw. (Finity / current.)
- Title "Pilot" + Series logo + Play button float over the empty band.
- After fix: title floats over a darkened-but-visible portion of the same backdrop, gradient eases into the un-darkened backdrop on the right ~30%.
Owner has not provided a current screenshot in this audit; capture recommended before any CSS change so before/after is documented.
3. Theme survey 2026-05
Surveyed candidates (live as of audit date), scored on Netflix fidelity, monochrome fidelity, recency, JF 10.10.3 compatibility, import format, license:
| Theme | Last commit | License | Netflix fidelity | Monochrome fidelity | JF 10.10.3 compat | Import | Notes |
|---|---|---|---|---|---|---|---|
| Cineplex v1.0.6 (current) | 2025-09-06 | MIT | 9/10 — true #E50914, Netflix Sans webfont, scale-hover, login backdrop |
2/10 | YES (verified live) | single @import (transitively pulls Finity) |
Bus-factor 1 (single author MRunkehl, 0 stars). Inherits Finity's left-band detail-page layout. |
| ElegantFin v25.12.31 | 2026-04-30 | GPL-2.0 | 5/10 — Jellyseerr blue/violet by default, recolour-able to #E50914 (eight --var overrides documented in 04 §3e) |
5/10 | YES (tested 10.11.5) | single @import |
Most actively maintained CSS theme in the ecosystem. Detail-page backdrop is full-width with a gradient overlay built in — no left band. |
| NeutralFin v1.3.0 | 2025-11-24 | GPL-2.0 | 1/10 (mid-grey accents, no red) | 9/10 — #131313 → #1e1e1e gradient, mid-grey accents, off-white text |
YES (tested implicitly via ElegantFin parent) | single @import |
Fork of ElegantFin. The "didn't look as good" feel was caused by our bg:#000 !important rules clamping its #131313→#1e1e1e gradient flat (see doc 11). With those dropped it would render correctly. |
| Theme Park (jellyfin pack) | active | GPL-3.0 | n/a — no Netflix preset (only aquamarine/hotline/dracula/dark/organizr/space-gray/plex/nord) | varies by preset | likely | single @import url(theme-park.dev/css/base/jellyfin/<NAME>.css) |
DQ for our brief; closest is plex (orange/black) but that's a different brand entirely. |
| JellyFlix (prayag17) | 2023-12-20 | none | 9/10 — origin of the genre | 1/10 | HALTED (README header) | single @import |
DQ — explicitly halted, broken on JF 10.11, risky on 10.10.3 |
| DarkFlix v5.1 | 2024-06 | GPL-3.0 | 8/10 | 1/10 | only declares 10.8.x; requires 67% browser zoom | single @import |
DQ — accessibility issue, no 10.10 statement |
| Ultrachromic (CTalvio) | "selectively maintained" — 146 commits, no recent date | MIT | 6/10 (accent-tunable) — three presets: Monochromic, Kaleidochromic, Novachromic | 8/10 (Monochromic preset) | unspecified | single @import per preset |
"Old, passively maintained." No Netflix preset, but Novachromic accepts custom accents — could be set to #E50914. |
| Finity (prism2001, Cineplex's parent) | 2026-05 (active) | none stated | 6/10 (dark, modern, no Netflix red by default) | 5/10 | unspecified | single @import |
Fully responsible for the detail-page layout we see on Cineplex. If the backdrop fix lands, we'd be fixing Finity's .backdropContainer rules. |
| abyss-jellyfin (AumGupta) | 2026-05 | n/a | 1/10 | 7/10 | unspecified | unknown | "Minimal dark." 290 stars, growing. Not Netflix-flavoured. |
| FossFlix (PaleCache) | 2026-01 | n/a | 6/10 (claims Netflix UI similarity) | 1/10 | unspecified | unknown | 1 star, unproven. Worth bookmark, not migration. |
| JellyFin (n00bcodr) | 2026-05 | n/a | 0/10 | 6/10 | unspecified | unknown | Inspired by Flow + Zesty — neither fits the brief. |
| JellyThemes (kingchenc) | 2026-01 | n/a | 0/10 | varies (six dark themes with glassmorphism) | unspecified | unknown | DQ for Netflix brief. |
| Hybrid: Cineplex + NeutralFin tweaks | n/a | derivative | 7/10 | 4/10 | YES if grafted carefully | one @import + tweaks |
Not actually possible to graft cleanly — Cineplex's red and NeutralFin's grey both define --theme-accent-color / --uiAccentColor at :root, last-write-wins. Picking the import = picking the palette. Ranges of personal-tweak overrides (e.g. .MuiSlider-thumb:white) DO survive across both. |
3a. Verdict on Theme Park
docs.theme-park.dev/themes/jellyfin/ lists eight presets: Aquamarine,
Hotline, Dracula, Dark, Organizr, Space-gray, Plex, Nord. No Netflix
preset. The closest cousin (hotline) is a magenta/cyan synthwave
look, not Netflix-red. Theme Park is therefore not a viable migration
target for the ARRFLIX brand; ruled out.
4. Personal-tweak portability matrix
For each personal-tweak block in current CustomCss, classify the
selector as theme-independent (generic Jellyfin selector, survives
any swap) vs theme-specific (requires re-targeting).
| # | Block | Selector | Type | Cineplex | ElegantFin | NeutralFin | Theme-Park | Portability |
|---|---|---|---|---|---|---|---|---|
| 2 | Cast/Crew hide | #castCollapsible, #guestCastCollapsible |
Generic JF id | works | works | works | works | HIGH |
| 3a | Logo (admin) | .adminDrawerLogo img |
Generic JF class | works | works (per 04 §3e — verified 0 ElegantFin matches) | works (no NeutralFin matches) | works | HIGH |
| 3b | Logo (masthead) | .pageTitleWithLogo |
Generic JF class | works (with bg-image, NOT content:) |
works (verified) | works | works | HIGH |
| 4 | Quick Connect hide | .btnQuick |
Generic JF class on <button> |
works | works | works | works | HIGH |
| 5 | Header icons hide | .headerSyncButton, .headerCastButton, .headerUserButton |
Generic JF classes (verified in 73233.*.chunk.js) |
works | works | works (NeutralFin sets width/height/border on .headerUserButton but display:none overrides those) |
works | HIGH |
| 6 | Slider thumb white | .MuiSlider-thumb + variants |
MUI runtime class | works | works | works (theme doesn't theme MUI sliders) | works | HIGH — but consider re-tinting on monochrome themes |
| 7a | Bg vars :root |
--primary-background-color, --background-color |
Jellyfin shell var | works (Cineplex defaults to #181818 — we override to #000) |
works | HARMFUL on NeutralFin — clamps the #131313→#1e1e1e gradient (see doc 11 row 8) |
works | MEDIUM — survives technically, but defeats NeutralFin's intent. |
| 7b/7c | Bg wrappers (html, body, .skinHeader, .mainAnimatedPages, #reactRoot, .dashboardDocument) |
Jellyfin shell wrappers | works (Cineplex doesn't theme these) | works (ElegantFin uses translucent wrappers — #000 underneath is fine) |
HARMFUL — clamps gradient + flattens .skinHeader.semiTransparent (see doc 11 row 10) |
likely works | MEDIUM — and harmful on detail pages for Cineplex (this is what's making the 17vw band hard-black, see §2c above) | |
| 8 | Settings drawer hide | a[href*="mypreferencesmenu"], [to="/mypreferencesmenu.html"], :has() parents |
JF route + MUI ListItem classes | works | works | works | works | HIGH (if browser supports :has()) |
| 9 | Count badge hide | .countIndicator |
Generic JF class | works | works | works (NeutralFin themes it, but display:none wins) |
works | HIGH |
| index.html | Anti-flash inline | html, body, .preload, .skinBody, .skinHeader, #reactRoot, .mainAnimatedPages |
Same wrappers as 7b/7c, but pre-bundle | works | works | HARMFUL — same issue as 7b/7c, but earlier in load (see doc 11 row 14) | likely | LOW-MEDIUM — needs !important removed and .skinHeader dropped from the list to be theme-portable |
| index.html | Submit-button red | .raised, .button-submit, .emby-button[type=submit], button[type=submit] |
Generic JF + MUI button classes | works (matches Cineplex's #E50914 accent) |
requires recolour-aware ElegantFin (works since override is in our hands) | HARMFUL — paints every submit Netflix-red over a monochrome theme (see doc 11 row 15) | works | LOW — rule is brand-specific, must be removed when brand colour changes (NeutralFin would need --btnSubmitColor instead) |
| index.html | ARRFLIX shim (title/favicon/mypreferencesmenu) |
inline <script> |
Independent of theme | works | works | works | works | HIGH |
| index.html | Splash logo | .splashLogo |
Pre-bundle JF class | works | works | works | works | HIGH |
Summary: 11 of 14 blocks are HIGH portability (theme-independent generic JF selectors). The 3 problem children are all variations of "force pure black background" — and they happen to be the same blocks flagged in doc 11 as harmful to NeutralFin AND, per §2c above, to be the cause of the hard-black detail-page band on Cineplex.
Operational rule: when swapping themes, audit blocks 7a / 7b / 7c / index.html-anti-flash / index.html-submit-red FIRST. The other tweaks ride along automatically.
5. Logo aspect-ratio fit
ARRFLIX wordmark PNG: 235 × 85 px, aspect 2.765 : 1.
| Container | Selector | Sizing on Cineplex/Finity | Wordmark fit |
|---|---|---|---|
| Admin drawer | .adminDrawerLogo img |
<img> element, content: swap, sized by sidebar (~240px wide) |
natural — replacement is the displayed image |
| Masthead | .pageTitleWithLogo |
<div>, bg-image + bg-size: contain (Finity convention) |
aspect preserved by contain, no squish |
| Detail page logo | .detailLogo |
position: absolute; right: 25vw; top: 10vh; width: 25vw; height: 16vh; bg-size: contain |
per-show clear-logo box. ARRFLIX wordmark is not used here — this is the show's clear-logo (e.g. Rick & Morty title art). Not a fit concern for our wordmark. |
| Splash | .splashLogo |
width:30%; height:30%; bg-size:contain; centered |
aspect preserved; on a 1920×1080 viewport renders ~576×324 box, wordmark settles at ~576×208 (height-limited by aspect). Looks correct. |
Verdict: 235 × 85 fits cleanly in every container. Aspect ratio is
NOT a factor in any of the rendering complaints. The native JF
admin-drawer + masthead use bg-size: contain, so a 2.765:1 wordmark
displays without distortion regardless of theme.
6. Pre-bundle splash quality
Inspecting web-overrides/index.html (93 lines, the bind-mounted
override of the JF web shell):
| Aspect | Value | Notes |
|---|---|---|
body { background: #000 } (declared in critical-path <style>) |
YES | Anti-flash baseline |
.splashLogo size |
width:30%; height:30% |
Centred via position:fixed; top:50%; left:50%; transform:translate(-50%,-50%) |
.splashLogo bg-image |
inlined data-URL of the 235 × 85 ARRFLIX wordmark | Same PNG as the masthead/admin drawer |
.splashLogo bg-size |
contain |
Aspect preserved |
| Animation | animation: fadein 0.5s (defined as @keyframes fadein { 0%{opacity:0} 100%{opacity:1} }) |
Half-second ease-in |
| Mobile vs desktop variant | @media (min-device-width: 992px) { .splashLogo { bg-image: <data-URL> } } |
The desktop branch CURRENTLY uses the same 235 × 85 PNG bytes as the small/mobile branch — i.e. there is no higher-resolution desktop asset. This is a half-implemented split. Owner could supply a 470 × 170 (2x) or 940 × 340 (4x) PNG to bake into the desktop branch for sharper rendering on 1080p+ displays. |
Screen reader / <title> |
<title> is set + locked at runtime by lockTitle() to "ARRFLIX" |
OK |
Verdict: splash is functional, fade-in is smooth, aspect is correct.
The only quality nit is the desktop <media> branch reading the same
small PNG as mobile — a 2× or 4× ARRFLIX wordmark in the desktop
branch would be sharper. Defer-able; not a complaint the owner has
raised.
7. Detail-page backdrop fix proposal (concrete CSS, NOT applied)
Re-stating §2d in implementation-ready form. Expected to drop into
CustomCss AFTER the Cineplex @import, BEFORE the existing
bg:#000 blocks (which need to be scoped out of detail pages to
not clobber the gradient — see body.itemDetailPage selectors below).
/* === Detail-page backdrop fix (proposal, 2026-05-08) === */
/* Convert Finity's 17vw black band into a Netflix-style gradient
overlay over a full-bleed backdrop. */
/* 1. Stretch backdrop container across the full viewport */
.layout-desktop .backdropContainer {
left: 0 !important;
width: 100vw !important;
}
/* 2. Replace Finity's mask.png with a CSS-only linear-gradient
that darkens the left ~50vw and fades to transparent.
`.backgroundContainer.withBackdrop` is the existing overlay
element in the Finity DOM. */
.layout-desktop .backgroundContainer.withBackdrop {
background-image: linear-gradient(
90deg,
rgba(0, 0, 0, 0.95) 0%,
rgba(0, 0, 0, 0.85) 25%,
rgba(0, 0, 0, 0.55) 45%,
rgba(0, 0, 0, 0.20) 65%,
rgba(0, 0, 0, 0.00) 85%
) !important;
background-size: 100vw 100vh !important;
left: 0 !important;
width: 100vw !important;
}
/* 3. UN-clamp the page bg specifically on detail pages so the
gradient composes against the actual backdrop, not pure black.
`.itemDetailPage` is added to <body> by JF on every detail
route (verified in main.jellyfin.bundle.js). */
body.itemDetailPage,
body.itemDetailPage #reactRoot,
body.itemDetailPage .mainAnimatedPages,
body.itemDetailPage .skinBody {
background-color: transparent !important;
}
Before/after expectation:
- Before: 17vw band on the left of the detail page is flat
#000; poster card hidden by Finity; title + clear-logo float on a hard black slab. - After: backdrop fills 100vw of the viewport. Title + logo float over a darkened-but-visible slice of the backdrop on the left, fading to full backdrop brightness around 70-85% across. Reads as netflix.com's title-card style.
Stops to tune once live (open DevTools, edit the gradient stops):
- If title text is illegible against busy artwork, push opacity stops
up:
0.95 / 0.92 / 0.75 / 0.40 / 0.10. - If too much of the backdrop is darkened, pull stops left:
0.95 / 0.80 / 0.40 / 0.10 / 0.00with the last stop at 60%. - If the right edge of the gradient creates a visible seam against a
bright backdrop, soften the last stop: append a sixth at
90% rgba(0,0,0,0)for an extra 5vw fade.
Untested side-effects to watch for:
- Finity hides
.detailImageContainer .cardon desktop. The fix preserves that (poster card stays hidden — title is the focus). If owner wants the poster card visible, drop:
by adding.layout-desktop .detailImageContainer .card { display: none }.layout-desktop .detailImageContainer .card { display: revert !important }. - The OSD scrubber (
.itemBackdropProgressBar) sits at the very bottom of.itemBackdrop. With the backdrop now full-width, it's also full-width (was already, just visually different against a colour-fade vs. black band). - Library-list pages that ALSO use the
.backgroundContainer.withBackdroplayer (a few in JF — backdrops on library tile rows) will get the same gradient. If they look wrong, scope rule (1) and (2) tobody.itemDetailPage .layout-desktop .backdropContaineretc.
8. Recommended forward path (top 3 ranked)
#1 — STAY on Cineplex + apply the §7 detail-page backdrop fix
Why: Cineplex is the only Netflix-faithful theme that runs on
JF 10.10.3 with a maintained codebase. The detail-page band is a
single rule's worth of CSS away from being a Netflix-style gradient
overlay. We've already invested in the brand stack (ARRFLIX wordmark,
header-icon hide, slider thumbs, Quick Connect off, settings hide); 11
of 14 personal tweaks survive the change, the other 3 (bg:#000)
need to be scoped to non-detail pages by selector chain
body:not(.itemDetailPage) instead of being dropped.
Risk: low. CSS-only, additive, no @import change, no
/branding POST hot-spot. Rolls back trivially.
Cost: ~30 minutes to apply, screenshot, tune gradient stops live.
#2 — Migrate to ElegantFin v25.12.31 with ARRFLIX #E50914 recolour
Why: ElegantFin's detail-page is full-width-backdrop with a gradient overlay built in — no left band — so the §7 fix becomes unnecessary. Most actively maintained CSS theme on JF (last commit 2026-04-30, GPL-2.0). The 04 §3e migration documented this exact config: 8 accent variables overridden, ARRFLIX logo + cast/crew + Quick Connect + header icons + slider thumbs all preserved.
Risk: medium. The previous attempt was overwritten by a sibling
Cineplex POST (race rule in 04 §3b). Personal-tweak block 7c
(.skinHeader.semiTransparent) still risks flattening ElegantFin's
translucent header — that block needs editing on landing.
Cost: ~45 minutes (re-do migration, scope the bg-clamp rules, verify all 11 personal tweaks intact post-POST).
Aesthetic delta vs Cineplex: ElegantFin is "polished Jellyseerr-y", Cineplex is "Netflix-faithful". With the recolour ElegantFin gets the brand red but keeps a non-Netflix layout (card design, hero strip, etc.). Owner has gone back-and-forth on this preference — explicitly chose Cineplex this morning.
#3 — Hybrid: keep Cineplex import + graft NeutralFin's --gradientPoint vars
Why: for owners who like Cineplex's red+webfont but want
NeutralFin's depth/gradient on backgrounds. Manually copy NeutralFin's
--darkerGradientPoint #131313 / --lighterGradientPoint #1e1e1e into a
:root block, drop our --primary-background-color: #000 !important
overrides, and let the gradient render.
Risk: higher than #1 or #2. Variables don't compose perfectly
across themes — Cineplex's Finity parent doesn't read those NeutralFin
vars, it reads its own --theme-background-color. So you'd actually
copy the values into Finity's variable: --theme-background-color: linear-gradient(...)
which CSS doesn't allow on a plain background-color. Real grafting
needs body { background-image: linear-gradient(180deg, #131313, #1e1e1e) }
plus dropping the bg:#000 !important rules.
Cost: ~60 min trial-and-error. Likely lower visual reward than #1.
Verdict: Recommended order is #1 first (lowest risk, biggest backdrop win), then #2 if owner re-evaluates Netflix-fidelity vs polish, #3 only as a fall-back if #1 doesn't read well.
9. Risks + rollback
Snapshot tag
snapshot-2026-05-08-pre-elegantfin — captured before the ElegantFin
attempt. Currently this is also the rollback point for any further
theme work because ElegantFin → NeutralFin → Cineplex have all been
applied (and reverted) on top of it. Located at
snapshots/2026-05-08-pre-elegantfin/.
If a future change wants its own snapshot, follow the pattern in
RESTORE.md: capture branding.json, index.html, all
displayprefs-*.json, users.json, libraries.json, write a new
RESTORE.md, tag the commit.
Prior failed swaps (timeline 2026-05-08)
| Time | Theme attempted | Outcome |
|---|---|---|
| early today | ElegantFin v25.12.31 (initial pick — pre-Netflix-brief) | replaced by Cineplex when owner asked for Netflix-faithful |
| mid-day | Cineplex v1.0.6 | applied, working |
| later | ElegantFin v25.12.31 + ARRFLIX recolour (04 §3e) | applied, then silently overwritten by a sibling Cineplex POST (race rule, see 04 §3b) |
| even later | NeutralFin v1.3.0 | applied, but a sibling Cineplex POST overwrote it minutes later (see doc 11 headline finding); also, our bg:#000 !important rules clamped its gradient flat so the brief render that DID land looked wrong |
| now | Cineplex v1.0.6 | active (verified live this audit) |
Race-rule reminder
/System/Configuration/branding takes a complete object on every
POST; whichever POST lands last wins. Per 04 §3b: any agent or script
touching this endpoint MUST GET → edit-only-its-fields → POST and
the branding POST must be the last in any sequence.
Detail-page fix rollback
If §7's CSS lands and looks wrong, remove the three new blocks from
CustomCss and POST branding. The §7 proposal is purely additive
(no rule removal); revert is a clean delete.
10. What was NOT touched during this audit
- No POST to
/System/Configuration/branding. - No edit to
web-overrides/index.htmlor the bind-mounted/jellyfin/jellyfin-web/index.html. - No
docker composeaction, no container restart. - No git commit on
snapshots/, no tag movement. - All inspections were
curlGET (/Branding/Configuration+/System/Configuration/branding) anddocker exec jellyfin sh -cbounded tocat/grep/wc/ls.
11. Sign-off
- Auditor: s8n (audit pass, 2026-05-08)
- Live theme at audit time: Cineplex v1.0.6 (verified —
/Branding/ConfigurationreturnsMRunkehl/cineplex@v1.0.6) - Top likely cause of detail-page black band: Finity (Cineplex's
parent) ships
--detail-page-backdrop-offset: 17%by design. Ourbg:#000 !importantrules turn that intentionally-clear 17vw band into a hard-black slab. The Finitymask.pngoverlay would have softened it into a gradient if it loads — worth a DevTools network check. - Recommended forward path: STAY on Cineplex + apply §7
detail-page CSS (full-bleed backdrop + linear-gradient overlay,
scoped to
body.itemDetailPage). - Personal-tweak portability: HIGH for 11 of 14 blocks; MEDIUM/LOW
for the 3
bg:#000blocks (must be scoped/dropped on theme swap). - Next step: owner reviews this doc + screenshots the current detail-page band, decides whether to apply §7. No work on the live server until that review.