diff --git a/Controls/PreviewPane.xaml b/Controls/PreviewPane.xaml
index 9f96054..7ca04a8 100644
--- a/Controls/PreviewPane.xaml
+++ b/Controls/PreviewPane.xaml
@@ -20,6 +20,11 @@
PreviewMouseRightButtonUp="Preview_MouseRightButtonUp">
= 0) scene.Elements.Move(blurIndex, BlurInsertIndex(scene));
- }
- else
- {
- _sceneGraph.AddElement(scene, source);
- }
+ // The blur is universal (creator ruling 2026-11-01): the compositor applies it over every
+ // layer, so its position in the list does not affect the output. It is added at the top so
+ // the layer list reads "this covers everything", and no reorder clamp is needed.
+ _sceneGraph.AddElement(scene, source);
if (sourceType == SourceType.WebSource)
{
@@ -133,38 +125,10 @@ public partial class MainViewModel
/// X/Y/W/H) in the same Source rows.
public void OnSceneElementsReordered()
{
- // A blur may never end up above the brand logo (creator ruling 2026-11-01). Only offending
- // blurs move — a legitimate reorder inside the layer list is left exactly as dropped.
- if (StagedScene is { } scene)
- {
- var brand = scene.Elements.OfType().FirstOrDefault(s => s.IsBrandLogo);
- if (brand != null)
- {
- foreach (var blur in scene.Elements.OfType().Where(s => s.IsBlur).ToList())
- {
- var brandIndex = scene.Elements.IndexOf(brand);
- var blurIndex = scene.Elements.IndexOf(blur);
- if (blurIndex > brandIndex) scene.Elements.Move(blurIndex, brandIndex);
- }
- }
- }
-
if (StagedScene != null) _sceneGraph.InvalidateBake(StagedScene);
SaveLayoutNow();
}
- /// The slot a privacy blur belongs in: immediately after the background, which puts it
- /// below the brand logo (the logo is inserted directly above the background too, so the blur
- /// must land before it).
- private static int BlurInsertIndex(Scene scene)
- {
- var background = scene.Elements.OfType().FirstOrDefault(s => s.IsBackground);
- if (background != null) return scene.Elements.IndexOf(background) + 1;
-
- var brand = scene.Elements.OfType().FirstOrDefault(s => s.IsBrandLogo);
- return brand != null ? scene.Elements.IndexOf(brand) : 0;
- }
-
// Duplicate resource names get an incrementing suffix with no space: Image,
// Image2, Image3… The next free number is derived from the names actually in
// the scene, so deleting a middle resource never collides with a survivor.
diff --git a/docs/ai/frame-pipeline.md b/docs/ai/frame-pipeline.md
index 70c8d0f..b1ce807 100644
--- a/docs/ai/frame-pipeline.md
+++ b/docs/ai/frame-pipeline.md
@@ -788,15 +788,16 @@ Full suite 290/291 passing, the sole failure the pre-existing compositor pixel t
bottom) in master space. `MainViewModel` owns the frame (`_socialBarFrame`, rebuilt by `RenderSocialBarFrame`
on load/save/`NotifySocialsChanged`).
- **Privacy blur layer (2026-11-01):** `SourceType.Blur` is a Live-scene element that blurs the
- composed-so-far buffer inside a rect or ellipse — it hides part of the creator's desktop without
- hiding the brand. `SceneCompositor` treats all visible blurs as ONE group the first time one is
+ composed frame inside a rect or ellipse. **It is universal**: `SceneCompositor` applies the blur
+ group AFTER every layer has been composited (before the flash/social/ticker overlays), so it can
+ cover the webcam, chat, alerts — anything — which is the creator's "moment of privacy" case. Its
+ position in the layer list is cosmetic for that reason (it is added at the top), and the earlier
+ "never above the brand" rule is superseded for blurs: a blur over the logo does obscure it. `SceneCompositor` treats all visible blurs as ONE group the first time one is
reached in the z-order (`ApplyBlurLayers`), sorts them **strongest first** and passes a shared
coverage map to `RegionBlur.ApplyInPlace`, so an overlap resolves to the strongest single blur
rather than a blurred blur. `RegionBlur` shares the webcam's radius curve and constants
(`Downscale 4`, `MinRadius 1`, `MaxRadius 6`, `RadiusFor(strength)`), so a strength means the
same thing in both features; the webcam's `BackgroundEffectProcessor` delegates to it.
- ⛔ Z-order is policy: `AddSource` inserts the blur directly above the background (below the brand
- logo), and `OnSceneElementsReordered` moves any blur that ends up above the brand back down.
Blur is `ElementKind.Dynamic`, so it is never baked into the static base and every render path
(pump, preview, thumbnails, scene dump) gets it from the one compositor.
- Known consideration: the pump reads the active scene on a background thread while the UI can still edit