--========================================================--
-- HouseAuras: Ambient Lighting, Dual Parallax & Co-Suite
-- Slash Command: /ha
-- Author: Ninejah-Proudmoore
-- Version: 1.8.2 (Cross-Realm Whisper Handshake Engine)
--========================================================--

HouseAurasDB = HouseAurasDB or {
    isConductor = false,
    conductorName = nil,
    followers = {}, -- Tracks subscribed players: ["Player-Realm"] = true
    windowPosition = {}
}

local ADDON_PREFIX = "HA_SYNC"
C_ChatInfo.RegisterAddonMessagePrefix(ADDON_PREFIX)

local lmFrame = nil
local lmActiveTicker = nil
local lmScrollTicker = nil
local lmActiveOverlay = nil
local lmForegroundOverlay1 = nil
local lmForegroundOverlay2 = nil
local fg1Texture = nil
local fg2Texture = nil

-- Independent Dual-Parallax State Tracking
local currentMode = "Solid"
local currentIntensity = "Normal"
local currentSpeed = "Normal"
local currentColor = { r = 1.0, g = 0.7, b = 0.3 }

local currentFg1Name = "None"
local currentFg1Intensity = "Normal"
local currentFg1Speed = "Normal"
local currentFg1HScroll = true
local currentFg1VScroll = true
local currentFg1InvertH = false
local currentFg1InvertV = false
local currentFg1Spin = false
local currentFg1Throb = false

local currentFg2Name = "None"
local currentFg2Intensity = "Normal"
local currentFg2Speed = "Normal"
local currentFg2HScroll = true
local currentFg2VScroll = true
local currentFg2InvertH = false
local currentFg2InvertV = false
local currentFg2Spin = false
local currentFg2Throb = false

local afkTheaterPreset = nil
local preAfkPanelShown = false

local fg1ScrollX, fg1ScrollY = 0, 0
local fg2ScrollX, fg2ScrollY = 0, 0
local fg1SpinAngle, fg2SpinAngle = 0, 0
local fg1ThrobTime, fg2ThrobTime = 0, 0

local FOREGROUND_LIST = {
    "None",
    "Clouds",
    "Starfield",
    "starfield2",
    "Water"
}

------------------------------------------------------------
-- Helpers: Position Persistence & Name Normalization
------------------------------------------------------------
local function LM_SavePosition(frame)
    if not frame then return end
    local left, top = frame:GetLeft(), frame:GetTop()
    if not left or not top then return end
    HouseAurasDB.windowPosition = { left = left, top = top }
end

local function LM_RestorePosition(frame)
    if not frame then return end
    local pos = HouseAurasDB.windowPosition
    if not pos.left or not pos.top then
        frame:ClearAllPoints()
        frame:SetPoint("CENTER")
        return
    end
    frame:ClearAllPoints()
    frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", pos.left, pos.top)
end

local function GetNormalizedUnitName(unit)
    local name, server = UnitName(unit)
    if not name then return nil end
    if server and server ~= "" then
        return name .. "-" .. server
    else
        return name .. "-" .. GetRealmName()
    end
end

------------------------------------------------------------
-- Core Multi-Layer Ambient & Dual Parallax Execution
------------------------------------------------------------
local function LM_StopAmbient()
    if lmActiveTicker then
        lmActiveTicker:Cancel()
        lmActiveTicker = nil
    end
    if lmScrollTicker then
        lmScrollTicker:Cancel()
        lmScrollTicker = nil
    end
    if lmActiveOverlay then
        lmActiveOverlay:SetBackdropColor(0, 0, 0, 0)
        lmActiveOverlay:Hide()
    end
end

local function LM_EnsureOverlays()
    if lmActiveOverlay and lmForegroundOverlay1 and lmForegroundOverlay2 then 
        return lmActiveOverlay, lmForegroundOverlay1, lmForegroundOverlay2 
    end
    
    local bg = CreateFrame("Frame", "HouseAurasDisplayOverlay", WorldFrame, "BackdropTemplate")
    bg:SetAllPoints(UIParent)
    bg:SetFrameStrata("BACKGROUND")
    bg:SetFrameLevel(1)
    bg:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8" })
    bg:SetBackdropColor(0, 0, 0, 0)
    bg:Hide()
    lmActiveOverlay = bg

    local fg1 = CreateFrame("Frame", "HouseAurasForegroundOverlay1", WorldFrame)
    fg1:SetAllPoints(UIParent)
    fg1:SetFrameStrata("BACKGROUND")
    fg1:SetFrameLevel(2)
    fg1:Hide()
    fg1Texture = fg1:CreateTexture(nil, "BACKGROUND")
    fg1Texture:SetAllPoints(fg1)
    fg1Texture:SetHorizTile(true)
    fg1Texture:SetVertTile(true)
    lmForegroundOverlay1 = fg1

    local fg2 = CreateFrame("Frame", "HouseAurasForegroundOverlay2", WorldFrame)
    fg2:SetAllPoints(UIParent)
    fg2:SetFrameStrata("BACKGROUND")
    fg2:SetFrameLevel(3)
    fg2:Hide()
    fg2Texture = fg2:CreateTexture(nil, "BACKGROUND")
    fg2Texture:SetAllPoints(fg2)
    fg2Texture:SetHorizTile(true)
    fg2Texture:SetVertTile(true)
    lmForegroundOverlay2 = fg2

    return lmActiveOverlay, lmForegroundOverlay1, lmForegroundOverlay2
end

local function GetSpeedMultiplier(speedStr)
    if speedStr == "Slow" then return 0.4
    elseif speedStr == "Fast" then return 2.2
    end
    return 1.0
end

local function LM_StartScrollEngine()
    if lmScrollTicker then return end
    
    local updateInterval = 0.03
    lmScrollTicker = C_Timer.NewTicker(updateInterval, function()
        local screenW = UIParent:GetWidth()
        local screenH = UIParent:GetHeight()
        local tileSize = 512

        if lmForegroundOverlay1 and lmForegroundOverlay1:IsShown() and fg1Texture then
            local speedMult = GetSpeedMultiplier(currentFg1Speed)
            local stepX = 0.0004 * speedMult
            local stepY = 0.0002 * speedMult
            
            if currentFg1HScroll then
                if currentFg1InvertH then fg1ScrollX = fg1ScrollX - stepX else fg1ScrollX = fg1ScrollX + stepX end
            end
            if currentFg1VScroll then
                if currentFg1InvertV then fg1ScrollY = fg1ScrollY - stepY else fg1ScrollY = fg1ScrollY + stepY end
            end

            if currentFg1Spin then
                fg1SpinAngle = fg1SpinAngle + (0.003 * speedMult)
            end

            local throbMultiplier = 1.0
            if currentFg1Throb then
                fg1ThrobTime = fg1ThrobTime + (0.03 * speedMult)
                throbMultiplier = 1.0 + 0.25 * math.sin(fg1ThrobTime * (2 * math.pi / 4.0))
            end

            local widthMult = (screenW / tileSize) * throbMultiplier
            local heightMult = (screenH / tileSize) * throbMultiplier

            if currentFg1Spin then
                local c = math.cos(fg1SpinAngle)
                local s = math.sin(fg1SpinAngle)
                local cx, cy = fg1ScrollX + (widthMult / 2), fg1ScrollY + (heightMult / 2)
                
                local x1, y1 = -widthMult / 2, -heightMult / 2
                local x2, y2 = widthMult / 2, -heightMult / 2
                local x3, y3 = widthMult / 2, heightMult / 2
                local x4, y4 = -widthMult / 2, heightMult / 2

                fg1Texture:SetTexCoord(
                    cx + (x1 * c - y1 * s), cy + (x1 * s + y1 * c),
                    cx + (x2 * c - y2 * s), cy + (x2 * s + y2 * c),
                    cx + (x4 * c - y4 * s), cy + (x4 * s + y4 * c),
                    cx + (x3 * c - y3 * s), cy + (x3 * s + y3 * c)
                )
            else
                local offsetX = (widthMult - (screenW / tileSize)) / 2
                local offsetY = (heightMult - (screenH / tileSize)) / 2
                fg1Texture:SetTexCoord(fg1ScrollX - offsetX, fg1ScrollX + widthMult - offsetX, fg1ScrollY - offsetY, fg1ScrollY + heightMult - offsetY)
            end
        end

        if lmForegroundOverlay2 and lmForegroundOverlay2:IsShown() and fg2Texture then
            local speedMult = GetSpeedMultiplier(currentFg2Speed)
            local stepX = 0.0009 * speedMult
            local stepY = 0.0005 * speedMult
            
            if currentFg2HScroll then
                if currentFg2InvertH then fg2ScrollX = fg2ScrollX - stepX else fg2ScrollX = fg2ScrollX + stepX end
            end
            if currentFg2VScroll then
                if currentFg2InvertV then fg2ScrollY = fg2ScrollY - stepY else fg2ScrollY = fg2ScrollY + stepY end
            end

            if currentFg2Spin then
                fg2SpinAngle = fg2SpinAngle - (0.005 * speedMult)
            end

            local throbMultiplier = 1.0
            if currentFg2Throb then
                fg2ThrobTime = fg2ThrobTime + (0.03 * speedMult)
                throbMultiplier = 1.0 + 0.25 * math.sin(fg2ThrobTime * (2 * math.pi / 4.0))
            end

            local widthMult = (screenW / tileSize) * throbMultiplier
            local heightMult = (screenH / tileSize) * throbMultiplier

            if currentFg2Spin then
                local c = math.cos(fg2SpinAngle)
                local s = math.sin(fg2SpinAngle)
                local cx, cy = fg2ScrollX + (widthMult / 2), fg2ScrollY + (heightMult / 2)
                
                local x1, y1 = -widthMult / 2, -heightMult / 2
                local x2, y2 = widthMult / 2, -heightMult / 2
                local x3, y3 = widthMult / 2, heightMult / 2
                local x4, y4 = -widthMult / 2, heightMult / 2

                fg2Texture:SetTexCoord(
                    cx + (x1 * c - y1 * s), cy + (x1 * s + y1 * c),
                    cx + (x2 * c - y2 * s), cy + (x2 * s + y2 * c),
                    cx + (x4 * c - y4 * s), cy + (x4 * s + y4 * c),
                    cx + (x3 * c - y3 * s), cy + (x3 * s + y3 * c)
                )
            else
                local offsetX = (widthMult - (screenW / tileSize)) / 2
                local offsetY = (heightMult - (screenH / tileSize)) / 2
                fg2Texture:SetTexCoord(fg2ScrollX - offsetX, fg2ScrollX + widthMult - offsetX, fg2ScrollY - offsetY, fg2ScrollY + heightMult - offsetY)
            end
        end
    end)
end

local function LM_TriggerLightning()
    local overlay, _, _ = LM_EnsureOverlays()
    local pattern = math.random(1, 3)
    
    if pattern == 1 then
        overlay:SetBackdropColor(1, 1, 1, 0.85)
        overlay:Show()
        C_Timer.After(0.06, function()
            UIFrameFadeOut(overlay, 1.2, 0.85, 0)
        end)
    elseif pattern == 2 then
        overlay:SetBackdropColor(1, 1, 1, 0.85)
        overlay:Show()
        C_Timer.After(0.05, function() overlay:SetBackdropColor(1, 1, 1, 0.15) end)
        C_Timer.After(0.12, function() overlay:SetBackdropColor(1, 1, 1, 0.90) end)
        C_Timer.After(0.18, function()
            UIFrameFadeOut(overlay, 1.5, 0.90, 0)
        end)
    else
        overlay:SetBackdropColor(1, 1, 1, 0.50)
        overlay:Show()
        C_Timer.After(0.08, function() overlay:SetBackdropColor(1, 1, 1, 0.20) end)
        C_Timer.After(0.18, function() overlay:SetBackdropColor(1, 1, 1, 0.95) end)
        C_Timer.After(0.26, function()
            UIFrameFadeOut(overlay, 1.8, 0.95, 0)
        end)
    end
end

local function LM_ApplyTint(r, g, b, mode, intensity, speed)
    local overlay, _, _ = LM_EnsureOverlays()
    
    currentColor = { r = r, g = g, b = b }
    currentMode = mode
    currentIntensity = intensity
    currentSpeed = speed

    if lmActiveTicker then
        lmActiveTicker:Cancel()
        lmActiveTicker = nil
    end

    if mode == "Solid" then
        local alpha = 0.30
        if r == 0 and g == 0 and b == 0 then
            alpha = 0.60
        elseif intensity == "Faint" then 
            alpha = 0.15
        elseif intensity == "Intense" then 
            alpha = 0.45 
        end

        overlay:SetBackdropColor(r, g, b, alpha)
        overlay:Show()
    elseif mode == "Pulse" then
        local baseAlpha = 0.30
        if r == 0 and g == 0 and b == 0 then
            baseAlpha = 0.60
        elseif intensity == "Faint" then 
            baseAlpha = 0.15
        elseif intensity == "Intense" then 
            baseAlpha = 0.45 
        end

        local period = 6.0
        if speed == "Slow" then period = 9.0
        elseif speed == "Fast" then period = 3.0 end

        local timeElapsed = 0
        local updateInterval = 0.05
        
        lmActiveTicker = C_Timer.NewTicker(updateInterval, function()
            timeElapsed = timeElapsed + updateInterval
            local wave = (math.sin((timeElapsed / period) * (2 * math.pi)) + 1) / 2
            local currentAlpha = baseAlpha * (0.3 + 0.7 * wave)
            
            overlay:SetBackdropColor(r, g, b, currentAlpha)
            overlay:Show()
        end)
    elseif mode == "Strobe" then
        local flashInterval = 0.10
        if speed == "Slow" then flashInterval = 0.20
        elseif speed == "Fast" then flashInterval = 0.04 end

        local isVisible = true
        overlay:SetBackdropColor(0, 0, 0, 0.9)
        overlay:Show()

        lmActiveTicker = C_Timer.NewTicker(flashInterval, function()
            isVisible = not isVisible
            if isVisible then
                overlay:SetBackdropColor(0, 0, 0, 0.9)
                overlay:Show()
            else
                overlay:SetBackdropColor(0, 0, 0, 0.0)
            end
        end)
    elseif mode == "Lightning" then
        LM_TriggerLightning()
        local period = 6.0
        if speed == "Slow" then period = 10.0 elseif speed == "Fast" then period = 3.0 end
        lmActiveTicker = C_Timer.NewTicker(period, function()
            LM_TriggerLightning()
        end)
    end
end

local function LM_ApplyForegroundLayer1(fgName, intensity, speed, hScroll, vScroll, invertH, invertV, spin, throb)
    local _, fg1, _ = LM_EnsureOverlays()
    currentFg1Name = fgName
    currentFg1Intensity = intensity
    if speed ~= nil then currentFg1Speed = speed end
    if hScroll ~= nil then currentFg1HScroll = hScroll end
    if vScroll ~= nil then currentFg1VScroll = vScroll end
    if invertH ~= nil then currentFg1InvertH = invertH end
    if invertV ~= nil then currentFg1InvertV = invertV end
    if spin ~= nil then currentFg1Spin = spin end
    if throb ~= nil then currentFg1Throb = throb end

    fg1:Hide()
    if fg1Texture then fg1Texture:SetTexture(nil) end

    if fgName ~= "None" then
        local texturePath = string.format("Interface\\AddOns\\HouseAuras\\Media\\%s.png", fgName)
        fg1Texture:SetTexture(texturePath, "REPEAT", "REPEAT")
        
        local alpha = 0.45
        if intensity == "Faint" then alpha = 0.25
        elseif intensity == "Intense" then alpha = 0.75 end

        fg1Texture:SetVertexColor(1, 1, 1, alpha)
        fg1:Show()
        fg1ScrollX, fg1ScrollY = 0, 0
        LM_StartScrollEngine()
    end
end

local function LM_ApplyForegroundLayer2(fgName, intensity, speed, hScroll, vScroll, invertH, invertV, spin, throb)
    local _, _, fg2 = LM_EnsureOverlays()
    currentFg2Name = fgName
    currentFg2Intensity = intensity
    if speed ~= nil then currentFg2Speed = speed end
    if hScroll ~= nil then currentFg2HScroll = hScroll end
    if vScroll ~= nil then currentFg2VScroll = vScroll end
    if invertH ~= nil then currentFg2InvertH = invertH end
    if invertV ~= nil then currentFg2InvertV = invertV end
    if spin ~= nil then currentFg2Spin = spin end
    if throb ~= nil then currentFg2Throb = throb end

    fg2:Hide()
    if fg2Texture then fg2Texture:SetTexture(nil) end

    if fgName ~= "None" then
        local texturePath = string.format("Interface\\AddOns\\HouseAuras\\Media\\%s.png", fgName)
        fg2Texture:SetTexture(texturePath, "REPEAT", "REPEAT")
        
        local alpha = 0.45
        if intensity == "Faint" then alpha = 0.25
        elseif intensity == "Intense" then alpha = 0.75 end

        fg2Texture:SetVertexColor(1, 1, 1, alpha)
        fg2:Show()
        fg2ScrollX, fg2ScrollY = 0, 0
        LM_StartScrollEngine()
    end
end

------------------------------------------------------------
-- Cross-Realm Dispatcher
------------------------------------------------------------
local function LM_BroadcastToFollowers(payload)
    if not HouseAurasDB.isConductor then return end

    for followerName, isSubbed in pairs(HouseAurasDB.followers) do
        if isSubbed then
            C_ChatInfo.SendAddonMessage(ADDON_PREFIX, payload, "WHISPER", followerName)
        end
    end
end

function LM_PlayDualConfig(r, g, b, tMode, tInt, tSpd, fg1Name, fg1Int, fg1Spd, fg1HS, fg1VS, fg1IH, fg1IV, fg1Sp, fg1Th, fg2Name, fg2Int, fg2Spd, fg2HS, fg2VS, fg2IH, fg2IV, fg2Sp, fg2Th, skipBroadcast)
    LM_ApplyTint(r, g, b, tMode, tInt, tSpd)
    LM_ApplyForegroundLayer1(fg1Name, fg1Int, fg1Spd, fg1HS, fg1VS, fg1IH, fg1IV, fg1Sp, fg1Th)
    LM_ApplyForegroundLayer2(fg2Name, fg2Int, fg2Spd, fg2HS, fg2VS, fg2IH, fg2IV, fg2Sp, fg2Th)

    if not skipBroadcast and HouseAurasDB.isConductor then
        local payload = string.format("CFG|%f|%f|%f|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s", 
            r, g, b, tMode, tInt, tSpd, 
            fg1Name, fg1Int, fg1Spd, tostring(fg1HS), tostring(fg1VS), tostring(fg1IH), tostring(fg1IV), tostring(fg1Sp), tostring(fg1Th),
            fg2Name, fg2Int, fg2Spd, tostring(fg2HS), tostring(fg2VS), tostring(fg2IH), tostring(fg2IV), tostring(fg2Sp), tostring(fg2Th))
        
        LM_BroadcastToFollowers(payload)
    end
end

------------------------------------------------------------
-- Cross-Realm Whisper Handshake Listener
------------------------------------------------------------
local syncListener = CreateFrame("Frame")
syncListener:RegisterEvent("CHAT_MSG_ADDON")
syncListener:SetScript("OnEvent", function(self, event, prefix, msg, channel, sender)
    if prefix ~= ADDON_PREFIX then return end

    -- 1. CONDUCTOR SIDE: Handle incoming subscriptions
    if HouseAurasDB.isConductor then
        if msg == "SUB" then
            HouseAurasDB.followers[sender] = true
            print(string.format("HouseAuras: [%s] synced to your lighting feed.", sender))
            
            -- Push current state snapshot instantly to new follower
            local currentPayload = string.format("CFG|%f|%f|%f|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s",
                currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed,
                currentFg1Name, currentFg1Intensity, currentFg1Speed, tostring(currentFg1HScroll), tostring(currentFg1VScroll), tostring(currentFg1InvertH), tostring(currentFg1InvertV), tostring(currentFg1Spin), tostring(currentFg1Throb),
                currentFg2Name, currentFg2Intensity, currentFg2Speed, tostring(currentFg2HScroll), tostring(currentFg2VScroll), tostring(currentFg2InvertH), tostring(currentFg2InvertV), tostring(currentFg2Spin), tostring(currentFg2Throb))
            
            C_ChatInfo.SendAddonMessage(ADDON_PREFIX, currentPayload, "WHISPER", sender)

        elseif msg == "UNSUB" then
            HouseAurasDB.followers[sender] = nil
            print(string.format("HouseAuras: [%s] disconnected from your feed.", sender))
        end
    end

    -- 2. FOLLOWER SIDE: Handle incoming lighting config from Conductor
    if HouseAurasDB.conductorName and sender == HouseAurasDB.conductorName then
        if string.sub(msg, 1, 4) == "CFG|" then
            local data = {}
            for v in string.gmatch(msg:sub(5), "[^|]+") do
                table.insert(data, v)
            end
            
            if #data >= 24 then
                local r, g, b = tonumber(data[1]), tonumber(data[2]), tonumber(data[3])
                local tMode, tInt, tSpd = data[4], data[5], data[6]
                local fg1Name, fg1Int, fg1Spd = data[7], data[8], data[9]
                local fg1HS = (data[10] == "true"); local fg1VS = (data[11] == "true")
                local fg1IH = (data[12] == "true"); local fg1IV = (data[13] == "true")
                local fg1Sp = (data[14] == "true"); local fg1Th = (data[15] == "true")
                local fg2Name, fg2Int, fg2Spd = data[16], data[17], data[18]
                local fg2HS = (data[19] == "true"); local fg2VS = (data[20] == "true")
                local fg2IH = (data[21] == "true"); local fg2IV = (data[22] == "true")
                local fg2Sp = (data[23] == "true"); local fg2Th = (data[24] == "true")

                LM_PlayDualConfig(r, g, b, tMode, tInt, tSpd, 
                    fg1Name, fg1Int, fg1Spd, fg1HS, fg1VS, fg1IH, fg1IV, fg1Sp, fg1Th,
                    fg2Name, fg2Int, fg2Spd, fg2HS, fg2VS, fg2IH, fg2IV, fg2Sp, fg2Th, true)
            end
        end
    end
end)

------------------------------------------------------------
-- AFK Theater Mode Engine
------------------------------------------------------------
local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("PLAYER_FLAGS_CHANGED")
eventFrame:SetScript("OnEvent", function(self, event, unit)
    if unit ~= "player" then return end

    if afkTheaterPreset then
        if UnitIsAFK("player") then
            if lmFrame and lmFrame:IsShown() then
                preAfkPanelShown = true
                lmFrame:Hide()
            else
                preAfkPanelShown = false
            end
            
            UIParent:Hide()
            
            local p = afkTheaterPreset
            LM_PlayDualConfig(p.r, p.g, p.b, p.tMode, p.tInt, p.tSpd, 
                p.fg1Name, p.fg1Int, p.fg1Spd, p.fg1HS, p.fg1VS, p.fg1IH, p.fg1IV, p.fg1Sp, p.fg1Th,
                p.fg2Name, p.fg2Int, p.fg2Spd, p.fg2HS, p.fg2VS, p.fg2IH, p.fg2IV, p.fg2Sp, p.fg2Th, true)
            print("HouseAuras: AFK detected — launching Theater Mode preset & hiding UI.")
        else
            UIParent:Show()

            LM_StopAmbient()
            if lmForegroundOverlay1 then lmForegroundOverlay1:Hide() end
            if lmForegroundOverlay2 then lmForegroundOverlay2:Hide() end

            if preAfkPanelShown and lmFrame then
                lmFrame:Show()
            end
            print("HouseAuras: Welcome back — exiting AFK Theater Mode, clearing effect & restoring UI.")
        end
    end
end)

------------------------------------------------------------
-- UI Builder: Multi-Layer Control Panel & Handshake Controls
------------------------------------------------------------
local function LM_BuildPanel()
    if lmFrame then return lmFrame end

    local f = CreateFrame("Frame", "HouseAurasControlPanel", UIParent, "BackdropTemplate")
    f:SetSize(370, 640) 
    f:SetFrameStrata("DIALOG")
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", function(self)
        f.StopMovingOrSizing(self)
        LM_SavePosition(f)
    end)
    f:SetClampedToScreen(true)
    f:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        tile = true, tileSize = 32, edgeSize = 32,
        insets = { left = 8, right = 8, top = 8, bottom = 8 }
    })

    local title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    title:SetPoint("TOP", 0, -14)
    title:SetText("HouseAuras: Multi-Layer Suite v1.8.2")

    local closeBtn = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    closeBtn:SetPoint("TOPRIGHT", -6, -6)
    closeBtn:SetScript("OnClick", function() f:Hide() end)

    local scrollFrame = CreateFrame("ScrollFrame", nil, f, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", 12, -40)
    scrollFrame:SetPoint("BOTTOMRIGHT", -30, 36)

    local scrollChild = CreateFrame("Frame", nil, scrollFrame)
    scrollChild:SetSize(320, 1120)
    scrollFrame:SetScrollChild(scrollChild)

    local function CreateCycleButton(parent, prefix, initialVal, optionsTable, callback)
        local btn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
        btn:SetSize(310, 22)
        btn.val = initialVal
        btn.prefix = prefix
        btn:SetText(prefix .. ": " .. btn.val)
        
        btn:SetScript("OnClick", function(self)
            local idx = 1
            for i, v in ipairs(optionsTable) do if v == self.val then idx = i break end end
            self.val = optionsTable[(idx % #optionsTable) + 1]
            btn.val = self.val
            btn:SetText(self.prefix .. ": " .. self.val)
            if callback then callback(self.val) end
        end)
        return btn
    end

    local function CreateCheckbox(parent, labelText, initialVal, callback)
        local cb = CreateFrame("CheckButton", nil, parent, "InterfaceOptionsCheckButtonTemplate")
        cb:SetSize(24, 24)
        cb.text = cb.text or cb:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        cb.text:SetPoint("LEFT", cb, "RIGHT", 6, 0)
        cb.text:SetText(labelText)
        cb:SetChecked(initialVal)
        
        cb:SetScript("OnClick", function(self)
            local checked = self:GetChecked()
            if callback then callback(checked) end
        end)
        return cb
    end

    --------------------------------------------------------
    -- Category 1: Ambient Tint Environment Controls
    --------------------------------------------------------
    local lblTint = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    lblTint:SetPoint("TOPLEFT", 10, -5)
    lblTint:SetText("1. Ambient Tint & Strobe Settings:")

    modeBtn = CreateCycleButton(scrollChild, "Tint Mode", currentMode, { "Solid", "Pulse", "Strobe", "Lightning" }, function(val) 
        currentMode = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    modeBtn:SetPoint("TOPLEFT", lblTint, "BOTTOMLEFT", 0, -4)

    intensityBtn = CreateCycleButton(scrollChild, "Tint Level", currentIntensity, { "Normal", "Intense", "Faint" }, function(val) 
        currentIntensity = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    intensityBtn:SetPoint("TOPLEFT", modeBtn, "BOTTOMLEFT", 0, -4)

    speedBtn = CreateCycleButton(scrollChild, "Tint/Strobe Speed", currentSpeed, { "Normal", "Fast", "Slow" }, function(val) 
        currentSpeed = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    speedBtn:SetPoint("TOPLEFT", intensityBtn, "BOTTOMLEFT", 0, -4)

    local swatches = {
        { name = "White",        r = 1.0, g = 1.0, b = 1.0 },
        { name = "Black",        r = 0.0, g = 0.0, b = 0.0 },
        { name = "Warm Amber",   r = 1.0, g = 0.7, b = 0.3 },
        { name = "Cool Cyan",    r = 0.2, g = 0.8, b = 1.0 },
        { name = "Royal Purple", r = 0.6, g = 0.2, b = 1.0 },
        { name = "Electric Blue",r = 0.1, g = 0.4, b = 1.0 },
        { name = "Soft Red",     r = 1.0, g = 0.2, b = 0.2 },
        { name = "Lime Green",   r = 0.2, g = 1.0, b = 0.2 },
        { name = "Pure Yellow",  r = 1.0, g = 1.0, b = 0.0 },
        { name = "Hot Magenta",  r = 1.0, g = 0.0, b = 1.0 },
    }

    local swatchContainer = CreateFrame("Frame", nil, scrollChild)
    swatchContainer:SetSize(310, 55)
    swatchContainer:SetPoint("TOPLEFT", speedBtn, "BOTTOMLEFT", 0, -6)

    for i, col in ipairs(swatches) do
        local rowIndex = math.floor((i - 1) / 5)
        local colIndex = (i - 1) % 5

        local colorBtn = CreateFrame("Button", nil, swatchContainer, "BackdropTemplate")
        colorBtn:SetSize(56, 20)
        colorBtn:SetPoint("TOPLEFT", swatchContainer, "TOPLEFT", colIndex * (56 + 6), -(rowIndex * (22 + 4)))
        colorBtn:SetBackdrop({ 
            bgFile = "Interface\\Buttons\\WHITE8x8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            edgeSize = 10
        })
        colorBtn:SetBackdropColor(col.r, col.g, col.b, 1.0)
        colorBtn:SetBackdropBorderColor(0.5, 0.5, 0.5, 1.0)
        
        colorBtn:SetScript("OnClick", function()
            LM_PlayDualConfig(col.r, col.g, col.b, currentMode, currentIntensity, currentSpeed, 
                currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
                currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
            print("HouseAuras: Applied Tint -> " .. col.name)
        end)
    end

    --------------------------------------------------------
    -- Category 2: Parallax Layer 1 Controls
    --------------------------------------------------------
    local lblFg1 = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    lblFg1:SetPoint("TOPLEFT", swatchContainer, "BOTTOMLEFT", 0, -12)
    lblFg1:SetText("2. Parallax Layer 1 (Base):")

    fg1CycleBtn = CreateCycleButton(scrollChild, "Layer 1 Asset", currentFg1Name, FOREGROUND_LIST, function(val) 
        currentFg1Name = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1CycleBtn:SetPoint("TOPLEFT", lblFg1, "BOTTOMLEFT", 0, -4)

    fg1IntensityBtn = CreateCycleButton(scrollChild, "Layer 1 Level", currentFg1Intensity, { "Normal", "Intense", "Faint" }, function(val) 
        currentFg1Intensity = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1IntensityBtn:SetPoint("TOPLEFT", fg1CycleBtn, "BOTTOMLEFT", 0, -4)

    fg1SpeedBtn = CreateCycleButton(scrollChild, "Layer 1 Speed", currentFg1Speed, { "Normal", "Fast", "Slow" }, function(val) 
        currentFg1Speed = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1SpeedBtn:SetPoint("TOPLEFT", fg1IntensityBtn, "BOTTOMLEFT", 0, -4)

    local fg1CbH = CreateCheckbox(scrollChild, "Horizontal Scroll", currentFg1HScroll, function(checked)
        currentFg1HScroll = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1CbH:SetPoint("TOPLEFT", fg1SpeedBtn, "BOTTOMLEFT", 0, -6)

    local fg1CbV = CreateCheckbox(scrollChild, "Vertical Scroll", currentFg1VScroll, function(checked)
        currentFg1VScroll = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1CbV:SetPoint("TOPLEFT", fg1CbH, "TOPLEFT", 155, 0)

    local fg1CbIH = CreateCheckbox(scrollChild, "Invert Horizontal", currentFg1InvertH, function(checked)
        currentFg1InvertH = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1CbIH:SetPoint("TOPLEFT", fg1CbH, "BOTTOMLEFT", 0, -4)

    local fg1CbIV = CreateCheckbox(scrollChild, "Invert Vertical", currentFg1InvertV, function(checked)
        currentFg1InvertV = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1CbIV:SetPoint("TOPLEFT", fg1CbV, "BOTTOMLEFT", 0, -4)

    local fg1CbSpin = CreateCheckbox(scrollChild, "Enable Spin", currentFg1Spin, function(checked)
        currentFg1Spin = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1CbSpin:SetPoint("TOPLEFT", fg1CbIH, "BOTTOMLEFT", 0, -4)

    local fg1CbThrob = CreateCheckbox(scrollChild, "Enable Throb", currentFg1Throb, function(checked)
        currentFg1Throb = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg1CbThrob:SetPoint("TOPLEFT", fg1CbSpin, "TOPLEFT", 155, 0)

    --------------------------------------------------------
    -- Category 3: Parallax Layer 2 Controls
    --------------------------------------------------------
    local lblFg2 = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    lblFg2:SetPoint("TOPLEFT", fg1CbSpin, "BOTTOMLEFT", 0, -12)
    lblFg2:SetText("3. Parallax Layer 2 (Overtop):")

    fg2CycleBtn = CreateCycleButton(scrollChild, "Layer 2 Asset", currentFg2Name, FOREGROUND_LIST, function(val) 
        currentFg2Name = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2CycleBtn:SetPoint("TOPLEFT", lblFg2, "BOTTOMLEFT", 0, -4)

    fg2IntensityBtn = CreateCycleButton(scrollChild, "Layer 2 Level", currentFg2Intensity, { "Normal", "Intense", "Faint" }, function(val) 
        currentFg2Intensity = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2IntensityBtn:SetPoint("TOPLEFT", fg2CycleBtn, "BOTTOMLEFT", 0, -4)

    fg2SpeedBtn = CreateCycleButton(scrollChild, "Layer 2 Speed", currentFg2Speed, { "Normal", "Fast", "Slow" }, function(val) 
        currentFg2Speed = val 
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2SpeedBtn:SetPoint("TOPLEFT", fg2IntensityBtn, "BOTTOMLEFT", 0, -4)

    local fg2CbH = CreateCheckbox(scrollChild, "Horizontal Scroll", currentFg2HScroll, function(checked)
        currentFg2HScroll = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2CbH:SetPoint("TOPLEFT", fg2SpeedBtn, "BOTTOMLEFT", 0, -6)

    local fg2CbV = CreateCheckbox(scrollChild, "Vertical Scroll", currentFg2VScroll, function(checked)
        currentFg2VScroll = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2CbV:SetPoint("TOPLEFT", fg2CbH, "TOPLEFT", 155, 0)

    local fg2CbIH = CreateCheckbox(scrollChild, "Invert Horizontal", currentFg2InvertH, function(checked)
        currentFg2InvertH = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2CbIH:SetPoint("TOPLEFT", fg2CbH, "BOTTOMLEFT", 0, -4)

    local fg2CbIV = CreateCheckbox(scrollChild, "Invert Vertical", currentFg2InvertV, function(checked)
        currentFg2InvertV = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2CbIV:SetPoint("TOPLEFT", fg2CbV, "BOTTOMLEFT", 0, -4)

    local fg2CbSpin = CreateCheckbox(scrollChild, "Enable Spin", currentFg2Spin, function(checked)
        currentFg2Spin = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2CbSpin:SetPoint("TOPLEFT", fg2CbIH, "BOTTOMLEFT", 0, -4)

    local fg2CbThrob = CreateCheckbox(scrollChild, "Enable Throb", currentFg2Throb, function(checked)
        currentFg2Throb = checked
        LM_PlayDualConfig(currentColor.r, currentColor.g, currentColor.b, currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, currentFg1HScroll, currentFg1VScroll, currentFg1InvertH, currentFg1InvertV, currentFg1Spin, currentFg1Throb,
            currentFg2Name, currentFg2Intensity, currentFg2Speed, currentFg2HScroll, currentFg2VScroll, currentFg2InvertH, currentFg2InvertV, currentFg2Spin, currentFg2Throb, false)
    end)
    fg2CbThrob:SetPoint("TOPLEFT", fg2CbSpin, "TOPLEFT", 155, 0)

    --------------------------------------------------------
    -- Category 4: Cross-Realm Whisper Handshake Controls
    --------------------------------------------------------
    local lblSync = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    lblSync:SetPoint("TOPLEFT", fg2CbSpin, "BOTTOMLEFT", 0, -12)
    lblSync:SetText("4. Cross-Realm Whisper Handshake:")

    local passTorchBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate")
    passTorchBtn:SetSize(310, 24)
    passTorchBtn:SetPoint("TOPLEFT", lblSync, "BOTTOMLEFT", 0, -4)
    passTorchBtn:SetText("Pass Torch / Follow Target's Feed")
    
    passTorchBtn:SetScript("OnClick", function()
        local targetName = GetNormalizedUnitName("target")
        local selfName = GetNormalizedUnitName("player")

        if targetName and targetName ~= "" and UnitIsPlayer("target") then
            if targetName == selfName then
                print("TorchLink Error: You cannot sync to yourself! Use 'Claim Conductor Role' instead.")
                return
            end

            if HouseAurasDB.conductorName then
                C_ChatInfo.SendAddonMessage(ADDON_PREFIX, "UNSUB", "WHISPER", HouseAurasDB.conductorName)
            end

            HouseAurasDB.isConductor = false
            HouseAurasDB.conductorName = targetName
            
            C_ChatInfo.SendAddonMessage(ADDON_PREFIX, "SUB", "WHISPER", targetName)
            print(string.format("TorchLink: Subscribed to cross-realm feed -> [%s].", targetName))
        else
            print("TorchLink Error: Target a valid player to follow their lighting.")
        end
    end)

    local claimConductorBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate")
    claimConductorBtn:SetSize(310, 24)
    claimConductorBtn:SetPoint("TOPLEFT", passTorchBtn, "BOTTOMLEFT", 0, -6)
    claimConductorBtn:SetText("Claim Conductor Role (Host Feed)")
    claimConductorBtn:SetScript("OnClick", function()
        local selfName = GetNormalizedUnitName("player")
        HouseAurasDB.isConductor = true
        HouseAurasDB.conductorName = selfName
        HouseAurasDB.followers = {}
        print(string.format("TorchLink: You are now hosting the Conductor feed [%s].", selfName))
    end)

    local desyncBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate")
    desyncBtn:SetSize(310, 24)
    desyncBtn:SetPoint("TOPLEFT", claimConductorBtn, "BOTTOMLEFT", 0, -6)
    desyncBtn:SetText("Disconnect / Unsubscribe")
    desyncBtn:SetScript("OnClick", function()
        if HouseAurasDB.conductorName and not HouseAurasDB.isConductor then
            C_ChatInfo.SendAddonMessage(ADDON_PREFIX, "UNSUB", "WHISPER", HouseAurasDB.conductorName)
        end
        HouseAurasDB.isConductor = false
        HouseAurasDB.conductorName = nil
        HouseAurasDB.followers = {}
        LM_StopAmbient()
        if lmForegroundOverlay1 then lmForegroundOverlay1:Hide() end
        if lmForegroundOverlay2 then lmForegroundOverlay2:Hide() end
        print("TorchLink: Disconnected and wiped subscription state.")
    end)

    --------------------------------------------------------
    -- Action Controls & Macro Export Generator
    --------------------------------------------------------
    local macroBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate")
    macroBtn:SetSize(310, 24)
    macroBtn:SetPoint("TOPLEFT", desyncBtn, "BOTTOMLEFT", 0, -12)
    macroBtn:SetText("Generate Action Bar Macro Command")
    macroBtn:SetScript("OnClick", function()
        local macroString = string.format("/run LM_PlayDualConfig(%f,%f,%f,'%s','%s','%s','%s','%s','%s',%s,%s,%s,%s,%s,%s,'%s','%s','%s',%s,%s,%s,%s,%s,%s,false)", 
            currentColor.r, currentColor.g, currentColor.b, 
            currentMode, currentIntensity, currentSpeed, 
            currentFg1Name, currentFg1Intensity, currentFg1Speed, tostring(currentFg1HScroll), tostring(currentFg1VScroll), tostring(currentFg1InvertH), tostring(currentFg1InvertV), tostring(currentFg1Spin), tostring(currentFg1Throb),
            currentFg2Name, currentFg2Intensity, currentFg2Speed, tostring(currentFg2HScroll), tostring(currentFg2VScroll), tostring(currentFg2InvertH), tostring(currentFg2InvertV), tostring(currentFg2Spin), tostring(currentFg2Throb))
        
        print("--- HouseAuras Macro String Generated ---")
        print(macroString)
        print("-----------------------------------------")
        
        if ChatEdit_ChooseBoxForSend then
            local editBox = ChatEdit_ChooseBoxForSend()
            if editBox then
                editBox:SetText(macroString)
                editBox:HighlightText()
            end
        end
    end)

    local setAfkBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate")
    setAfkBtn:SetSize(310, 24)
    setAfkBtn:SetPoint("TOPLEFT", macroBtn, "BOTTOMLEFT", 0, -6)
    setAfkBtn:SetText("Set Current Settings as AFK Theater")
    setAfkBtn:SetScript("OnClick", function()
        afkTheaterPreset = {
            r = currentColor.r, g = currentColor.g, b = currentColor.b,
            tMode = currentMode, tInt = currentIntensity, tSpd = currentSpeed,
            fg1Name = currentFg1Name, fg1Int = currentFg1Intensity, fg1Spd = currentFg1Speed,
            fg1HS = currentFg1HScroll, fg1VS = currentFg1VScroll,
            fg1IH = currentFg1InvertH, fg1IV = currentFg1InvertV,
            fg1Sp = currentFg1Spin, fg1Th = currentFg1Throb,
            fg2Name = currentFg2Name, fg2Int = currentFg2Intensity, fg2Speed = currentFg2Speed,
            fg2HS = currentFg2HScroll, fg2VS = currentFg2VScroll,
            fg2IH = currentFg2InvertH, fg2InvertV = currentFg2InvertV,
            fg2Sp = currentFg2Spin, fg2Th = currentFg2Throb
        }
        print("HouseAuras: AFK Theater preset captured!")
    end)

    local resetBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate")
    resetBtn:SetSize(310, 24)
    resetBtn:SetPoint("TOPLEFT", setAfkBtn, "BOTTOMLEFT", 0, -6)
    resetBtn:SetText("Reset Defaults")
    resetBtn:SetScript("OnClick", function()
        LM_StopAmbient()
        if lmForegroundOverlay1 then lmForegroundOverlay1:Hide() end
        if lmForegroundOverlay2 then lmForegroundOverlay2:Hide() end
        
        currentMode = "Solid"; currentIntensity = "Normal"; currentSpeed = "Normal"
        currentColor = { r = 1.0, g = 0.7, b = 0.3 }
        currentFg1Name = "None"; currentFg1Intensity = "Normal"; currentFg1Speed = "Normal"
        currentFg1HScroll = true; currentFg1VScroll = true; currentFg1InvertH = false; currentFg1InvertV = false; currentFg1Spin = false; currentFg1Throb = false
        currentFg2Name = "None"; currentFg2Intensity = "Normal"; currentFg2Speed = "Normal"
        currentFg2HScroll = true; currentFg2VScroll = true; currentFg2InvertH = false; currentFg2InvertV = false; currentFg2Spin = false; currentFg2Throb = false

        if modeBtn then modeBtn.val = "Solid"; modeBtn:SetText("Tint Mode: Solid") end
        if intensityBtn then intensityBtn.val = "Normal"; intensityBtn:SetText("Tint Level: Normal") end
        if speedBtn then speedBtn.val = "Normal"; speedBtn:SetText("Tint/Strobe Speed: Normal") end
        
        if fg1CycleBtn then fg1CycleBtn.val = "None"; fg1CycleBtn:SetText("Layer 1 Asset: None") end
        if fg1IntensityBtn then fg1IntensityBtn.val = "Normal"; fg1IntensityBtn:SetText("Layer 1 Level: Normal") end
        if fg1SpeedBtn then fg1SpeedBtn.val = "Normal"; fg1SpeedBtn:SetText("Layer 1 Speed: Normal") end
        
        if fg2CycleBtn then fg2CycleBtn.val = "None"; fg2CycleBtn:SetText("Layer 2 Asset: None") end
        if fg2IntensityBtn then fg2IntensityBtn.val = "Normal"; fg2IntensityBtn:SetText("Layer 2 Level: Normal") end
        if fg2SpeedBtn then fg2SpeedBtn.val = "Normal"; fg2SpeedBtn:SetText("Layer 2 Speed: Normal") end
        
        print("HouseAuras: Reset all settings.")
    end)

    local creditText = f:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    creditText:SetPoint("BOTTOM", 0, 12)
    creditText:SetText("By Ninejah-Proudmoore")

    LM_RestorePosition(f)
    lmFrame = f
    return f
end

SLASH_HOUSEAURAS1 = "/ha"
SlashCmdList["HOUSEAURAS"] = function(msg)
    local cmd = string.lower(msg or "")
    local frame = lmFrame or LM_BuildPanel()

    if cmd == "off" or cmd == "stop" then
        LM_StopAmbient()
        if lmForegroundOverlay1 then lmForegroundOverlay1:Hide() end
        if lmForegroundOverlay2 then lmForegroundOverlay2:Hide() end
        local overlay, _, _ = LM_EnsureOverlays()
        overlay:SetBackdropColor(0, 0, 0, 0.60)
        overlay:Show()
        currentMode = "Solid"
        currentColor = { r = 0, g = 0, b = 0 }
        if modeBtn then modeBtn.val = "Solid"; modeBtn:SetText("Tint Mode: Solid") end
        print("HouseAuras: Ambient lighting blackout activated.")
    else
        if frame:IsShown() then
            frame:Hide()
        else
            frame:Show()
        end
    end
end