local addonName, ns = ...
local L = ns.L

local cache = {}
local W = ns.Widgets
local C = ns.COLORS

-- BrightQoL-style reload popup for minimap changes
local minimapReloadFrame

local function ShowMinimapReloadPopup()
    if minimapReloadFrame then
        minimapReloadFrame:Show()
        minimapReloadFrame:Raise()
        return
    end

    local f = CreateFrame("Frame", "BrightQoL_MinimapReloadPrompt", UIParent, "BackdropTemplate")
    f:SetSize(300, 110)
    f:SetPoint("CENTER", UIParent, "CENTER", 0, 100)
    f:SetFrameStrata("DIALOG")
    f:SetToplevel(true)
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop",  f.StopMovingOrSizing)
    f:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    f:SetBackdropColor(0.05, 0.05, 0.08, 0.97)
    f:SetBackdropBorderColor(0.2, 0.2, 0.25, 0.9)
    tinsert(UISpecialFrames, "BrightQoL_MinimapReloadPrompt")

    -- Accent line top
    local accent = f:CreateTexture(nil, "OVERLAY")
    accent:SetHeight(2)
    accent:SetPoint("TOPLEFT",  f, "TOPLEFT",  1, -1)
    accent:SetPoint("TOPRIGHT", f, "TOPRIGHT", -1, -1)
    accent:SetColorTexture(0, 207/255, 1, 1)

    -- Body text
    local body = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    body:SetPoint("TOP", f, "TOP", 0, -18)
    body:SetWidth(260)
    body:SetJustifyH("CENTER")
    body:SetWordWrap(true)
    body:SetText("|cff00ccffBrightMinimap|r requires a UI reload to apply changes.")

    -- Reload button
    local btnReload = ns.Widgets:CreateButton(f, { text = "Reload Now", width = 120, height = 26 })
    btnReload:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 20, 16)
    btnReload:SetScript("OnClick", function() ReloadUI() end)

    -- Later button
    local btnLater = ns.Widgets:CreateButton(f, { text = "Later", width = 120, height = 26 })
    btnLater:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -20, 16)
    btnLater:SetScript("OnClick", function() f:Hide() end)

    minimapReloadFrame = f
    f:Show()
end

local function GetBMM() return _G["BrightQoL_Minimap"] end
local function GetDB()
    local bmm = GetBMM()
    return bmm and bmm.db and bmm.db.profile
end

---------------------------------------------------------------------------
-- Live helpers
---------------------------------------------------------------------------
local function LiveBorderColor()
    local bmm = GetBMM()
    if not bmm or not bmm.backdrop then return end
    local c = bmm.db.profile.colorBorder or {0,0,0,1}
    bmm.backdrop:SetColorTexture(c[1],c[2],c[3],c[4] or 1)
end
local function LiveBorderSize()
    local bmm = GetBMM()
    if not bmm or not bmm.backdrop then return end
    local db = bmm.db.profile
    local s = (db.size or 200)+(db.borderSize or 5)
    bmm.backdrop:SetSize(s,s)
end
local function LiveScale()
    local db = GetDB(); if not db then return end
    Minimap:SetScale(db.scale or 1.0)
end

local function LiveHideButtonCircles(hide)
    local bmm = GetBMM(); if not bmm then return end
    local alpha = hide and 0 or 1
    local refs = {
        bmm.zoomInOverlay,  bmm.zoomInBackground,
        bmm.zoomOutOverlay, bmm.zoomOutBackground,
        bmm.mailOverlay,    bmm.mailBackground,
    }
    for _, tex in ipairs(refs) do
        if tex then pcall(function() tex:SetAlpha(alpha) end) end
    end
end
local function LiveFont(path)
    local bmm = GetBMM(); if not bmm then return end
    BrightQoL.minimap.textFont = path
    local function trySet(fs, size)
        if fs then pcall(function() fs:SetFont(path, size or 12, "OUTLINE") end) end
    end
    if bmm.clock    then trySet(bmm.clock.text,    bmm.db.profile.clockConfig.fontSize) end
    if bmm.zonetext then trySet(bmm.zonetext.text, bmm.db.profile.zoneTextConfig.fontSize) end
    if bmm.coords   then trySet(bmm.coords,        bmm.db.profile.coordsConfig.fontSize) end
end
local function ApplyClock()
    local bmm = GetBMM()
    if not bmm or not bmm.clock or not bmm.backdrop then return end
    local db = bmm.db.profile
    bmm.clock:ClearAllPoints()
    bmm.clock:SetPoint("TOPLEFT",bmm.backdrop,"BOTTOMLEFT",db.clockConfig.x or 0,db.clockConfig.y or -4)
    if db.clock then bmm.clock:SetParent(Minimap) else bmm.clock:SetParent(bmm) end
end
local function ApplyCoords()
    local bmm = GetBMM()
    if not bmm or not bmm.coords or not bmm.backdrop then return end
    local db = bmm.db.profile
    bmm.coords:ClearAllPoints()
    bmm.coords:SetPoint("TOPRIGHT",bmm.backdrop,"BOTTOMRIGHT",db.coordsConfig.x or 0,db.coordsConfig.y or -4)
    if db.coords then bmm.coords:SetParent(Minimap); bmm.coords.shown=true
    else          bmm.coords:SetParent(bmm);         bmm.coords.shown=false end
end
local function ApplyZoneText()
    local bmm = GetBMM()
    if not bmm or not bmm.zonetext or not bmm.backdrop then return end
    local db = bmm.db.profile
    bmm.zonetext:ClearAllPoints()
    bmm.zonetext:SetPoint("BOTTOM",bmm.backdrop,"TOP",db.zoneTextConfig.x or 0,db.zoneTextConfig.y or 3)
    if db.zoneText then bmm.zonetext:SetParent(Minimap) else bmm.zonetext:SetParent(bmm) end
end
local raidDiffHooked = false
local raidDiffApplying = false  -- re-entrancy guard
local function ApplyRaidDiffIcon()
    if raidDiffApplying then return end
    local bmm = GetBMM()
    if not bmm or not bmm.backdrop then return end
    local db = GetDB(); if not db then return end
    local cfg = db.raidDiffIconConfig or { x = 0, y = 0 }
    local frame = MinimapCluster and MinimapCluster.InstanceDifficulty
    if not frame then return end
    raidDiffApplying = true
    frame:ClearAllPoints()
    frame:SetPoint("CENTER", bmm.backdrop, "CENTER", cfg.x or 0, cfg.y or 0)
    raidDiffApplying = false
    -- Hook Blizzard's SetPoint to prevent it from resetting our position
    if not raidDiffHooked then
        raidDiffHooked = true
        hooksecurefunc(frame, "SetPoint", function()
            if raidDiffApplying then return end  -- prevent recursion
            local d = GetDB(); if not d or not d.raidDiffIconConfig then return end
            if d.raidDiffIcon then
                local c = d.raidDiffIconConfig
                local bm = GetBMM(); if not bm or not bm.backdrop then return end
                raidDiffApplying = true
                frame:ClearAllPoints()
                frame:SetPoint("CENTER", bm.backdrop, "CENTER", c.x or 0, c.y or 0)
                raidDiffApplying = false
            end
        end)
    end
end

local function LiveButton(key, show)
    local bmm = GetBMM(); if not bmm then return end
    local function sp(fr,par) if fr then bmm.SetParent(fr,par) end end
    if key=="zoomBtn" then
        sp(Minimap.ZoomIn  or _G["MinimapZoomIn"],  show and Minimap or bmm)
        sp(Minimap.ZoomOut or _G["MinimapZoomOut"], show and Minimap or bmm)
    elseif key=="mail"             then sp(MinimapCluster.IndicatorFrame.MailFrame,          show and Minimap or bmm)
    elseif key=="craftingOrder"    then sp(MinimapCluster.IndicatorFrame.CraftingOrderFrame, show and Minimap or bmm)
    elseif key=="addonCompartment" then sp(AddonCompartmentFrame, show and Minimap or bmm)
    elseif key=="missions"         then sp(ExpansionLandingPageMinimapButton, show and Minimap or bmm)
    elseif key=="raidDiffIcon" then
        if MinimapCluster and MinimapCluster.InstanceDifficulty then
            sp(MinimapCluster.InstanceDifficulty, show and Minimap or bmm)
        end
    end
end
local function LiveHideAddons(hide)
    local ok, ldbi = pcall(function() return LibStub("LibDBIcon-1.0") end)
    if not ok or not ldbi then return end
    local tbl = ldbi:GetButtonList()
    for i = 1, #tbl do
        ldbi:ShowOnEnter(tbl[i], hide)
    end
end

-- Border color proxy
local function MakeColorProxy(db)
    return setmetatable({},{
        __index=function(_,k)
            local c=db.colorBorder or {0,0,0,1}
            if k=="r" then return c[1] elseif k=="g" then return c[2] elseif k=="b" then return c[3] end
        end,
        __newindex=function(_,k,v)
            if not db.colorBorder then db.colorBorder={0,0,0,1} end
            if k=="r" then db.colorBorder[1]=v elseif k=="g" then db.colorBorder[2]=v elseif k=="b" then db.colorBorder[3]=v end
        end,
    })
end

---------------------------------------------------------------------------
-- Element drag overlays
---------------------------------------------------------------------------
local elementOverlays = {}
local function MakeElementOverlay(name, label, getFrame, getCfg, applyFn)
    if elementOverlays[name] then return end
    local ov = CreateFrame("Frame",nil,UIParent,"BackdropTemplate")
    ov:SetFrameStrata("HIGH"); ov:SetMovable(true); ov:SetClampedToScreen(true)
    ov:RegisterForDrag("LeftButton"); ov:EnableMouse(true)
    ov:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8",edgeFile="Interface\\Buttons\\WHITE8X8",edgeSize=2})
    ov:SetBackdropColor(0,0.45,1,0.22)
    do local ar,ag,ab=ns.Theme.GetAccent(); ov:SetBackdropBorderColor(ar,ag,ab,0.95) end
    local lbl=ov:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
    lbl:SetPoint("TOP", 0, -4); lbl:SetTextColor(1,1,1,0.9); lbl:SetText("|cff00cfff"..label.."|r")
    local sx,sy=0,0
    ov:SetScript("OnDragStart",function(self) sx,sy=self:GetCenter(); self:StartMoving() end)
    ov:SetScript("OnDragStop",function(self)
        self:StopMovingOrSizing()
        local ex,ey=self:GetCenter(); local cfg=getCfg()
        if cfg then cfg.x=(cfg.x or 0)+math.floor(ex-sx); cfg.y=(cfg.y or 0)+math.floor(ey-sy) end
        applyFn()
        local lf=getFrame()
        if lf then self:ClearAllPoints(); self:SetPoint("CENTER",lf,"CENTER") end
    end)
    ov:Hide(); elementOverlays[name]=ov
end
local function ShowElementOverlay(name, show, getFrame)
    local ov=elementOverlays[name]; if not ov then return end
    if show then
        local lf=getFrame()
        if lf and lf:IsShown() then
            ov:SetSize(math.max(lf:GetWidth()+20,60),math.max(lf:GetHeight()+14,24))
            ov:ClearAllPoints(); ov:SetPoint("CENTER",lf,"CENTER"); ov:Show()
        end
    else ov:Hide() end
end
local function HideAllOverlays() for _,ov in pairs(elementOverlays) do ov:Hide() end end

---------------------------------------------------------------------------
-- Config panel
---------------------------------------------------------------------------
function ns:InitMinimap()
    local p = ns.MainFrame.Content

    W:CachedPanel(cache, "mmFrame", p, function(f)
        local sf, sc = W:CreateScrollFrame(f, 1100)

        W:CreatePageHeader(sc,
            {{"MINI",C.BLUE},{"MAP",C.ORANGE}},
            W.Colorize("Shape, size, border, clock, coordinates and more", C.GRAY))

        local bqolDB = BrightQoL.minimap

        -- ── MASTER ENABLE ─────────────────────────────────────────────────
        local killArea = CreateFrame("Frame",nil,sc,"BackdropTemplate")
        killArea:SetSize(600,62); killArea:SetPoint("TOPLEFT",10,-75)
        killArea:SetBackdrop({bgFile=[[Interface\Buttons\WHITE8x8]]})
        killArea:SetBackdropColor(0.01,0.56,0.91,0.08)

        local masterCB = W:CreateCheckbox(killArea,{
            label="Enable Minimap Module", db=bqolDB, key="enabled",
            x=15, y=-8, isMaster=true,
        })

        local note = sc:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
        note:SetPoint("TOPLEFT",killArea,"BOTTOMLEFT",0,-8)
        note:SetTextColor(1,0.82,0,0.85)
        note:SetText("|cffffcc00Note:|r Size and shape require reload. Border, scale, positions, buttons are live.")
        local reloadBtn = W:CreateButton(sc,{text="Reload UI",width=90,height=22})
        reloadBtn:SetPoint("LEFT",note,"RIGHT",10,0); reloadBtn:SetScript("OnClick",ReloadUI)

        -- ── SECTION CONTAINER ─────────────────────────────────────────────
        local sectionContainer = CreateFrame("Frame",nil,sc)
        sectionContainer:SetPoint("TOPLEFT",killArea,"BOTTOMLEFT",0,-46)
        sectionContainer:SetPoint("RIGHT",sc,"RIGHT",-10,0)
        sectionContainer:SetHeight(1000)
        sectionContainer:SetShown(bqolDB.enabled)

        local allSections={}
        local function RelayoutSections()
            local h=75+62+46
            for i,s in ipairs(allSections) do
                s:ClearAllPoints()
                if i==1 then s:SetPoint("TOPLEFT",sectionContainer,"TOPLEFT",0,0)
                else s:SetPoint("TOPLEFT",allSections[i-1],"BOTTOMLEFT",0,-12) end
                s:SetPoint("RIGHT",sectionContainer,"RIGHT",0,0)
                if bqolDB.enabled then h=h+s:GetHeight()+12 end
            end
            sc:SetHeight(math.max(h+40,600))
        end
        local function add(s) allSections[#allSections+1]=s end


        local db = GetDB()
        if not db then
            local warn=sectionContainer:CreateFontString(nil,"OVERLAY","GameFontNormal")
            warn:SetPoint("TOPLEFT",8,-8); warn:SetTextColor(1,0.82,0,1)
            warn:SetText("Minimap initialising — open this tab again after login.")
            sectionContainer:SetHeight(40)
            masterCB:HookScript("OnClick",function(self)
                bqolDB.enabled=self:GetChecked() and true or false
                sectionContainer:SetShown(bqolDB.enabled); RelayoutSections()
                if ns.RefreshSidebarDots then ns.RefreshSidebarDots() end
                ShowMinimapReloadPopup()
            end)
            RelayoutSections(); return
        end

        local bmm = GetBMM()
        MakeElementOverlay("clock",    "Clock",     function() return bmm and bmm.clock    end, function() return db.clockConfig    end, ApplyClock)
        MakeElementOverlay("coords",   "Coords",    function() return bmm and bmm.coords   end, function() return db.coordsConfig   end, ApplyCoords)
        MakeElementOverlay("zonetext", "Zone Text", function() return bmm and bmm.zonetext end, function() return db.zoneTextConfig end, ApplyZoneText)
        MakeElementOverlay("raidDiff", "Raid Diff Icon",
            function() return MinimapCluster and MinimapCluster.InstanceDifficulty end,
            function() return db.raidDiffIconConfig end,
            ApplyRaidDiffIcon)

        -- ── POSITION & UNLOCK ─────────────────────────────────────────────
        local posWrap, posContent = W:CreateCollapsibleSection(sectionContainer,{
            text="Position & Unlock", startOpen=true, onCollapse=RelayoutSections})
        add(posWrap)
        local GP=ns.Layout:New(3)
        W:CreateCheckbox(posContent,{label="Unlock Minimap & Buttons",x=GP:Col(1),y=GP:Row(1)+5,
            template="ChatConfigCheckButtonTemplate",checked=not db.lock,
            onChange=function(v)
                db.lock = not v
                db.unlock = v  -- BrightMinimap uses db.unlock for button drag
                Minimap:SetMovable(v)
            end})
        local resetBtn=W:CreateButton(posContent,{text="Reset Position",width=120,height=22})
        resetBtn:SetPoint("TOPLEFT",GP:Col(2),GP:Row(1)-14)
        resetBtn:SetScript("OnClick",function()
            db.position={"CENTER","CENTER",0,0}
            db.freeButtonPos = {}
            Minimap:ClearAllPoints(); Minimap:SetPoint("CENTER",UIParent,"CENTER",0,0)
        end)
        W:CreateCheckbox(posContent,{label="Unlock Clock",x=GP:Col(1),y=GP:Row(2)+5,
            template="ChatConfigCheckButtonTemplate",
            onChange=function(v) ShowElementOverlay("clock",v,function() return bmm and bmm.clock end) end})
        W:CreateCheckbox(posContent,{label="Unlock Coords",x=GP:Col(2),y=GP:Row(2)+5,
            template="ChatConfigCheckButtonTemplate",
            onChange=function(v) ShowElementOverlay("coords",v,function() return bmm and bmm.coords end) end})
        W:CreateCheckbox(posContent,{label="Unlock Zone Text",x=GP:Col(3),y=GP:Row(2)+5,
            template="ChatConfigCheckButtonTemplate",
            onChange=function(v) ShowElementOverlay("zonetext",v,function() return bmm and bmm.zonetext end) end})
        posContent:SetHeight(GP:Height(2)); posWrap:RecalcHeight()

        -- ── SHAPE & SIZE ─────────────────────────────────────────────────
        local shapeWrap, shapeContent = W:CreateCollapsibleSection(sectionContainer,{
            text="Shape & Size", startOpen=true, onCollapse=RelayoutSections})
        add(shapeWrap)
        local GS=ns.Layout:New(2)
        W:CreateDropdown(shapeContent,{label="Shape",x=GS:Col(1),y=GS:Row(1),width=160,
            options={{text="Square",value="SQUARE"},{text="Round",value="ROUND"}},db=db,key="shape"})
        W:CreateSlider(shapeContent,{label="Size (px)",db=db,key="size",min=100,max=350,step=2,
            x=GS:Col(2),y=GS:Row(1),width=190})
        W:CreateSlider(shapeContent,{label="Border Size",db=db,key="borderSize",
            min=0,max=30,step=1,x=GS:Col(1),y=GS:Row(2),width=190,onChange=function() LiveBorderSize() end})
        W:CreateSlider(shapeContent,{label="Scale",db=db,key="scale",
            min=0.5,max=2.0,step=0.05,x=GS:Col(2),y=GS:Row(2),width=190,onChange=function() LiveScale() end})
        local colorProxy=MakeColorProxy(db)
        W:CreateColorPicker(shapeContent,{label="Border Color",
            db=colorProxy,rKey="r",gKey="g",bKey="b",noClassColor=true,
            x=GS:Col(1),y=GS:Row(3),onChange=function() LiveBorderColor() end})
        W:CreateCheckbox(shapeContent,{
            label="Hide Button Circles (always on when enabled)",
            db=db, key="hideButtonCircles",
            x=GS:Col(1), y=GS:Row(4)+5,
            template="ChatConfigCheckButtonTemplate",
            onChange=function(v) LiveHideButtonCircles(v) end})

        shapeContent:SetHeight(GS:Height(4)); shapeWrap:RecalcHeight()

        -- ── DISPLAY ELEMENTS ─────────────────────────────────────────────
        local dispWrap, dispContent = W:CreateCollapsibleSection(sectionContainer,{
            text="Display Elements", startOpen=true, onCollapse=RelayoutSections})
        add(dispWrap)

        -- Running y tracker — starts at 0 (top of dispContent), each step goes negative
        local y = 0

        -- Font picker
        local fontLabel = dispContent:CreateFontString(nil,"OVERLAY","BrightQoL_FontHighSmall")
        fontLabel:SetPoint("TOPLEFT",8,y)
        fontLabel:SetText("TEXT FONT  |cff00cfff(clock · coords · zone text — live)|r")
        do local ar,ag,ab=ns.Theme.GetAccent(); fontLabel:SetTextColor(ar,ag,ab) end
        y = y - 20  -- 16px text + 4px gap
        W:CreateFontPicker(dispContent, 8, y, bqolDB.textFont or "", function(path) LiveFont(path) end)
        y = y - 36  -- 28px picker + 8px gap

        -- Auto Zoom + Hide Addons
        W:CreateCheckbox(dispContent,{label="Auto Zoom Out",db=db,key="autoZoom",
            x=8,y=y+5,template="ChatConfigCheckButtonTemplate"})
        W:CreateCheckbox(dispContent,{label="Hide Addon Icons (hover)",db=db,key="hideAddons",
            x=270,y=y+5,template="ChatConfigCheckButtonTemplate",
            onChange=function(v) LiveHideAddons(v) end})
        y = y - 38  -- 22px row + 16px gap before elements

        -- Per-element block layout constants:
        -- y + 0    → section label (16px)
        -- y - 22   → slider row (50px frame height): checkbox at x=8, slX at x=110, slY at x=310
        -- y - 80   → color picker button (26px)
        -- Block total = 80 + 26 + 18 = 124 → use 130 for gap
        local BLOCK = 130
        local LABEL_Y   =   0   -- section label relative to block top
        local SL_Y      = -22   -- slider row relative to block top
        local CP_Y      = -78   -- color picker relative to block top

        -- colorDb: optional proxy table for the color picker (defaults to cfgTable)
        local function ElementBlock(content, yTop, labelStr, showKey, cfgTable,
                                    xLo, xHi, yLo, yHi,
                                    rKey, gKey, bKey, applyFn, colorDb)
            colorDb = colorDb or cfgTable

            -- Section label
            local lbl = content:CreateFontString(nil,"OVERLAY","BrightQoL_FontHighSmall")
            lbl:SetPoint("TOPLEFT", 8, yTop + LABEL_Y)
            lbl:SetText(labelStr)
            do local ar,ag,ab=ns.Theme.GetAccent(); lbl:SetTextColor(ar,ag,ab) end

            -- Show checkbox
            W:CreateCheckbox(content,{label="Show",db=db,key=showKey,
                x=8, y=yTop+SL_Y+5,
                template="ChatConfigCheckButtonTemplate",
                onChange=function() applyFn() end})

            -- Offset X slider
            W:CreateSlider(content,{label="Offset X",db=cfgTable,key="x",
                min=xLo,max=xHi,step=1, x=100, y=yTop+SL_Y, width=160,
                onChange=function() applyFn() end})

            -- Offset Y slider
            W:CreateSlider(content,{label="Offset Y",db=cfgTable,key="y",
                min=yLo,max=yHi,step=1, x=330, y=yTop+SL_Y, width=155,
                onChange=function() applyFn() end})

            -- Color picker (uses colorDb so proxies work correctly)
            W:CreateColorPicker(content,{label="Color",
                db=colorDb, rKey=rKey, gKey=gKey, bKey=bKey, noClassColor=true,
                x=8, y=yTop+CP_Y,
                onChange=function() applyFn() end})
        end

        -- Clock: clockConfig has colorR/colorG/colorB as separate keys
        ElementBlock(dispContent, y, "CLOCK", "clock", db.clockConfig,
            -200,200,-100,100, "colorR","colorG","colorB",
            function()
                ApplyClock()
                local bmm2=GetBMM()
                if bmm2 and bmm2.clock and bmm2.clock.text then
                    local c=db.clockConfig
                    pcall(function() bmm2.clock.text:SetTextColor(c.colorR or 1,c.colorG or 1,c.colorB or 1,1) end)
                end
            end)
        y = y - BLOCK

        -- Coords: coordsConfig.color is array {r,g,b,a} — use proxy
        local coordColorProxy = setmetatable({},{
            __index=function(_,k)
                local c=db.coordsConfig.color or {1,1,1,1}
                if k=="cr" then return c[1] elseif k=="cg" then return c[2] elseif k=="cb" then return c[3] end
            end,
            __newindex=function(_,k,v)
                if not db.coordsConfig.color then db.coordsConfig.color={1,1,1,1} end
                if k=="cr" then db.coordsConfig.color[1]=v elseif k=="cg" then db.coordsConfig.color[2]=v elseif k=="cb" then db.coordsConfig.color[3]=v end
            end,
        })
        ElementBlock(dispContent, y, "COORDINATES", "coords", db.coordsConfig,
            -200,200,-100,100, "cr","cg","cb",
            function()
                ApplyCoords()
                local bmm2=GetBMM()
                if bmm2 and bmm2.coords then
                    local c=db.coordsConfig.color or {1,1,1,1}
                    pcall(function() bmm2.coords:SetTextColor(c[1],c[2],c[3],1) end)
                end
            end, coordColorProxy)
        y = y - BLOCK

        -- ZoneText: zoneTextConfig.colorNormal is array {r,g,b,a}
        local zoneColorProxy = setmetatable({},{
            __index=function(_,k)
                local c=db.zoneTextConfig.colorNormal or {1,0.82,0,1}
                if k=="zr" then return c[1] elseif k=="zg" then return c[2] elseif k=="zb" then return c[3] end
            end,
            __newindex=function(_,k,v)
                if not db.zoneTextConfig.colorNormal then db.zoneTextConfig.colorNormal={1,0.82,0,1} end
                if k=="zr" then db.zoneTextConfig.colorNormal[1]=v elseif k=="zg" then db.zoneTextConfig.colorNormal[2]=v elseif k=="zb" then db.zoneTextConfig.colorNormal[3]=v end
            end,
        })
        ElementBlock(dispContent, y, "ZONE TEXT", "zoneText", db.zoneTextConfig,
            -200,200,-50,100, "zr","zg","zb",
            function()
                ApplyZoneText()
                local bmm2=GetBMM()
                if bmm2 and bmm2.zonetext and bmm2.zonetext.text then
                    local c=db.zoneTextConfig.colorNormal or {1,0.82,0,1}
                    pcall(function() bmm2.zonetext.text:SetTextColor(c[1],c[2],c[3],1) end)
                end
            end, zoneColorProxy)
        y = y - BLOCK

        -- Raid Diff Icon: show/hide + X/Y sliders (no color picker)
        local rdLabel = dispContent:CreateFontString(nil,"OVERLAY","BrightQoL_FontHighSmall")
        rdLabel:SetPoint("TOPLEFT", 8, y + LABEL_Y)
        rdLabel:SetText("RAID DIFFICULTY ICON")
        do local ar,ag,ab=ns.Theme.GetAccent(); rdLabel:SetTextColor(ar,ag,ab) end

        W:CreateCheckbox(dispContent,{label="Show",db=db,key="raidDiffIcon",
            x=8, y=y+SL_Y+5,
            template="ChatConfigCheckButtonTemplate",
            onChange=function(v)
                LiveButton("raidDiffIcon", v)
                ShowElementOverlay("raidDiff", v, function()
                    return MinimapCluster and MinimapCluster.InstanceDifficulty
                end)
            end})

        if not db.raidDiffIconConfig then db.raidDiffIconConfig = { x = 0, y = 0 } end

        W:CreateSlider(dispContent,{label="Offset X",db=db.raidDiffIconConfig,key="x",
            min=-200,max=200,step=1, x=100, y=y+SL_Y, width=160,
            onChange=function()
                ApplyRaidDiffIcon()
                if rdOverlayOpen then ShowRaidDiffOverlay(true) end
            end})

        W:CreateSlider(dispContent,{label="Offset Y",db=db.raidDiffIconConfig,key="y",
            min=-200,max=200,step=1, x=330, y=y+SL_Y, width=155,
            onChange=function()
                ApplyRaidDiffIcon()
                if rdOverlayOpen then ShowRaidDiffOverlay(true) end
            end})

        local rdOverlayOpen = false
        local unlockRD = W:CreateButton(dispContent,{text="Unlock",width=70,height=22})
        unlockRD:SetPoint("TOPLEFT", 8, y+SL_Y-34)

        local function ShowRaidDiffOverlay(show)
            local ov = elementOverlays["raidDiff"]
            if not ov then return end
            if show then
                -- Anchor to minimap backdrop — visible even outside instances
                local bmm = GetBMM()
                local anchor = (bmm and bmm.backdrop) or Minimap
                local d = GetDB()
                local cx = d and d.raidDiffIconConfig and (d.raidDiffIconConfig.x or 0) or 0
                local cy = d and d.raidDiffIconConfig and (d.raidDiffIconConfig.y or 0) or 0
                ov:SetSize(44, 44)
                ov:ClearAllPoints()
                ov:SetPoint("CENTER", anchor, "CENTER", cx, cy)
                ov:Show()
            else
                ov:Hide()
            end
        end

        unlockRD:SetScript("OnClick", function()
            rdOverlayOpen = not rdOverlayOpen
            unlockRD.Text:SetText(rdOverlayOpen and "Lock" or "Unlock")
            ShowRaidDiffOverlay(rdOverlayOpen)
        end)

        y = y - 90

        dispContent:SetHeight(math.abs(y) + 50)
        dispWrap:RecalcHeight()

        -- ── MINIMAP BUTTONS ───────────────────────────────────────────────
        local btnsWrap, btnsContent = W:CreateCollapsibleSection(sectionContainer,{
            text="Minimap Buttons", startOpen=false, onCollapse=RelayoutSections})
        add(btnsWrap)
        local GB=ns.Layout:New(3)
        local btnDefs={
            {key="zoomBtn",label="Zoom Buttons"},{key="mail",label="Mail"},
            {key="craftingOrder",label="Crafting Orders"},{key="missions",label="Missions Button"},
            {key="addonCompartment",label="Addon Compartment"},{key="raidDiffIcon",label="Raid Diff Icon"},
        }
        for i,d in ipairs(btnDefs) do
            local row=math.ceil(i/3); local col=((i-1)%3)+1
            W:CreateCheckbox(btnsContent,{label=d.label,db=db,key=d.key,
                x=GB:Col(col),y=GB:Row(row)+5,template="ChatConfigCheckButtonTemplate",
                onChange=function(v)
                    LiveButton(d.key,v)
                    if d.key == "addonCompartment" then
                        local mbtn = _G["BrightQoL_MinimapButton"]
                        if mbtn then if v then mbtn:Hide() else mbtn:Show() end end
                    end
                end})
        end

        -- Position note
        local dragNote = btnsContent:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
        dragNote:SetPoint("TOPLEFT", 8, GB:Row(3) - 4)
        dragNote:SetTextColor(0.6, 0.6, 0.6, 1)
        dragNote:SetText("|cff888888When unlocked, drag Zoom, Mail and Addon Compartment buttons freely to reposition them. To move the Zoom Out (\"-\") button, make sure the map is not fully zoomed out.|r")
        dragNote:SetWidth(460)

        -- Live apply helper — moves a button relative to the minimap backdrop
        local function ApplyButtonPos(key)
            local bmm2 = GetBMM(); if not bmm2 then return end
            if not db.freeButtonPos then db.freeButtonPos = {} end
            local pos = db.freeButtonPos[key]; if not pos then return end
            local btnMap = {
                zoomIn           = function() return Minimap and (Minimap.ZoomIn  or _G["MinimapZoomIn"])  end,
                zoomOut          = function() return Minimap and (Minimap.ZoomOut or _G["MinimapZoomOut"]) end,
                mail             = function()
                    return MinimapCluster and MinimapCluster.IndicatorFrame
                           and MinimapCluster.IndicatorFrame.MailFrame
                end,
                addonCompartment = function() return AddonCompartmentFrame end,
            }
            local getBtn = btnMap[key]
            if getBtn then
                local btn = getBtn()
                local bd = bmm2 and bmm2.backdrop
                if btn and bd then
                    pos.set = true  -- mark as user-positioned
                    pcall(function()
                        bmm2.ClearAllPoints(btn)
                        bmm2.SetPoint(btn, "CENTER", bd, "CENTER", pos.x or 0, pos.y or 0)
                    end)
                end
            end
        end

        -- Per-button position sliders
        -- Layout: two rows per button (label + slider row)
        -- Slider X: x=8  w=200  |  Slider Y: x=295  w=200
        local SL_W  = 200   -- slider width
        local SL_X1 = 8     -- X slider left edge
        local SL_X2 = 295   -- Y slider left edge  (8 + 200 + 45editbox + 10gap + 32label = 295)
        local ROW_H = 56    -- px per button entry

        local posHeaderLbl = btnsContent:CreateFontString(nil,"OVERLAY","BrightQoL_FontHighSmall")
        posHeaderLbl:SetPoint("TOPLEFT", 8, GB:Row(3) - 34)
        posHeaderLbl:SetText("BUTTON POSITIONS")
        do local ar,ag,ab = ns.Theme.GetAccent(); posHeaderLbl:SetTextColor(ar,ag,ab) end

        local btnPosDefs = {
            { key="mail",            label="Mail"              },
            { key="addonCompartment",label="Addon Compartment" },
        }

        -- Ensure every entry exists before building sliders
        if not db.freeButtonPos then db.freeButtonPos = {} end
        for _, d in ipairs(btnPosDefs) do
            if not db.freeButtonPos[d.key] then
                db.freeButtonPos[d.key] = {x=0, y=0}
            end
        end

        local posBaseY = GB:Row(3) - 56   -- first button row starts here

        for idx, d in ipairs(btnPosDefs) do
            local yTop = posBaseY - (idx - 1) * ROW_H

            -- Sub-label
            local lbl = btnsContent:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
            lbl:SetPoint("TOPLEFT", SL_X1, yTop)
            lbl:SetTextColor(0.8, 0.8, 0.8, 1)
            lbl:SetText(d.label)

            local posEntry = db.freeButtonPos[d.key]

            W:CreateSlider(btnsContent, {
                label="X", db=posEntry, key="x",
                min=-500, max=500, step=1,
                x=SL_X1, y=yTop - 18, width=SL_W,
                onChange=function() ApplyButtonPos(d.key) end,
            })
            W:CreateSlider(btnsContent, {
                label="Y", db=posEntry, key="y",
                min=-500, max=500, step=1,
                x=SL_X2, y=yTop - 18, width=SL_W,
                onChange=function() ApplyButtonPos(d.key) end,
            })
        end

        local totalH = GB:Height(2) + 44 + 24 + #btnPosDefs * ROW_H + 20
        btnsContent:SetHeight(totalH); btnsWrap:RecalcHeight()

        -- ── MOUSE BINDINGS ────────────────────────────────────────────────
        local mouseWrap, mouseContent = W:CreateCollapsibleSection(sectionContainer,{
            text="Mouse Button Actions", startOpen=false, onCollapse=RelayoutSections})
        add(mouseWrap)
        local MOUSE_OPTS={{text="None",value="None"},{text="Left Button",value="LeftButton"},
            {text="Right Button",value="RightButton"},{text="Middle Button",value="MiddleButton"}}
        local GM=ns.Layout:New(2)
        local mouseDefs={{key="calendarBtn",label="Calendar"},{key="trackingBtn",label="Tracking Menu"},
            {key="missionsBtn",label="Missions"},{key="mapBtn",label="World Map"}}
        for i,d in ipairs(mouseDefs) do
            local row=math.ceil(i/2); local col=((i-1)%2)+1
            W:CreateDropdown(mouseContent,{label=d.label,x=GM:Col(col),y=GM:Row(row),
                width=160,options=MOUSE_OPTS,db=db,key=d.key})
        end
        mouseContent:SetHeight(GM:Height(2)); mouseWrap:RecalcHeight()

        -- ── WIRING ────────────────────────────────────────────────────────
        masterCB:HookScript("OnClick",function(self)
            bqolDB.enabled=self:GetChecked() and true or false
            sectionContainer:SetShown(bqolDB.enabled); RelayoutSections()
            if ns.RefreshSidebarDots then ns.RefreshSidebarDots() end
            if not bqolDB.enabled then HideAllOverlays() end
            ShowMinimapReloadPopup()
        end)
        if not ns.MainFrame.__minimapHideHooked then
            ns.MainFrame.__minimapHideHooked=true
            ns.MainFrame:HookScript("OnHide",function()
                HideAllOverlays()
local d=GetDB(); if d then d.lock=true end
            end)
        end
        RelayoutSections()
    end)
end
