diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c18dd8d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+__pycache__/
diff --git a/bin/apply-26-incident-fixes.sh b/bin/apply-26-incident-fixes.sh
index b05c09e..eb9042d 100755
--- a/bin/apply-26-incident-fixes.sh
+++ b/bin/apply-26-incident-fixes.sh
@@ -13,7 +13,11 @@
set -euo pipefail
-# 3. encoding.xml — disable throttling + segment deletion (both containers if present)
+# 3+5. encoding.xml — disable throttling + segment deletion (HLS 499)
+# AND disable software tonemapping (CPU-only nullstone
+# cannot sustain real-time 4K HDR tonemap+x264, ffmpeg
+# runs at ~0.5x → 18s wait time before video starts;
+# R&M is fake-HDR per doc 21 anyway, so no visual loss)
for cfg in /home/docker/jellyfin/config/config/encoding.xml \
/home/docker/jellyfin-dev/config/config/encoding.xml; do
[ -f "$cfg" ] || continue
@@ -21,6 +25,8 @@ for cfg in /home/docker/jellyfin/config/config/encoding.xml \
sed -i \
-e 's|true|false|' \
-e 's|true|false|' \
+ -e 's|true|false|' \
+ -e 's|true|false|' \
"$cfg"
echo "[+] patched $cfg"
done
@@ -44,7 +50,10 @@ patch = """
INC1: BLACK-PASS occludes backdrop; transparent-scope via :has().
INC2: pin backdrop position:fixed so it persists across scroll.
INC3: extend transparent-scope through detail-page sub-sections so
- section wrappers don't paint over the pinned backdrop. */
+ section wrappers don't paint over the pinned backdrop.
+ INC4: override the 2026-05-08 .emby-scroller=#000 rule on detail page
+ (it was painting a black band behind every carousel — most visible
+ on admin-only "More from Season" / "More Like This"). */
.mainDetailButtons .material-icons.play_arrow::after {
content: "Play" !important;
}
@@ -111,6 +120,21 @@ patch = """
background-color: transparent !important;
background: transparent !important;
}
+/* INC4: 2026-05-08 home-page "kill gray band" rule paints .emby-scroller
+ #000 unscoped — that's the OPAQUE wrapper around every carousel inside
+ .itemDetailPage. Override back to transparent on detail page only. */
+.itemDetailPage .emby-scroller,
+.itemDetailPage .emby-scroller-container,
+.itemDetailPage .verticalSection,
+.itemDetailPage .padded-top-focusscale,
+.itemDetailPage .padded-bottom-focusscale,
+.itemDetailPage .moreFromSeasonSection,
+.itemDetailPage .moreFromArtistSection,
+.itemDetailPage .scrollSliderContainer,
+.itemDetailPage .scrollButtonContainer {
+ background-color: transparent !important;
+ background: transparent !important;
+}
"""
s = s.replace("", patch + "")
open(p, "w").write(s)
diff --git a/bin/headless-test-v2.py b/bin/headless-test-v2.py
new file mode 100755
index 0000000..77b396e
--- /dev/null
+++ b/bin/headless-test-v2.py
@@ -0,0 +1,629 @@
+#!/usr/bin/env python3
+"""ARRFLIX headless smoke-test v2.
+
+Why v2 exists (see docs/26 INC4 audit):
+ v1 had three coverage gaps that let two regressions ship:
+ - Logged in only as `guest` (non-admin restricted) → admin-only sections
+ like the "More from Season N" carousel never rendered, so the black
+ band behind that carousel was invisible to the test.
+ - Never clicked Play → never observed the