local _, ns = ...

-----------------------------
-- Localization
-----------------------------
local L = {}
local locale = GetLocale()

L["nCastbar"]              = "nCastbar"
L["General"]               = "General"
L["Player"]                = "Player"
L["Target"]                = "Target"
L["Focus"]                 = "Focus"
L["Pet"]                   = "Pet"
L["Enable"]                = "Enable"
L["Width"]                 = "Width"
L["Height"]                = "Height"
L["Scale"]                 = "Scale"
L["Show Icon"]             = "Show Icon"
L["Icon Position"]         = "Icon Position"
L["Left"]                  = "Left"
L["Right"]                 = "Right"
L["Icon Outside"]          = "Icon Outside"
L["Show Latency"]          = "Show Latency"
L["Show Safezone"]         = "Show Safezone"
L["Class Color"]           = "Class Color"
L["Class Color Cast"]      = "Class Color for Cast"
L["class_color_cast_info"] = "Overrides the manual Cast Color below"
L["desc_enable"]           = "Show or hide this castbar entirely."
L["desc_width"]            = "Pixel width of the castbar."
L["desc_height"]           = "Pixel height / thickness of the castbar."
L["desc_scale"]            = "Scale multiplier applied to the entire castbar frame."
L["desc_dynamic_width"]    = "Shrinks the bar to match the remaining cast time."
L["desc_show_time"]        = "Displays the elapsed / remaining cast time as text on the bar."
L["desc_class_color"]      = "Tints the bar with your class colour instead of the manual Cast Colour."
L["desc_text_position"]    = "Where the spell name and time text are drawn relative to the bar."
L["desc_font_size"]        = "Font size for the spell-name label."
L["desc_center_text"]      = "Centre the spell name horizontally on the bar."
L["desc_font"]             = "Font face used for all castbar text (requires LibSharedMedia)."
L["desc_outline"]          = "Outline style applied to the spell-name font."
L["desc_show_icon"]        = "Shows the spell icon attached to the castbar."
L["desc_icon_outside"]     = "Moves the icon so it sits outside the bar boundary."
L["desc_icon_position"]    = "Which side of the bar the icon is attached to."
L["desc_cast_success"]     = "Briefly flashes the bar in the Success Colour on a completed cast."
L["desc_bar_texture"]      = "Statusbar texture used for the cast-progress fill (requires LibSharedMedia)."
L["desc_show_latency"]     = "Shows a latency / ping indicator at the end of the cast bar (player only)."
L["desc_show_safezone"]    = "Shows a safe-zone overlay based on your current latency (player only)."
L["Show Time"]             = "Show Cast Time"
L["Text Position"]         = "Text Position"
L["Inside"]                = "Inside"
L["Above"]                 = "Above"
L["Below"]                 = "Below"
L["Dynamic Width"]         = "Dynamic Width"
L["Spell Name Font Size"]  = "Spell Name Font Size"
L["Spell Name Font"]       = "Spell Name Font"
L["Spell Name Outline"]    = "Spell Name Outline"
L["Outline None"]          = "None"
L["Outline Normal"]        = "Normal"
L["Outline Thick"]         = "Thick"
L["Colors"]                = "Colors"
L["Cast Color"]            = "Cast Color"
L["Channel Color"]         = "Channel Color"
L["Non-Interruptible"]     = "Non-Interruptible"
L["Failed Color"]          = "Failed Color"
L["Safezone Color"]        = "Safezone Color"
L["Cast Success"]          = "Cast Success Flash"
L["Success Color"]         = "Success Color"
L["Edit Mode"]             = "Edit Mode"
L["Toggle Edit Mode"]      = "Toggle Edit Mode"
L["edit_mode_desc"]        = "In Edit Mode you can freely move the castbars. Click and drag the castbar anchors to position them."
L["Debug Options"]         = "Debug Options"
L["Debug Mode (Frame)"]    = "Debug Mode (Frame)"
L["Debug Chat"]            = "Debug Chat"
L["debug_info"]            = "Debug Mode: Shows detailed logs in a separate window\nDebug Chat: Shows cast events directly in chat"
L["Debug Chat Enabled"]    = "Debug-Chat:|r |cff00ff00Enabled|r"
L["Debug Chat Disabled"]   = "Debug-Chat:|r |cffff0000Disabled"
L["Reset Settings"]        = "Reset Settings"
L["Settings reset!"]       = "Settings reset!"
L["Open Settings"]         = "Open Settings"
L["Author"]                = "Author"
L["addon_desc"]            = "Modern castbar display extracted from NeavUI with Beautycase borders."
L["Layout"]                = "Layout"
L["Icon"]                  = "Icon"
L["Text"]                  = "Text"
L["Center Text"]           = "Center Text"
L["Bar Texture"]           = "Bar Texture"
L["Show Minimap Icon"]     = "Show Minimap Icon"
L["minimap_reload_title"]  = "UI Reload Required"
L["minimap_reload_body"]   = "Changing the minimap icon visibility requires a UI reload to take effect."
L["btn_reload_ui"]         = "Reload UI"
L["btn_cancel"]            = "Cancel"
L["Close"]                 = "Close"
L["GUI Scale"]             = "GUI Scale"

if locale == "deDE" then
    L["desc_enable"]           = "Zeigt oder versteckt diese Castbar."
    L["desc_width"]            = "Breite der Castbar in Pixeln."
    L["desc_height"]           = "Höhe / Dicke der Castbar in Pixeln."
    L["desc_scale"]            = "Skalierungsfaktor für den gesamten Castbar-Frame."
    L["desc_dynamic_width"]    = "Verkleinert den Balken proportional zur verbleibenden Castzeit."
    L["desc_show_time"]        = "Zeigt die vergangene / verbleibende Castzeit als Text auf dem Balken."
    L["desc_class_color"]      = "Färbt den Balken in der Klassenfarbe statt der manuell gewählten Castfarbe."
    L["desc_text_position"]    = "Position des Zaubernamens und der Zeitanzeige relativ zum Balken."
    L["desc_font_size"]        = "Schriftgröße für das Zaubername-Label."
    L["desc_center_text"]      = "Zentriert den Zaubernamen horizontal auf dem Balken."
    L["desc_font"]             = "Schriftart für alle Castbar-Texte (benötigt LibSharedMedia)."
    L["desc_outline"]          = "Konturstil für die Zaubername-Schrift."
    L["desc_show_icon"]        = "Zeigt das Zauber-Icon an der Castbar."
    L["desc_icon_outside"]     = "Verschiebt das Icon außerhalb des Balkens."
    L["desc_icon_position"]    = "Auf welcher Seite des Balkens das Icon angehängt wird."
    L["desc_cast_success"]     = "Blendet den Balken kurz in der Erfolgsfarbe auf, wenn ein Cast abgeschlossen wird."
    L["desc_bar_texture"]      = "Statusbalken-Textur für die Cast-Fortschrittsanzeige (benötigt LibSharedMedia)."
    L["desc_show_latency"]     = "Zeigt einen Latenz-/Ping-Indikator am Ende des Castbalkens (nur Spieler)."
    L["desc_show_safezone"]    = "Zeigt eine Safezone basierend auf der aktuellen Latenz (nur Spieler)."
    L["General"]               = "Allgemein"
    L["Player"]                = "Spieler"
    L["Target"]                = "Ziel"
    L["Focus"]                 = "Fokus"
    L["Pet"]                   = "Begleiter"
    L["Enable"]                = "Aktivieren"
    L["Width"]                 = "Breite"
    L["Height"]                = "Höhe"
    L["Scale"]                 = "Skalierung"
    L["Show Icon"]             = "Icon anzeigen"
    L["Icon Position"]         = "Icon-Position"
    L["Left"]                  = "Links"
    L["Right"]                 = "Rechts"
    L["Icon Outside"]          = "Icon außerhalb"
    L["Show Latency"]          = "Latenz anzeigen"
    L["Show Safezone"]         = "Safezone anzeigen"
    L["Class Color"]           = "Klassenfarbe"
    L["Class Color Cast"]      = "Klassenfarbe für Cast"
    L["class_color_cast_info"] = "Überschreibt die manuelle Cast-Farbe unten"
    L["Show Time"]             = "Castzeit anzeigen"
    L["Text Position"]         = "Text-Position"
    L["Inside"]                = "Innerhalb"
    L["Above"]                 = "Oberhalb"
    L["Below"]                 = "Unterhalb"
    L["Dynamic Width"]         = "Dynamische Breite"
    L["Spell Name Font Size"]  = "Schriftgröße Zaubername"
    L["Spell Name Font"]       = "Schriftart Zaubername"
    L["Spell Name Outline"]    = "Schriftkontur Zaubername"
    L["Outline None"]          = "Keine"
    L["Outline Normal"]        = "Normal"
    L["Outline Thick"]         = "Dick"
    L["Colors"]                = "Farben"
    L["Cast Color"]            = "Cast-Farbe"
    L["Channel Color"]         = "Channel-Farbe"
    L["Non-Interruptible"]     = "Nicht unterbrechbar"
    L["Failed Color"]          = "Fehlgeschlagen-Farbe"
    L["Safezone Color"]        = "Safezone-Farbe"
    L["Cast Success"]          = "Erfolgreicher Cast Flash"
    L["Success Color"]         = "Erfolgs-Farbe"
    L["Edit Mode"]             = "Bearbeitungsmodus"
    L["Toggle Edit Mode"]      = "Bearbeitungsmodus umschalten"
    L["edit_mode_desc"]        = "Im Bearbeitungsmodus kannst du die Castbars frei verschieben. Klicke und ziehe die Castbar-Anker, um sie zu positionieren."
    L["Debug Options"]         = "Debug-Optionen"
    L["Debug Mode (Frame)"]    = "Debug-Modus (Frame)"
    L["Debug Chat"]            = "Debug-Chat"
    L["debug_info"]            = "Debug-Modus: Zeigt detaillierte Logs in einem separaten Fenster\nDebug-Chat: Zeigt Cast-Events direkt im Chat"
    L["Debug Chat Enabled"]    = "Debug-Chat:|r |cff00ff00Aktiviert|r"
    L["Debug Chat Disabled"]   = "Debug-Chat:|r |cffff0000Deaktiviert"
    L["Reset Settings"]        = "Zurücksetzen"
    L["Settings reset!"]       = "Einstellungen zurückgesetzt!"
    L["Open Settings"]         = "Einstellungen öffnen"
    L["Author"]                = "Autor"
    L["addon_desc"]            = "Moderne Castbar-Anzeige extrahiert aus NeavUI mit Beautycase-Borders."
    L["Layout"]                = "Layout"
    L["Icon"]                  = "Icon"
    L["Text"]                  = "Text"
    L["Center Text"]           = "Text zentrieren"
    L["Bar Texture"]           = "Balkentextur"
    L["Show Minimap Icon"]     = "Minimap-Icon anzeigen"
    L["minimap_reload_title"]  = "UI-Reload erforderlich"
    L["minimap_reload_body"]   = "Das Ändern der Minimap-Icon-Sichtbarkeit erfordert einen UI-Reload."
    L["btn_reload_ui"]         = "UI neu laden"
    L["btn_cancel"]            = "Abbrechen"
    L["Close"]                 = "Schließen"
    L["GUI Scale"]             = "GUI-Skalierung"
end

-----------------------------
-- Color Scheme  (#ae4500)
-----------------------------
local C = {
    primary    = {0.682, 0.271, 0.000},
    primaryHi  = {0.85,  0.42,  0.08},
    primaryDim = {0.30,  0.10,  0.00},
    bg         = {0.050, 0.035, 0.025, 0.98},
    sidebar    = {0.040, 0.028, 0.018, 1.0},
    content    = {0.060, 0.042, 0.030, 0.97},
    panel      = {0.090, 0.065, 0.045, 0.95},
    border     = {0.25,  0.14,  0.06,  1.0},
    text       = {0.95,  0.90,  0.85},
    textDim    = {0.52,  0.40,  0.30},
    textBright = {1.00,  1.00,  1.00},
    separator  = {0.25,  0.14,  0.06,  0.55},
}

-- ── LAYOUT CONSTANTS ────────────────────────────────────────────────────────
local FRAME_W   = 860
local FRAME_H   = 640
local TITLE_H   = 52
local SIDEBAR_W = 170
local BOT_H     = 42
local PAD       = 16
local CONTENT_W = FRAME_W - SIDEBAR_W - 10

local FONT = "Fonts\\FRIZQT__.TTF"

-----------------------------
-- Low-level helpers
-----------------------------
local function Tex(parent, layer)
    return parent:CreateTexture(nil, layer or "ARTWORK")
end

local function FS(parent, template)
    return parent:CreateFontString(nil, "OVERLAY", template or "GameFontNormal")
end

local function FlatFrame(parent, r, g, b, a, br, bg_, bb, ba)
    local f = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    f:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
    f:SetBackdropColor(r or 0, g or 0, b or 0, a or 1)
    f:SetBackdropBorderColor(br or r or 0, bg_ or g or 0, bb or b or 0, ba or a or 1)
    return f
end

local function MakeBorder(frame, r, g, b, sz)
    sz = sz or 1
    local borders = {}
    local function Ln(a1, a2, horiz)
        local t = frame:CreateTexture(nil, "OVERLAY")
        t:SetPoint(a1); t:SetPoint(a2)
        if horiz then t:SetHeight(sz) else t:SetWidth(sz) end
        t:SetColorTexture(r, g, b, 1)
        borders[#borders+1] = t
    end
    Ln("TOPLEFT","TOPRIGHT",true); Ln("BOTTOMLEFT","BOTTOMRIGHT",true)
    Ln("TOPLEFT","BOTTOMLEFT",false); Ln("TOPRIGHT","BOTTOMRIGHT",false)
    frame._borders = borders
end

local function SetBorderColor(frame, r, g, b)
    if frame._borders then
        for _, t in ipairs(frame._borders) do t:SetColorTexture(r, g, b, 1) end
    end
end

-----------------------------
-- Section Header  (nBuff-style gradient line)
-----------------------------
local function SectionHeader(parent, text, anchor, xOfs, yOfs)
    local lbl = FS(parent, "GameFontNormal")
    lbl:SetPoint("TOPLEFT", anchor or parent, "TOPLEFT", xOfs or 0, yOfs or 0)
    lbl:SetTextColor(C.primary[1], C.primary[2], C.primary[3], 1)
    lbl:SetText(text)
    local line = Tex(parent, "BACKGROUND"); line:SetHeight(1)
    line:SetPoint("TOPLEFT",  lbl,    "BOTTOMLEFT",  0, -3)
    line:SetPoint("TOPRIGHT", parent, "TOPRIGHT",   -4, yOfs or 0)
    line:SetGradient("HORIZONTAL",
        CreateColor(C.primary[1], C.primary[2], C.primary[3], 0.7),
        CreateColor(0, 0, 0, 0))
    return lbl
end

-----------------------------
-- Styled Button  (dynamic width, nBuff-style)
-----------------------------
local BTN_PAD_X = 20

local function StyledButton(parent, text, fixedW, h, onClick)
    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
    btn:SetBackdropColor(C.primary[1]*0.12, C.primary[2]*0.08, C.primary[3]*0.12, 1)
    btn:SetBackdropBorderColor(C.primary[1]*0.55, C.primary[2]*0.45, C.primary[3]*0.80, 1)
    local lbl = FS(btn)
    lbl:SetPoint("CENTER")
    lbl:SetTextColor(C.primary[1], C.primary[2], C.primary[3], 1)
    lbl:SetText(text)
    local btnH = h or 26
    local function ApplySize()
        local btnW = fixedW
        if not btnW then
            local sw = lbl:GetStringWidth()
            btnW = (sw > 0) and (sw + BTN_PAD_X) or 110
        end
        btn:SetSize(btnW, btnH)
    end
    ApplySize()
    btn:SetScript("OnShow", function(self) self:SetScript("OnShow", nil); ApplySize() end)
    function btn:SetButtonText(t)
        lbl:SetText(t)
        if not fixedW then
            C_Timer.After(0, function()
                if btn and btn:GetParent() then
                    local sw = lbl:GetStringWidth()
                    btn:SetWidth((sw > 0) and (sw + BTN_PAD_X) or 110)
                end
            end)
        end
    end
    btn:SetScript("OnEnter", function(s)
        s:SetBackdropColor(C.primary[1]*0.28, C.primary[2]*0.22, C.primary[3]*0.32, 1)
        s:SetBackdropBorderColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1)
        lbl:SetTextColor(1,1,1,1)
    end)
    btn:SetScript("OnLeave", function(s)
        s:SetBackdropColor(C.primary[1]*0.12, C.primary[2]*0.08, C.primary[3]*0.12, 1)
        s:SetBackdropBorderColor(C.primary[1]*0.55, C.primary[2]*0.45, C.primary[3]*0.80, 1)
        lbl:SetTextColor(C.primary[1], C.primary[2], C.primary[3], 1)
    end)
    btn:SetScript("OnClick", onClick)
    return btn, lbl
end

local function DangerButton(parent, text, fixedW, h, onClick)
    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
    btn:SetBackdropColor(0.55, 0.08, 0.08, 1)
    btn:SetBackdropBorderColor(0.70, 0.15, 0.15, 1)
    local lbl = FS(btn)
    lbl:SetPoint("CENTER"); lbl:SetTextColor(1,1,1,1); lbl:SetText(text)
    local btnH = h or 26
    local function ApplySize()
        local btnW = fixedW
        if not btnW then
            local sw = lbl:GetStringWidth()
            btnW = (sw > 0) and (sw + BTN_PAD_X) or 110
        end
        btn:SetSize(btnW, btnH)
    end
    ApplySize()
    btn:SetScript("OnShow", function(self) self:SetScript("OnShow", nil); ApplySize() end)
    btn:SetScript("OnEnter", function(s)
        s:SetBackdropColor(0.80, 0.14, 0.14, 1)
        s:SetBackdropBorderColor(0.95, 0.25, 0.25, 1)
    end)
    btn:SetScript("OnLeave", function(s)
        s:SetBackdropColor(0.55, 0.08, 0.08, 1)
        s:SetBackdropBorderColor(0.70, 0.15, 0.15, 1)
    end)
    btn:SetScript("OnClick", onClick)
    return btn, lbl
end

-----------------------------
-- Modern Toggle  (nBuff-style pill switch)
-----------------------------
local TOGGLE_W = 36
local TOGGLE_H = 18
local KNOB_SZ  = 12

local function ModernToggle(parent, label, x, y, getter, setter, desc)
    local rowH = desc and 50 or 28
    local con = CreateFrame("Frame", nil, parent)
    con:SetSize(CONTENT_W - 20, rowH)
    con:SetPoint("TOPLEFT", parent, "TOPLEFT", x or 0, y or 0)

    local track = CreateFrame("Button", nil, con, "BackdropTemplate")
    track:SetSize(TOGGLE_W, TOGGLE_H)
    track:SetPoint("LEFT", con, "LEFT", 0, desc and 8 or 0)
    track:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8",
        edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})

    local knob = CreateFrame("Frame", nil, track, "BackdropTemplate")
    knob:SetSize(KNOB_SZ, KNOB_SZ)
    knob:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8",
        edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})

    local lbl = FS(con)
    lbl:SetPoint("LEFT", track, "RIGHT", 8, desc and 8 or 0)
    lbl:SetJustifyH("LEFT")
    lbl:SetTextColor(unpack(C.text))
    lbl:SetText(label)

    if desc then
        local d = FS(con, "GameFontHighlightSmall")
        d:SetPoint("TOPLEFT", lbl, "BOTTOMLEFT", 0, -2)
        d:SetPoint("RIGHT", con, "RIGHT", -4, 0)
        d:SetJustifyH("LEFT")
        d:SetTextColor(unpack(C.textDim))
        d:SetText(desc)
    end

    local function SetVisual(on)
        if on then
            track:SetBackdropColor(C.primary[1]*0.30, C.primary[2]*0.22, C.primary[3]*0.40, 1)
            track:SetBackdropBorderColor(C.primary[1]*0.70, C.primary[2]*0.58, C.primary[3]*0.90, 1)
            knob:ClearAllPoints(); knob:SetPoint("RIGHT", track, "RIGHT", -3, 0)
            knob:SetBackdropColor(C.primary[1], C.primary[2], C.primary[3], 1)
            knob:SetBackdropBorderColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1)
            lbl:SetTextColor(unpack(C.textBright))
        else
            track:SetBackdropColor(0.08, 0.06, 0.04, 1)
            track:SetBackdropBorderColor(unpack(C.border))
            knob:ClearAllPoints(); knob:SetPoint("LEFT", track, "LEFT", 3, 0)
            knob:SetBackdropColor(0.28, 0.18, 0.10, 1)
            knob:SetBackdropBorderColor(0.20, 0.12, 0.06, 1)
            lbl:SetTextColor(unpack(C.text))
        end
    end

    local function Refresh()
        local v = getter()
        SetVisual(v == true or v == 1)
    end
    Refresh()

    track:SetScript("OnEnter", function()
        local on = (getter() == true or getter() == 1)
        if on then track:SetBackdropBorderColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1)
        else        track:SetBackdropBorderColor(C.primary[1]*0.50, C.primary[2]*0.42, C.primary[3]*0.70, 1) end
    end)
    track:SetScript("OnLeave", function()
        local on = (getter() == true or getter() == 1)
        if on then track:SetBackdropBorderColor(C.primary[1]*0.70, C.primary[2]*0.58, C.primary[3]*0.90, 1)
        else        track:SetBackdropBorderColor(unpack(C.border)) end
    end)
    track:SetScript("OnClick", function()
        local newVal = not (getter() == true or getter() == 1)
        SetVisual(newVal); setter(newVal)
    end)

    con.Refresh = Refresh
    return con
end

-----------------------------
-- Modern Slider  (nBuff-style, native Slider widget + colored fill)
-----------------------------
local function ModernSlider(parent, label, desc, minV, maxV, step, anchor, yOfs, getter, setter, fmt)
    local rowH = 58
    local con = CreateFrame("Frame", nil, parent)
    con:SetSize(CONTENT_W - 20, rowH)
    con:SetPoint("TOPLEFT", anchor or parent, "TOPLEFT", 0, yOfs or 0)

    local lbl = FS(con); lbl:SetPoint("TOPLEFT", 0, 0)
    lbl:SetTextColor(unpack(C.text)); lbl:SetText(label)
    local valLbl = FS(con, "GameFontNormalSmall")
    valLbl:SetPoint("TOPRIGHT", 0, -1)
    valLbl:SetTextColor(C.primary[1], C.primary[2], C.primary[3], 1)

    if desc then
        local d = FS(con, "GameFontHighlightSmall")
        d:SetPoint("TOPLEFT", lbl, "BOTTOMLEFT", 0, -1)
        d:SetTextColor(unpack(C.textDim)); d:SetText(desc)
    end

    local track = Tex(con, "BACKGROUND"); track:SetHeight(3)
    track:SetPoint("BOTTOMLEFT",  con, "BOTTOMLEFT",   6, 14)
    track:SetPoint("BOTTOMRIGHT", con, "BOTTOMRIGHT", -6, 14)
    track:SetColorTexture(0.18, 0.18, 0.18, 1)

    local fill = Tex(con, "ARTWORK"); fill:SetHeight(3)
    fill:SetPoint("LEFT", track, "LEFT", 0, 0)
    fill:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 0.85)

    local sl = CreateFrame("Slider", nil, con)
    sl:SetHeight(18)
    sl:SetPoint("LEFT",  track, "LEFT",  0, 0)
    sl:SetPoint("RIGHT", track, "RIGHT", 0, 0)
    sl:SetMinMaxValues(minV, maxV); sl:SetValueStep(step)
    sl:SetObeyStepOnDrag(true); sl:SetValue(getter())
    sl:SetOrientation("HORIZONTAL")
    local thumb = Tex(sl, "OVERLAY"); thumb:SetSize(10, 14)
    thumb:SetTexture("Interface\\Buttons\\WHITE8X8")
    thumb:SetVertexColor(C.primary[1], C.primary[2], C.primary[3], 1)
    sl:SetThumbTexture(thumb)

    local function UpdateFill()
        local pct = math.max(0, (sl:GetValue()-minV)/math.max(maxV-minV, 0.001))
        local w = track:GetWidth(); if w and w > 0 then fill:SetWidth(w*pct) end
    end
    local function UpdateVal()
        local v = sl:GetValue()
        valLbl:SetText(fmt and fmt(v) or string.format("%.2f", v))
        UpdateFill(); setter(v)
    end
    sl:SetScript("OnValueChanged", UpdateVal)
    sl:SetScript("OnSizeChanged",  UpdateFill)
    sl:SetScript("OnEnter", function() thumb:SetVertexColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1) end)
    sl:SetScript("OnLeave", function() thumb:SetVertexColor(C.primary[1],   C.primary[2],   C.primary[3],   1) end)
    UpdateVal()
    con.slider  = sl
    con.Refresh = function() sl:SetValue(getter()); UpdateVal() end
    return con
end

-----------------------------
-- Dropdown  (nBuff-style popup)
-----------------------------
local activeDropdownPopup = nil
local function CloseActiveDropdown()
    if activeDropdownPopup then activeDropdownPopup:Hide(); activeDropdownPopup = nil end
end

local dropdownScrollGuard = CreateFrame("Frame")
dropdownScrollGuard:EnableMouseWheel(true)
dropdownScrollGuard:SetAllPoints(UIParent)
dropdownScrollGuard:SetFrameStrata("TOOLTIP")
dropdownScrollGuard:Hide()
dropdownScrollGuard:SetScript("OnMouseWheel", function(self)
    CloseActiveDropdown(); self:Hide()
end)

local _orig = CloseActiveDropdown
CloseActiveDropdown = function() _orig(); dropdownScrollGuard:Hide() end

local function ModernDropdown(parent, label, options, getter, setter, anchor, yOfs, width)
    local W      = width or 260
    local BTN_H  = 26
    local ITEM_H = 24
    local container = CreateFrame("Frame", nil, parent)
    container:SetSize(W, 52)
    container:SetPoint("TOPLEFT", anchor or parent, "TOPLEFT", 0, yOfs or 0)

    if label and label ~= "" then
        local lbl2 = FS(container, "GameFontHighlightSmall")
        lbl2:SetPoint("TOPLEFT", 0, 0)
        lbl2:SetTextColor(unpack(C.text)); lbl2:SetText(label)
    end

    local btn = CreateFrame("Button", nil, container, "BackdropTemplate")
    btn:SetSize(W, BTN_H); btn:SetPoint("TOPLEFT", 0, -20)
    btn:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
    btn:SetBackdropColor(0.14, 0.14, 0.14, 1)
    btn:SetBackdropBorderColor(unpack(C.border))

    local btnLbl = FS(btn, "GameFontHighlightSmall")
    btnLbl:SetPoint("LEFT", btn, "LEFT", 8, 0)
    btnLbl:SetPoint("RIGHT", btn, "RIGHT", -26, 0)
    btnLbl:SetJustifyH("LEFT")
    btnLbl:SetTextColor(unpack(C.text))

    local arrow = Tex(btn, "OVERLAY")
    arrow:SetSize(16, 16)
    arrow:SetPoint("RIGHT", btn, "RIGHT", -6, 0)
    arrow:SetTexture("Interface\\AddOns\\nCastbar\\Media\\logo_small")

    local function UpdateDisplay()
        local val = getter()
        for _, opt in ipairs(options) do
            if opt.value == val then btnLbl:SetText(opt.text); return end
        end
        btnLbl:SetText("–")
    end
    UpdateDisplay()

    local popup = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
    popup:SetFrameStrata("TOOLTIP")
    popup:SetWidth(W)
    popup:SetHeight(math.min(#options, 8) * ITEM_H + 4)
    popup:Hide()
    popup:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
    popup:SetBackdropColor(0.05, 0.06, 0.10, 0.97)
    popup:SetBackdropBorderColor(C.primary[1]*0.6, C.primary[2]*0.5, C.primary[3]*0.9, 1)

    for i, opt in ipairs(options) do
        local row = CreateFrame("Button", nil, popup, "BackdropTemplate")
        row:SetPoint("TOPLEFT",  popup, "TOPLEFT",  1, -(i-1)*ITEM_H-1)
        row:SetPoint("TOPRIGHT", popup, "TOPRIGHT", -1,-(i-1)*ITEM_H-1)
        row:SetHeight(ITEM_H)
        row:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", tile=false, edgeSize=0,
            insets={left=0,right=0,top=0,bottom=0}})
        row:SetBackdropColor(0,0,0,0)

        local pip = Tex(row, "ARTWORK"); pip:SetWidth(2); pip:SetHeight(ITEM_H-8)
        pip:SetPoint("LEFT", row, "LEFT", 4, 0)
        pip:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 1)

        local rowLbl = FS(row, "GameFontHighlightSmall")
        rowLbl:SetPoint("LEFT", row, "LEFT", 12, 0)
        rowLbl:SetTextColor(unpack(C.text)); rowLbl:SetText(opt.text)

        local function RefreshRow()
            if opt.value == getter() then
                rowLbl:SetTextColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1); pip:SetAlpha(1)
            else rowLbl:SetTextColor(unpack(C.text)); pip:SetAlpha(0) end
        end
        RefreshRow()

        row:SetScript("OnEnter", function(s) s:SetBackdropColor(C.primary[1]*0.15, C.primary[2]*0.12, C.primary[3]*0.18, 1) end)
        row:SetScript("OnLeave", function(s) s:SetBackdropColor(0,0,0,0) end)
        row:SetScript("OnClick", function()
            setter(opt.value); UpdateDisplay(); CloseActiveDropdown()
        end)
    end

    btn:SetScript("OnEnter", function(s) s:SetBackdropBorderColor(C.primary[1], C.primary[2], C.primary[3], 1) end)
    btn:SetScript("OnLeave", function(s) s:SetBackdropBorderColor(unpack(C.border)) end)
    btn:SetScript("OnClick", function()
        if popup:IsShown() then CloseActiveDropdown(); return end
        CloseActiveDropdown()
        popup:ClearAllPoints()
        local bLeft = btn:GetLeft(); local bBottom = btn:GetBottom(); local bTop = btn:GetTop()
        if bBottom and bBottom > (#options * ITEM_H + 20) then
            popup:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", bLeft, bBottom - 2)
        else
            popup:SetPoint("BOTTOMLEFT", nil, "BOTTOMLEFT", bLeft, (bTop or 0) + 2)
        end
        popup:Show(); activeDropdownPopup = popup; dropdownScrollGuard:Show()
    end)

    container.Refresh = UpdateDisplay
    return container
end

-----------------------------
-- Color Picker Row
-----------------------------
local function ColorPickerRow(parent, label, unit, colorKey, anchor, yOfs)
    local con = CreateFrame("Frame", nil, parent)
    con:SetSize(CONTENT_W - 20, 28)
    con:SetPoint("TOPLEFT", anchor or parent, "TOPLEFT", 0, yOfs or 0)

    local lbl = FS(con); lbl:SetPoint("LEFT", 0, 0)
    lbl:SetTextColor(unpack(C.text)); lbl:SetText(label)

    local box = CreateFrame("Button", nil, con, "BackdropTemplate")
    box:SetSize(46, 20); box:SetPoint("RIGHT", 0, 0)
    box:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
    box:SetBackdropBorderColor(unpack(C.border))
    local bbg = box:CreateTexture(nil, "BACKGROUND"); bbg:SetAllPoints()

    if not nCastbarDB.profile then nCastbarDB.profile = {} end
    if not nCastbarDB.profile[unit] then nCastbarDB.profile[unit] = {} end
    if not nCastbarDB.profile[unit].colors then nCastbarDB.profile[unit].colors = {} end

    local function UpdatePreview()
        local cc = nCastbarDB.profile[unit] and nCastbarDB.profile[unit].colors and nCastbarDB.profile[unit].colors[colorKey]
        if cc then bbg:SetColorTexture(cc[1],cc[2],cc[3],1)
        else bbg:SetColorTexture(0.4,0.4,0.4,1) end
    end
    UpdatePreview()

    box:SetScript("OnEnter", function() box:SetBackdropBorderColor(C.primary[1], C.primary[2], C.primary[3], 1) end)
    box:SetScript("OnLeave", function() box:SetBackdropBorderColor(unpack(C.border)) end)
    box:SetScript("OnClick", function()
        local cc = nCastbarDB.profile[unit] and nCastbarDB.profile[unit].colors and nCastbarDB.profile[unit].colors[colorKey]
        if not cc then return end
        ColorPickerFrame:SetupColorPickerAndShow({
            r=cc[1], g=cc[2], b=cc[3], hasOpacity=false,
            swatchFunc = function()
                local r,g,b = ColorPickerFrame:GetColorRGB()
                nCastbarDB.profile[unit].colors[colorKey] = {r,g,b}; UpdatePreview(); ns.Castbar:Reload()
            end,
            cancelFunc = function(p)
                nCastbarDB.profile[unit].colors[colorKey] = {p.r,p.g,p.b}; UpdatePreview(); ns.Castbar:Reload()
            end,
        })
    end)
    return con
end

-----------------------------
-- Scrollable content helper
-----------------------------
local function MakeScrollContent(parent)
    local TRACK_W = 8
    local sf = CreateFrame("ScrollFrame", nil, parent, "UIPanelScrollFrameTemplate")
    sf:SetPoint("TOPLEFT",     parent, "TOPLEFT",    2,          -42)
    sf:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT",-(TRACK_W+6), 4)

    local sc = CreateFrame("Frame", nil, sf)
    sc:SetWidth(CONTENT_W - 26)
    sc:SetHeight(2000)
    sf:SetScrollChild(sc)

    -- Style the scrollbar
    local sb = sf.ScrollBar
    if sb then
        if sb.ScrollUpButton   then sb.ScrollUpButton:SetAlpha(0);   sb.ScrollUpButton:Disable()   end
        if sb.ScrollDownButton then sb.ScrollDownButton:SetAlpha(0); sb.ScrollDownButton:Disable() end
        sb:SetWidth(TRACK_W)
        local trackTex = sb:CreateTexture(nil, "BACKGROUND")
        trackTex:SetAllPoints(sb)
        trackTex:SetColorTexture(0.05, 0.05, 0.08, 0.9)
        local thumb = sb.ThumbTexture or (sb.GetThumbTexture and sb:GetThumbTexture())
        if thumb then
            thumb:SetWidth(TRACK_W)
            thumb:SetTexture(nil)
            thumb:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 0.75)
        end
    end
    return sc, sf
end

-----------------------------
-- LSM Font Dropdown
-----------------------------
local function LSMFontDropdown(parent, label, unit, key, anchor, yOfs, width)
    local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
    if not LSM then return end
    width = width or 230
    local ITEM_H = 24

    local opts = {}
    for _, name in ipairs(LSM:List("font") or {}) do
        opts[#opts+1] = {value=name, text=name, path=LSM:Fetch("font",name)}
    end
    if #opts == 0 then return end

    local container = CreateFrame("Frame", nil, parent)
    container:SetSize(width, 52)
    container:SetPoint("TOPLEFT", anchor or parent, "TOPLEFT", 0, yOfs or 0)

    if label and label ~= "" then
        local lbl2 = FS(container, "GameFontHighlightSmall")
        lbl2:SetPoint("TOPLEFT", 0, 0)
        lbl2:SetTextColor(unpack(C.text)); lbl2:SetText(label)
    end

    local btn = CreateFrame("Button", nil, container, "BackdropTemplate")
    btn:SetSize(width, 26); btn:SetPoint("TOPLEFT", 0, -20)
    btn:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
    btn:SetBackdropColor(0.14, 0.14, 0.14, 1)
    btn:SetBackdropBorderColor(unpack(C.border))

    local btnLbl = FS(btn, "GameFontHighlightSmall")
    btnLbl:SetPoint("LEFT", btn, "LEFT", 8, 0); btnLbl:SetPoint("RIGHT", btn, "RIGHT", -26, 0)
    btnLbl:SetJustifyH("LEFT")
    btnLbl:SetTextColor(unpack(C.text))

    local arrow = Tex(btn, "OVERLAY")
    arrow:SetSize(16, 16)
    arrow:SetPoint("RIGHT", btn, "RIGHT", -6, 0)
    arrow:SetTexture("Interface\\AddOns\\nCastbar\\Media\\logo_small")

    if not nCastbarDB.profile then nCastbarDB.profile = {} end
    if unit and not nCastbarDB.profile[unit] then nCastbarDB.profile[unit] = {} end
    local function GetVal() return unit and nCastbarDB.profile[unit][key] or nCastbarDB.profile[key] end
    local function SetVal(v)
        if unit then nCastbarDB.profile[unit][key] = v else nCastbarDB.profile[key] = v end
    end
    btnLbl:SetText(GetVal() or "")

    btn:SetScript("OnEnter", function(s) s:SetBackdropBorderColor(C.primary[1], C.primary[2], C.primary[3], 1) end)
    btn:SetScript("OnLeave", function(s) s:SetBackdropBorderColor(unpack(C.border)) end)
    btn:SetScript("OnClick", function()
        if activeDropdownPopup then CloseActiveDropdown(); return end
        CloseActiveDropdown()
        local VISIBLE = math.min(#opts, 12)
        local menuH = VISIBLE * ITEM_H + 8

        local menu = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
        menu:SetSize(width, menuH)
        menu:SetFrameStrata("TOOLTIP"); menu:SetFrameLevel(200)
        menu:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
            tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
        menu:SetBackdropColor(0.05, 0.06, 0.10, 0.97)
        menu:SetBackdropBorderColor(C.primary[1]*0.6, C.primary[2]*0.5, C.primary[3]*0.9, 1)

        local bLeft = btn:GetLeft(); local bBottom = btn:GetBottom(); local bTop = btn:GetTop()
        if bBottom and bBottom > (VISIBLE * ITEM_H + 20) then
            menu:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", bLeft, bBottom - 2)
        else
            menu:SetPoint("BOTTOMLEFT", nil, "BOTTOMLEFT", bLeft, (bTop or 0) + 2)
        end

        local msf = CreateFrame("ScrollFrame", nil, menu, "UIPanelScrollFrameTemplate")
        msf:SetPoint("TOPLEFT", 0, -2); msf:SetPoint("BOTTOMRIGHT", -10, 2)
        local msc = CreateFrame("Frame", nil, msf); msc:SetSize(width-14, #opts * ITEM_H + 4); msf:SetScrollChild(msc)
        local msb = msf.ScrollBar
        if msb then
            if msb.ScrollUpButton   then msb.ScrollUpButton:SetAlpha(0);   msb.ScrollUpButton:Disable()   end
            if msb.ScrollDownButton then msb.ScrollDownButton:SetAlpha(0); msb.ScrollDownButton:Disable() end
            msb:SetWidth(6)
            local mt = msb:CreateTexture(nil, "BACKGROUND"); mt:SetAllPoints(msb); mt:SetColorTexture(0.05,0.05,0.08,0.9)
            local th = msb.ThumbTexture or (msb.GetThumbTexture and msb:GetThumbTexture())
            if th then th:SetWidth(6); th:SetTexture(nil); th:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 0.75) end
        end

        for i, opt in ipairs(opts) do
            local row = CreateFrame("Button", nil, msc, "BackdropTemplate")
            row:SetSize(width-24, ITEM_H)
            row:SetPoint("TOP", 0, -(i-1)*ITEM_H)
            row:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", tile=false, edgeSize=0,
                insets={left=0,right=0,top=0,bottom=0}})
            row:SetBackdropColor(0,0,0,0)
            local pip = Tex(row, "ARTWORK"); pip:SetWidth(2); pip:SetHeight(ITEM_H-8)
            pip:SetPoint("LEFT", row, "LEFT", 4, 0)
            pip:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 1)
            local rowLbl = FS(row, "GameFontHighlightSmall")
            rowLbl:SetPoint("LEFT", row, "LEFT", 12, 0); rowLbl:SetPoint("RIGHT", row, "RIGHT", -4, 0)
            rowLbl:SetJustifyH("LEFT")
            local ok = pcall(function() rowLbl:SetFont(opt.path, 11) end)
            if not ok then rowLbl:SetFont(FONT, 11) end
            rowLbl:SetText(opt.text)
            local cv = GetVal()
            if opt.value == cv then rowLbl:SetTextColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1); pip:SetAlpha(1)
            else rowLbl:SetTextColor(unpack(C.text)); pip:SetAlpha(0) end
            row:SetScript("OnEnter", function(s) s:SetBackdropColor(C.primary[1]*0.15, C.primary[2]*0.12, C.primary[3]*0.18, 1) end)
            row:SetScript("OnLeave", function(s) s:SetBackdropColor(0,0,0,0) end)
            row:SetScript("OnClick", function()
                SetVal(opt.value); btnLbl:SetText(opt.text)
                CloseActiveDropdown(); ns.Castbar:Reload()
            end)
        end
        menu:Show(); activeDropdownPopup = menu; dropdownScrollGuard:Show()
    end)
end

-----------------------------
-- LSM Statusbar Texture Dropdown
-----------------------------
local function LSMTextureDropdown(parent, label, unit, key, anchor, yOfs, width)
    local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
    if not LSM then return end
    width = width or 260
    local ITEM_H = 26

    local opts = {}
    for _, name in ipairs(LSM:List("statusbar") or {}) do
        opts[#opts+1] = {value=name, text=name, path=LSM:Fetch("statusbar",name)}
    end
    if #opts == 0 then return end

    local container = CreateFrame("Frame", nil, parent)
    container:SetSize(width, 52)
    container:SetPoint("TOPLEFT", anchor or parent, "TOPLEFT", 0, yOfs or 0)

    if label and label ~= "" then
        local lbl2 = FS(container, "GameFontHighlightSmall")
        lbl2:SetPoint("TOPLEFT", 0, 0)
        lbl2:SetTextColor(unpack(C.text)); lbl2:SetText(label)
    end

    local btn = CreateFrame("Button", nil, container, "BackdropTemplate")
    btn:SetSize(width, 26); btn:SetPoint("TOPLEFT", 0, -20)
    btn:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
    btn:SetBackdropColor(0.14, 0.14, 0.14, 1)
    btn:SetBackdropBorderColor(unpack(C.border))

    local btnPreview = Tex(btn, "ARTWORK"); btnPreview:SetSize(width*0.3, 12)
    btnPreview:SetPoint("LEFT", btn, "LEFT", 6, 0)

    local btnLbl = FS(btn, "GameFontHighlightSmall")
    btnLbl:SetPoint("LEFT", btnPreview, "RIGHT", 6, 0); btnLbl:SetPoint("RIGHT", btn, "RIGHT", -26, 0)
    btnLbl:SetJustifyH("LEFT"); btnLbl:SetTextColor(unpack(C.text))

    local arrow = Tex(btn, "OVERLAY")
    arrow:SetSize(16, 16)
    arrow:SetPoint("RIGHT", btn, "RIGHT", -6, 0)
    arrow:SetTexture("Interface\\AddOns\\nCastbar\\Media\\logo_small")

    if not nCastbarDB.profile then nCastbarDB.profile = {} end
    if unit and not nCastbarDB.profile[unit] then nCastbarDB.profile[unit] = {} end
    local function GetVal() return unit and nCastbarDB.profile[unit][key] or nCastbarDB.profile[key] end
    local function SetVal(v)
        if unit then nCastbarDB.profile[unit][key] = v else nCastbarDB.profile[key] = v end
    end
    local cur = GetVal() or "smooth_v2"
    btnLbl:SetText(cur)
    local cp = LSM:Fetch("statusbar", cur) or "Interface\\AddOns\\nCastbar\\Media\\smooth_v2"
    btnPreview:SetTexture(cp); btnPreview:SetVertexColor(C.primary[1], C.primary[2], C.primary[3], 1)

    btn:SetScript("OnEnter", function(s) s:SetBackdropBorderColor(C.primary[1], C.primary[2], C.primary[3], 1) end)
    btn:SetScript("OnLeave", function(s) s:SetBackdropBorderColor(unpack(C.border)) end)
    btn:SetScript("OnClick", function()
        if activeDropdownPopup then CloseActiveDropdown(); return end
        CloseActiveDropdown()
        local VISIBLE = math.min(#opts, 10)
        local menuH = VISIBLE * ITEM_H + 8

        local menu = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
        menu:SetSize(width, menuH)
        menu:SetFrameStrata("TOOLTIP"); menu:SetFrameLevel(200)
        menu:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
            tile=false, edgeSize=1, insets={left=0,right=0,top=0,bottom=0}})
        menu:SetBackdropColor(0.05, 0.06, 0.10, 0.97)
        menu:SetBackdropBorderColor(C.primary[1]*0.6, C.primary[2]*0.5, C.primary[3]*0.9, 1)

        local bLeft = btn:GetLeft(); local bBottom = btn:GetBottom(); local bTop = btn:GetTop()
        if bBottom and bBottom > (VISIBLE * ITEM_H + 20) then
            menu:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", bLeft, bBottom - 2)
        else
            menu:SetPoint("BOTTOMLEFT", nil, "BOTTOMLEFT", bLeft, (bTop or 0) + 2)
        end

        local msf = CreateFrame("ScrollFrame", nil, menu, "UIPanelScrollFrameTemplate")
        msf:SetPoint("TOPLEFT", 0, -2); msf:SetPoint("BOTTOMRIGHT", -10, 2)
        local msc = CreateFrame("Frame", nil, msf); msc:SetSize(width-14, #opts * ITEM_H + 4); msf:SetScrollChild(msc)
        local msb = msf.ScrollBar
        if msb then
            if msb.ScrollUpButton   then msb.ScrollUpButton:SetAlpha(0);   msb.ScrollUpButton:Disable()   end
            if msb.ScrollDownButton then msb.ScrollDownButton:SetAlpha(0); msb.ScrollDownButton:Disable() end
            msb:SetWidth(6)
            local mt = msb:CreateTexture(nil, "BACKGROUND"); mt:SetAllPoints(msb); mt:SetColorTexture(0.05,0.05,0.08,0.9)
            local th = msb.ThumbTexture or (msb.GetThumbTexture and msb:GetThumbTexture())
            if th then th:SetWidth(6); th:SetTexture(nil); th:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 0.75) end
        end

        for i, opt in ipairs(opts) do
            local row = CreateFrame("Button", nil, msc, "BackdropTemplate")
            row:SetSize(width-24, ITEM_H); row:SetPoint("TOP", 0, -(i-1)*ITEM_H)
            row:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", tile=false, edgeSize=0,
                insets={left=0,right=0,top=0,bottom=0}})
            row:SetBackdropColor(0,0,0,0)
            local rPrev = Tex(row, "ARTWORK"); rPrev:SetSize((width-24)*0.3, 12)
            rPrev:SetPoint("LEFT", row, "LEFT", 6, 0)
            rPrev:SetTexture(opt.path); rPrev:SetVertexColor(C.primary[1], C.primary[2], C.primary[3], 1)
            local rLbl = FS(row, "GameFontHighlightSmall")
            rLbl:SetPoint("LEFT", rPrev, "RIGHT", 6, 0)
            rLbl:SetTextColor(unpack(C.text)); rLbl:SetText(opt.text)
            local cv = GetVal()
            if opt.value == cv then rLbl:SetTextColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1) end
            row:SetScript("OnEnter", function(s) s:SetBackdropColor(C.primary[1]*0.15, C.primary[2]*0.12, C.primary[3]*0.18, 1) end)
            row:SetScript("OnLeave", function(s) s:SetBackdropColor(0,0,0,0) end)
            row:SetScript("OnClick", function()
                SetVal(opt.value); btnLbl:SetText(opt.text)
                local p2 = LSM:Fetch("statusbar", opt.value) or opt.path
                btnPreview:SetTexture(p2)
                CloseActiveDropdown(); ns.Castbar:Reload()
            end)
        end
        menu:Show(); activeDropdownPopup = menu; dropdownScrollGuard:Show()
    end)
end

-----------------------------
-- Edit Mode Grid Overlay
-----------------------------
local editModeGrid

local function CreateEditModeGrid()
    if editModeGrid then return editModeGrid end
    local STEP_M = 32; local STEP_B = 128
    local uiScale = UIParent:GetScale()
    local W = math.ceil(GetScreenWidth()  * UIParent:GetEffectiveScale() / uiScale)
    local H = math.ceil(GetScreenHeight() * UIParent:GetEffectiveScale() / uiScale)

    local grid = CreateFrame("Frame", "nCastbarEditGrid", UIParent)
    grid:SetAllPoints(UIParent); grid:SetFrameStrata("BACKGROUND"); grid:SetFrameLevel(1); grid:Hide()
    local dim = Tex(grid, "BACKGROUND"); dim:SetAllPoints(); dim:SetColorTexture(0,0,0,0.35)

    local function MakeLine(isH, px, py, major, center)
        local t = Tex(grid, "ARTWORK")
        if center then t:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 0.55)
        elseif major then t:SetColorTexture(0.40,0.45,0.75,0.22)
        else t:SetColorTexture(0.30,0.33,0.55,0.11) end
        if isH then
            t:SetHeight(center and 2 or 1)
            t:SetPoint("TOPLEFT",  UIParent, "BOTTOMLEFT",  0, py)
            t:SetPoint("TOPRIGHT", UIParent, "BOTTOMRIGHT", 0, py)
        else
            t:SetWidth(center and 2 or 1)
            t:SetPoint("TOPLEFT",    UIParent, "TOPLEFT",  px,  0)
            t:SetPoint("BOTTOMLEFT", UIParent, "TOPLEFT",  px, -H)
        end
    end

    local cx = math.floor(W/2); local cy = math.floor(H/2)
    local x = 0; while x <= W do MakeLine(false, x, nil, (x%STEP_B==0), (x==cx)); x=x+STEP_M end
    local y = 0; while y <= H do MakeLine(true,  nil, y, (y%STEP_B==0), (y==cy)); y=y+STEP_M end

    local wm = FS(grid, "GameFontNormalSmall")
    wm:SetFont(FONT, 11, "OUTLINE"); wm:SetPoint("BOTTOM", UIParent, "BOTTOM", 0, 6)
    wm:SetText("|cff6680ffn|rCastbar  –  EDIT MODE"); wm:SetTextColor(1,1,1,0.55)

    editModeGrid = grid; return grid
end

local function ShowEditGrid() CreateEditModeGrid(); editModeGrid:Show() end
local function HideEditGrid() if editModeGrid then editModeGrid:Hide() end end

-----------------------------
-- MAIN CONFIG FRAME  (nBuff layout: sidebar + content)
-----------------------------
local configFrame
local activeTabIdx = 0

local function BuildConfigFrame()
    if configFrame then return configFrame end

    local frame = CreateFrame("Frame", "nCastbarConfigFrame", UIParent, "BackdropTemplate")
    frame:SetSize(FRAME_W, FRAME_H)
    frame:SetPoint("CENTER")
    frame:SetMovable(true); frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", frame.StartMoving)
    frame:SetScript("OnDragStop",  frame.StopMovingOrSizing)
    frame:SetFrameStrata("DIALOG"); frame:SetClampedToScreen(true); frame:Hide()
    frame:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
        tile=false, edgeSize=2, insets={left=0,right=0,top=0,bottom=0}})
    frame:SetBackdropColor(unpack(C.bg))
    frame:SetBackdropBorderColor(C.primary[1]*0.5, C.primary[2]*0.4, C.primary[3]*0.7, 1)
    tinsert(UISpecialFrames, "nCastbarConfigFrame")

    -- Fade
    frame.fadeIn  = frame:CreateAnimationGroup()
    local fI = frame.fadeIn:CreateAnimation("Alpha")
    fI:SetFromAlpha(0); fI:SetToAlpha(1); fI:SetDuration(0.16); fI:SetSmoothing("OUT")
    frame.fadeIn:SetScript("OnFinished", function(s) s:GetParent():SetAlpha(1) end)
    frame.fadeOut = frame:CreateAnimationGroup()
    local fO = frame.fadeOut:CreateAnimation("Alpha")
    fO:SetFromAlpha(1); fO:SetToAlpha(0); fO:SetDuration(0.12); fO:SetSmoothing("IN")
    frame.fadeOut:SetScript("OnFinished", function(s) s:GetParent():Hide() end)
    function frame:FadeIn()  self:SetAlpha(0); self:Show(); self.fadeOut:Stop(); self.fadeIn:Play()  end
    function frame:FadeOut() CloseActiveDropdown(); self.fadeIn:Stop(); self.fadeOut:Play()          end
    frame:SetScript("OnHide", function() CloseActiveDropdown() end)

    frame:SetScript("OnKeyDown", function(self, k)
        if k == "ESCAPE" then
            self:SetPropagateKeyboardInput(false)
            if activeDropdownPopup then CloseActiveDropdown(); return end
            frame:FadeOut()
        else
            self:SetPropagateKeyboardInput(true)
        end
    end)

    -- ── TITLE BAR ────────────────────────────────────────────────────────────
    local titleBg = FlatFrame(frame,
        C.primary[1]*0.08, C.primary[2]*0.06, C.primary[3]*0.13, 1,
        C.primary[1]*0.35, C.primary[2]*0.28, C.primary[3]*0.55, 1)
    titleBg:SetPoint("TOPLEFT",  frame, "TOPLEFT",   2, -2)
    titleBg:SetPoint("TOPRIGHT", frame, "TOPRIGHT",  -2, -2)
    titleBg:SetHeight(TITLE_H)

    -- Left accent stripe
    local ta = Tex(titleBg); ta:SetWidth(3)
    ta:SetPoint("TOPLEFT",    titleBg, "TOPLEFT",    0, 0)
    ta:SetPoint("BOTTOMLEFT", titleBg, "BOTTOMLEFT", 0, 0)
    ta:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 1)

    -- Logo (small) in title bar
    local logoTex = Tex(titleBg)
    logoTex:SetTexture("Interface\\AddOns\\nCastbar\\Media\\logo_small")
    logoTex:SetSize(28, 28)
    logoTex:SetPoint("LEFT", titleBg, "LEFT", 14, 0)

    local titleLbl = FS(titleBg, "GameFontNormalHuge")
    titleLbl:SetPoint("LEFT", logoTex, "RIGHT", 10, 0)
    titleLbl:SetTextColor(1, 1, 1, 1)
    titleLbl:SetText("nCastbar")

    -- Bottom gradient line
    local tgl = Tex(titleBg); tgl:SetHeight(2)
    tgl:SetPoint("BOTTOMLEFT",  titleBg, "BOTTOMLEFT",  0, 0)
    tgl:SetPoint("BOTTOMRIGHT", titleBg, "BOTTOMRIGHT", 0, 0)
    tgl:SetGradient("HORIZONTAL",
        CreateColor(C.primary[1], C.primary[2], C.primary[3], 1),
        CreateColor(0, 0, C.primary[3]*0.2, 0))

    -- Close button
    local closeBtn = CreateFrame("Button", nil, titleBg)
    closeBtn:SetSize(28, 28); closeBtn:SetPoint("RIGHT", titleBg, "RIGHT", -14, 0)
    local closeBg = Tex(closeBtn, "BACKGROUND"); closeBg:SetAllPoints()
    closeBg:SetColorTexture(0.45, 0.08, 0.08, 0.8)
    MakeBorder(closeBtn, 0.6, 0.15, 0.15)
    local closeLbl = FS(closeBtn)
    closeLbl:SetFont(FONT, 16, "OUTLINE"); closeLbl:SetPoint("CENTER", 0, 1)
    closeLbl:SetText("×"); closeLbl:SetTextColor(1,1,1,1)
    closeBtn:SetScript("OnEnter", function()
        closeBg:SetColorTexture(0.90, 0.22, 0.22, 1)
        SetBorderColor(closeBtn, 0.90, 0.22, 0.22)
    end)
    closeBtn:SetScript("OnLeave", function()
        closeBg:SetColorTexture(0.45, 0.08, 0.08, 0.8)
        SetBorderColor(closeBtn, 0.6, 0.15, 0.15)
    end)
    closeBtn:SetScript("OnClick", function() frame:FadeOut() end)

    -- ── SIDEBAR ───────────────────────────────────────────────────────────────
    local sidebar = FlatFrame(frame, C.sidebar[1], C.sidebar[2], C.sidebar[3], C.sidebar[4])
    sidebar:SetPoint("TOPLEFT",    titleBg, "BOTTOMLEFT",   2,  0)
    sidebar:SetPoint("BOTTOMLEFT", frame,   "BOTTOMLEFT",   2,  BOT_H+2)
    sidebar:SetWidth(SIDEBAR_W)

    -- Right separator line of sidebar
    local sbl = Tex(sidebar); sbl:SetWidth(1)
    sbl:SetPoint("TOPRIGHT",    sidebar, "TOPRIGHT",    0,  0)
    sbl:SetPoint("BOTTOMRIGHT", sidebar, "BOTTOMRIGHT", 0,  0)
    sbl:SetColorTexture(C.primary[1]*0.35, C.primary[2]*0.28, C.primary[3]*0.55, 1)

    -- ── CONTENT AREA ──────────────────────────────────────────────────────────
    local contentBg = FlatFrame(frame, C.content[1], C.content[2], C.content[3], C.content[4],
        C.border[1], C.border[2], C.border[3], C.border[4])
    contentBg:SetPoint("TOPLEFT",     sidebar,  "TOPRIGHT",    1,  0)
    contentBg:SetPoint("BOTTOMRIGHT", frame,    "BOTTOMRIGHT", -2, BOT_H+2)

    -- Content section title
    local contentHeader = FS(contentBg, "GameFontNormalLarge")
    contentHeader:SetPoint("TOPLEFT", contentBg, "TOPLEFT", PAD, -14)
    contentHeader:SetTextColor(C.primary[1], C.primary[2], C.primary[3], 0.75)
    local chLine = Tex(contentBg, "BACKGROUND"); chLine:SetHeight(1)
    chLine:SetPoint("TOPLEFT",  contentBg, "TOPLEFT",  PAD,  -34)
    chLine:SetPoint("TOPRIGHT", contentBg, "TOPRIGHT", -PAD, -34)
    chLine:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 0.20)

    -- ── BOTTOM BAR ────────────────────────────────────────────────────────────
    local bot = FlatFrame(frame, 0.038, 0.038, 0.050, 1, 0.08, 0.10, 0.18, 1)
    bot:SetPoint("BOTTOMLEFT",  frame, "BOTTOMLEFT",   2, 2)
    bot:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -2, 2)
    bot:SetHeight(BOT_H)
    local botLine = Tex(bot); botLine:SetHeight(1)
    botLine:SetPoint("TOPLEFT",  bot, "TOPLEFT",  0, 0)
    botLine:SetPoint("TOPRIGHT", bot, "TOPRIGHT", 0, 0)
    botLine:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 0.30)

    -- GUI Scale stepper
    do
        local SCALE_STEP = 0.05; local SCALE_MIN = 0.5; local SCALE_MAX = 2.0
        local function GetScale() return (nCastbarDB and nCastbarDB.guiScale) or 1.0 end
        local function ApplyScale(v)
            v = math.max(SCALE_MIN, math.min(SCALE_MAX, v))
            v = math.floor(v / SCALE_STEP + 0.5) * SCALE_STEP
            v = math.floor(v * 100 + 0.5) / 100
            if nCastbarDB then nCastbarDB.guiScale = v end
            frame:SetScale(v)
        end

        local scaleLbl = FS(bot, "GameFontHighlightSmall")
        scaleLbl:SetPoint("LEFT", bot, "LEFT", 10, 0)
        scaleLbl:SetTextColor(unpack(C.textDim))
        scaleLbl:SetText(L["GUI Scale"])

        local valBox = CreateFrame("EditBox", nil, bot, "BackdropTemplate")
        valBox:SetSize(46, 22)
        valBox:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8x8",
            edgeFile="Interface\\Buttons\\WHITE8x8", edgeSize=1,
            insets={left=0,right=0,top=0,bottom=0}})
        valBox:SetBackdropColor(0.04, 0.05, 0.09, 0.9)
        valBox:SetBackdropBorderColor(C.primary[1]*0.4, C.primary[2]*0.35, C.primary[3]*0.6, 1)
        valBox:SetFont(FONT, 11, "")
        valBox:SetTextColor(0.8, 0.9, 1.0, 1)
        valBox:SetJustifyH("CENTER"); valBox:SetMaxLetters(5); valBox:SetAutoFocus(false)
        valBox:EnableMouseWheel(true)

        local function SyncScaleBox()
            valBox:SetText(string.format("%.2f", GetScale()))
            frame:SetScale(GetScale())
        end

        local function MakeArrow(lbl2, dir)
            local b = CreateFrame("Button", nil, bot, "BackdropTemplate")
            b:SetSize(20, 22)
            b:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8x8",
                edgeFile="Interface\\Buttons\\WHITE8x8", edgeSize=1,
                insets={left=1,right=1,top=1,bottom=1}})
            b:SetBackdropColor(0.06, 0.08, 0.14, 1)
            b:SetBackdropBorderColor(C.primary[1]*0.45, C.primary[2]*0.38, C.primary[3]*0.65, 1)
            local t2 = b:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            t2:SetAllPoints(); t2:SetText(lbl2)
            t2:SetTextColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1)
            b:SetScript("OnEnter", function(s)
                s:SetBackdropColor(0.10, 0.14, 0.24, 1)
                s:SetBackdropBorderColor(C.primary[1], C.primary[2], C.primary[3], 1)
            end)
            b:SetScript("OnLeave", function(s)
                s:SetBackdropColor(0.06, 0.08, 0.14, 1)
                s:SetBackdropBorderColor(C.primary[1]*0.45, C.primary[2]*0.38, C.primary[3]*0.65, 1)
                s.held = false
            end)
            local ticker
            b:SetScript("OnClick", function() ApplyScale(GetScale() + dir * SCALE_STEP); SyncScaleBox() end)
            b:SetScript("OnMouseDown", function()
                b.held = true
                C_Timer.After(0.4, function()
                    if b.held then
                        ticker = C_Timer.NewTicker(0.08, function()
                            if b.held then ApplyScale(GetScale() + dir * SCALE_STEP); SyncScaleBox()
                            else ticker:Cancel(); ticker = nil end
                        end)
                    end
                end)
            end)
            b:SetScript("OnMouseUp", function() b.held = false; if ticker then ticker:Cancel(); ticker = nil end end)
            return b
        end

        local btnMinus = MakeArrow("<", -1); btnMinus:SetPoint("LEFT", scaleLbl, "RIGHT", 8, 0)
        valBox:SetPoint("LEFT", btnMinus, "RIGHT", 2, 0)
        local btnPlus  = MakeArrow(">",  1); btnPlus:SetPoint("LEFT",  valBox,   "RIGHT", 2, 0)

        valBox:SetScript("OnMouseWheel", function(_, d) ApplyScale(GetScale()+d*SCALE_STEP); SyncScaleBox() end)
        local function CommitTyped()
            local v = tonumber(valBox:GetText()); if v then ApplyScale(v) end
            SyncScaleBox(); valBox:ClearFocus()
        end
        valBox:SetScript("OnEnterPressed",  CommitTyped)
        valBox:SetScript("OnTabPressed",    CommitTyped)
        valBox:SetScript("OnEditFocusLost", CommitTyped)
        valBox:SetScript("OnEscapePressed", function() SyncScaleBox(); valBox:ClearFocus() end)
        frame:HookScript("OnShow", SyncScaleBox)
        frame.SyncScale = SyncScaleBox
        SyncScaleBox()
    end

    -- Close button (bottom right, dynamic width)
    local closeBot, closeBotLbl = StyledButton(bot, L["Close"], nil, 26, function() frame:FadeOut() end)
    closeBot:SetPoint("RIGHT", bot, "RIGHT", -12, 0)

    -- ── TAB DEFINITIONS ───────────────────────────────────────────────────────
    local TAB_DEFS = {
        { id="general", label=L["General"], icon="Interface\\Icons\\INV_Misc_Note_06" },
        { id="player",  label=L["Player"],  icon="Interface\\Icons\\Achievement_Character_Human_Male" },
        { id="target",  label=L["Target"],  icon="Interface\\Icons\\Ability_Warrior_OffensiveStance" },
        { id="focus",   label=L["Focus"],   icon="Interface\\Icons\\Spell_Holy_AuraMastery" },
        { id="pet",     label=L["Pet"],     icon="Interface\\Icons\\Ability_Hunter_BeastCall" },
    }

    local TAB_H   = 40
    local TAB_GAP = 1
    local tabs      = {}
    local tabPanels = {}

    local function SelectTab(idx)
        activeTabIdx = idx
        for i, tab in ipairs(tabs) do
            local sel = (i == idx)
            if sel then
                tab.accent:Show()
                tab.bgTex:SetColorTexture(C.primary[1]*0.15, C.primary[2]*0.12, C.primary[3]*0.20, 1)
                tab.icon:SetVertexColor(1, 0.90, 1.00, 1)
                tab.lbl:SetTextColor(1, 0.96, 1.00, 1)
            else
                tab.accent:Hide()
                tab.bgTex:SetColorTexture(C.sidebar[1], C.sidebar[2], C.sidebar[3], 1)
                tab.icon:SetVertexColor(0.42, 0.44, 0.55, 1)
                tab.lbl:SetTextColor(0.68, 0.70, 0.80, 1)
            end
            tabPanels[i]:SetShown(sel)
        end
        contentHeader:SetText(TAB_DEFS[idx].label)
    end

    for i, td in ipairs(TAB_DEFS) do
        local tab = CreateFrame("Button", nil, sidebar)
        tab:SetHeight(TAB_H)
        tab:SetPoint("TOPLEFT",  sidebar, "TOPLEFT",  0, -(i-1)*(TAB_H+TAB_GAP))
        tab:SetPoint("TOPRIGHT", sidebar, "TOPRIGHT", -1, -(i-1)*(TAB_H+TAB_GAP))

        tab.bgTex = Tex(tab, "BACKGROUND"); tab.bgTex:SetAllPoints()
        tab.bgTex:SetColorTexture(C.sidebar[1], C.sidebar[2], C.sidebar[3], 1)

        tab.accent = Tex(tab, "OVERLAY"); tab.accent:SetWidth(3)
        tab.accent:SetPoint("TOPLEFT",    tab, "TOPLEFT",    0, 0)
        tab.accent:SetPoint("BOTTOMLEFT", tab, "BOTTOMLEFT", 0, 0)
        tab.accent:SetColorTexture(C.primary[1], C.primary[2], C.primary[3], 1)
        tab.accent:Hide()

        local sep = Tex(tab, "BORDER"); sep:SetHeight(1)
        sep:SetPoint("BOTTOMLEFT",  tab, "BOTTOMLEFT",  0, 0)
        sep:SetPoint("BOTTOMRIGHT", tab, "BOTTOMRIGHT", 0, 0)
        sep:SetColorTexture(0.08, 0.09, 0.16, 1)

        tab.icon = Tex(tab, "OVERLAY"); tab.icon:SetSize(18, 18)
        tab.icon:SetPoint("LEFT", tab, "LEFT", 14, 0)
        tab.icon:SetTexture(td.icon)
        tab.icon:SetVertexColor(0.42, 0.44, 0.55, 1)

        tab.lbl = FS(tab, "GameFontNormalSmall")
        tab.lbl:SetPoint("LEFT",  tab.icon, "RIGHT", 8, 0)
        tab.lbl:SetPoint("RIGHT", tab,      "RIGHT", -6, 0)
        tab.lbl:SetJustifyH("LEFT")
        tab.lbl:SetTextColor(0.68, 0.70, 0.80, 1)
        tab.lbl:SetText(td.label)

        local ii = i
        tab:SetScript("OnEnter", function(s)
            if activeTabIdx ~= ii then
                s.bgTex:SetColorTexture(0.06, 0.07, 0.12, 1)
                s.lbl:SetTextColor(C.primaryHi[1], C.primaryHi[2], C.primaryHi[3], 1)
                s.icon:SetVertexColor(C.primary[1], C.primary[2], C.primary[3], 1)
            end
        end)
        tab:SetScript("OnLeave", function(s)
            if activeTabIdx ~= ii then
                s.bgTex:SetColorTexture(C.sidebar[1], C.sidebar[2], C.sidebar[3], 1)
                s.lbl:SetTextColor(0.68, 0.70, 0.80, 1)
                s.icon:SetVertexColor(0.42, 0.44, 0.55, 1)
            end
        end)
        tab:SetScript("OnClick", function() SelectTab(ii) end)
        tabs[i] = tab

        -- Tab panel (content area)
        local panel = CreateFrame("Frame", nil, contentBg)
        panel:SetPoint("TOPLEFT",     contentBg, "TOPLEFT",     0,    0)
        panel:SetPoint("BOTTOMRIGHT", contentBg, "BOTTOMRIGHT", 0,    0)
        panel:Hide()
        tabPanels[i] = panel
    end

    configFrame = frame
    frame._tabs       = tabs
    frame._tabPanels  = tabPanels
    frame._TAB_DEFS   = TAB_DEFS
    frame._SelectTab  = SelectTab
    frame._contentBg  = contentBg
    frame._sidebar    = sidebar

    return frame, tabs, tabPanels, SelectTab
end

-----------------------------
-- Build the frame + panels
-----------------------------
local frame, tabs, tabPanels, SelectTab = BuildConfigFrame()

-----------------------------
-- Helper: build scrollable unit panel
-----------------------------
local function BuildUnitPanel(parent, unit)
    local sc, sf = MakeScrollContent(parent)
    local PAD2 = 14
    local y    = -PAD2

    -- Layout
    local s1 = SectionHeader(sc, L["Layout"], sc, PAD2, y); y = y - 28
    ModernToggle(sc, L["Enable"], PAD2, y,
        function() return nCastbarDB.profile[unit].enabled end,
        function(v) nCastbarDB.profile[unit].enabled = v; ns.Castbar:Reload() end,
        L["desc_enable"])
    y = y - 56

    ModernSlider(sc, L["Width"], L["desc_width"], 100, 400, 1, sc, y,
        function() return nCastbarDB.profile[unit].width end,
        function(v) nCastbarDB.profile[unit].width = v; ns.Castbar:Reload() end,
        function(v) return string.format("%d", v) end)
    y = y - 72

    ModernSlider(sc, L["Height"], L["desc_height"], 10, 40, 1, sc, y,
        function() return nCastbarDB.profile[unit].height end,
        function(v) nCastbarDB.profile[unit].height = v; ns.Castbar:Reload() end,
        function(v) return string.format("%d", v) end)
    y = y - 72

    ModernSlider(sc, L["Scale"], L["desc_scale"], 0.5, 2.0, 0.01, sc, y,
        function() return nCastbarDB.profile[unit].scale end,
        function(v) nCastbarDB.profile[unit].scale = v; ns.Castbar:Reload() end)
    y = y - 72

    ModernToggle(sc, L["Dynamic Width"], PAD2, y,
        function() return nCastbarDB.profile[unit].dynamicWidth end,
        function(v) nCastbarDB.profile[unit].dynamicWidth = v; ns.Castbar:Reload() end,
        L["desc_dynamic_width"])
    y = y - 56

    ModernToggle(sc, L["Show Time"], PAD2, y,
        function() return nCastbarDB.profile[unit].showTime end,
        function(v) nCastbarDB.profile[unit].showTime = v; ns.Castbar:Reload() end,
        L["desc_show_time"])
    y = y - 56

    ModernToggle(sc, L["Class Color Cast"], PAD2, y,
        function() return nCastbarDB.profile[unit].classColorCast end,
        function(v) nCastbarDB.profile[unit].classColorCast = v; ns.Castbar:Reload() end,
        L["desc_class_color"])
    y = y - 56

    -- Bar Texture
    LSMTextureDropdown(sc, L["Bar Texture"], unit, "statusbarTexture", sc, y, CONTENT_W-30)
    y = y - 58

    -- Text
    y = y - 10
    SectionHeader(sc, L["Text"], sc, PAD2, y); y = y - 28
    ModernDropdown(sc, L["Text Position"],
        {{value="inside",text=L["Inside"]},{value="above",text=L["Above"]},{value="below",text=L["Below"]}},
        function() return nCastbarDB.profile[unit].textPosition or "inside" end,
        function(v) nCastbarDB.profile[unit].textPosition = v; ns.Castbar:Reload() end,
        sc, y, 200)
    y = y - 58

    ModernSlider(sc, L["Spell Name Font Size"], L["desc_font_size"], 8, 22, 1, sc, y,
        function() return nCastbarDB.profile[unit].spellNameFontSize or 12 end,
        function(v) nCastbarDB.profile[unit].spellNameFontSize = v; ns.Castbar:Reload() end,
        function(v) return string.format("%d", v) end)
    y = y - 72

    ModernToggle(sc, L["Center Text"], PAD2, y,
        function() return nCastbarDB.profile[unit].textAlignCenter end,
        function(v) nCastbarDB.profile[unit].textAlignCenter = v; ns.Castbar:Reload() end,
        L["desc_center_text"])
    y = y - 56

    LSMFontDropdown(sc, L["Spell Name Font"], unit, "spellNameFont", sc, y, 230)
    y = y - 58

    ModernDropdown(sc, L["Spell Name Outline"],
        {{value="NONE",text=L["Outline None"]},{value="OUTLINE",text=L["Outline Normal"]},{value="THICKOUTLINE",text=L["Outline Thick"]}},
        function() return nCastbarDB.profile[unit].spellNameOutline or "NONE" end,
        function(v) nCastbarDB.profile[unit].spellNameOutline = v; ns.Castbar:Reload() end,
        sc, y, 230)
    y = y - 58

    -- Icon
    y = y - 10
    SectionHeader(sc, L["Icon"], sc, PAD2, y); y = y - 28
    ModernToggle(sc, L["Show Icon"], PAD2, y,
        function() return nCastbarDB.profile[unit].icon.show end,
        function(v) nCastbarDB.profile[unit].icon.show = v; ns.Castbar:Reload() end,
        L["desc_show_icon"])
    y = y - 56

    ModernToggle(sc, L["Icon Outside"], PAD2, y,
        function() return nCastbarDB.profile[unit].icon.positionOutside end,
        function(v) nCastbarDB.profile[unit].icon.positionOutside = v; ns.Castbar:Reload() end,
        L["desc_icon_outside"])
    y = y - 56

    ModernDropdown(sc, L["Icon Position"],
        {{value="LEFT",text=L["Left"]},{value="RIGHT",text=L["Right"]}},
        function() return nCastbarDB.profile[unit].icon.position or "LEFT" end,
        function(v) nCastbarDB.profile[unit].icon.position = v; ns.Castbar:Reload() end,
        sc, y, 180)
    y = y - 58

    -- Colors
    y = y - 10
    SectionHeader(sc, L["Colors"], sc, PAD2, y); y = y - 28

    ModernToggle(sc, L["Cast Success"], PAD2, y,
        function() return nCastbarDB.profile[unit].castSuccess ~= false end,
        function(v) nCastbarDB.profile[unit].castSuccess = v end,
        L["desc_cast_success"])
    y = y - 56

    ColorPickerRow(sc, L["Cast Color"],        unit, "cast",             sc, y); y = y - 32
    ColorPickerRow(sc, L["Channel Color"],     unit, "channel",          sc, y); y = y - 32
    ColorPickerRow(sc, L["Non-Interruptible"], unit, "nonInterruptible", sc, y); y = y - 32
    ColorPickerRow(sc, L["Failed Color"],      unit, "failed",           sc, y); y = y - 32
    ColorPickerRow(sc, L["Success Color"],     unit, "success",          sc, y); y = y - 32
    if unit == "player" then
        ColorPickerRow(sc, L["Safezone Color"], unit, "safezone", sc, y); y = y - 32
    end

    -- Player extras
    if unit == "player" then
        y = y - 10
        SectionHeader(sc, "Player", sc, PAD2, y); y = y - 28
        ModernToggle(sc, L["Show Latency"], PAD2, y,
            function() return nCastbarDB.profile[unit].showLatency end,
            function(v) nCastbarDB.profile[unit].showLatency = v; ns.Castbar:Reload() end,
            L["desc_show_latency"])
        y = y - 56
        ModernToggle(sc, L["Show Safezone"], PAD2, y,
            function() return nCastbarDB.profile[unit].showSafezone end,
            function(v) nCastbarDB.profile[unit].showSafezone = v; ns.Castbar:Reload() end,
            L["desc_show_safezone"])
        y = y - 56
    end

    sc:SetHeight(-y + PAD2)
    return sc, sf
end

-----------------------------
-- General Tab
-----------------------------
do
    local gc = tabPanels[1]
    local sc, sf = MakeScrollContent(gc)
    local PAD2 = 14
    local y    = -PAD2

    -- Hero Logo (centered)
    do
        local logoBig = Tex(sc, "ARTWORK")
        logoBig:SetTexture("Interface\\AddOns\\nCastbar\\Media\\logo")
        logoBig:SetSize(300, 200)
        logoBig:SetPoint("TOP", sc, "TOP", 0, y)
        y = y - 200 - 16
    end

    -- Edit Mode
    SectionHeader(sc, L["Edit Mode"], sc, PAD2, y); y = y - 28
    local editBtn, editLbl = StyledButton(sc, L["Toggle Edit Mode"], nil, 30, nil)
    editBtn:SetPoint("TOPLEFT", sc, "TOPLEFT", PAD2, y)
    editBtn:SetScript("OnClick", function()
        if InCombatLockdown() then print("|cff6680ffnCastbar:|r Cannot toggle edit mode in combat!"); return end
        local anyVisible = false
        for _, anchor in pairs(ns.Castbar.anchors) do
            if anchor:IsShown() then anyVisible = true; break end
        end
        if anyVisible then
            for _, anchor in pairs(ns.Castbar.anchors) do anchor:Hide() end
            editLbl:SetText(L["Toggle Edit Mode"]); HideEditGrid()
        else
            for _, anchor in pairs(ns.Castbar.anchors) do anchor:Show() end
            editLbl:SetText(L["Edit Mode"] .. " |cff00ff00ON|r"); ShowEditGrid()
        end
        C_Timer.After(0, function()
            local sw = editLbl:GetStringWidth()
            editBtn:SetWidth((sw>0) and (sw+BTN_PAD_X) or 150)
        end)
    end)
    local eDesc = FS(sc, "GameFontHighlightSmall")
    eDesc:SetPoint("TOPLEFT",  sc, "TOPLEFT",  PAD2 + 10, y - 34)
    eDesc:SetWidth(CONTENT_W - 40); eDesc:SetJustifyH("LEFT")
    eDesc:SetTextColor(unpack(C.textDim)); eDesc:SetText(L["edit_mode_desc"])
    y = y - 80

    -- Debug
    y = y - 10
    SectionHeader(sc, L["Debug Options"], sc, PAD2, y); y = y - 28
    local dcToggle = ModernToggle(sc, L["Debug Mode (Frame)"], PAD2, y,
        function() return nCastbarDB and nCastbarDB.profile and nCastbarDB.profile.debug end,
        function(v) if nCastbarDB and nCastbarDB.profile then nCastbarDB.profile.debug = v; ns.Castbar:Reload() end end,
        L["debug_info"] and "Shows detailed cast logs in a floating debug frame." or nil)
    y = y - 56

    local dcChatToggle = ModernToggle(sc, L["Debug Chat"], PAD2, y,
        function() return nCastbarDB and nCastbarDB.profile and nCastbarDB.profile.debugChat end,
        function(v)
            if nCastbarDB and nCastbarDB.profile then
                nCastbarDB.profile.debugChat = v
                if v then ns.Print("|cff6680ff" .. L["Debug Chat Enabled"])
                else ns.Print("|cff6680ff" .. L["Debug Chat Disabled"]) end
            end
        end,
        "Prints cast start / stop events directly into the chat window.")
    y = y - 56

    -- Minimap
    y = y - 10
    SectionHeader(sc, L["Show Minimap Icon"], sc, PAD2, y); y = y - 28
    local _mmOn = true -- placeholder; real init in PLAYER_LOGIN
    local mmToggle = ModernToggle(sc, L["Show Minimap Icon"], PAD2, y,
        function()
            if nCastbarDB and nCastbarDB.minimap then return not nCastbarDB.minimap.hide end
            return true
        end,
        function(v)
            -- show dialog
        end,
        "Shows or hides the nCastbar minimap button. Requires a UI reload.")
    sc._mmToggle = mmToggle
    y = y - 56

    -- Reset
    y = y - 10
    SectionHeader(sc, L["Reset Settings"], sc, PAD2, y); y = y - 28
    local rBtn, rLbl = DangerButton(sc, L["Reset Settings"], nil, 28, function()
        for k, v in pairs(ns.defaults) do nCastbarDB.profile[k] = v end; ReloadUI()
    end)
    rBtn:SetPoint("TOPLEFT", sc, "TOPLEFT", PAD2, y)
    local rInfo = FS(sc, "GameFontHighlightSmall")
    rInfo:SetPoint("LEFT", rBtn, "RIGHT", 10, 0)
    rInfo:SetText("|cffff6666Reloads UI|r"); rInfo:SetTextColor(unpack(C.textDim))
    y = y - 40

    sc:SetHeight(-y + PAD2)
    tabPanels[1]._mmToggle = mmToggle
end

-----------------------------
-- Slash Commands
-----------------------------
SLASH_NCASTBAR1 = "/ncastbar"
SLASH_NCASTBAR2 = "/ncb"
SlashCmdList["NCASTBAR"] = function(msg)
    msg = (msg or ""):lower():trim()
    if msg == "edit" or msg == "move" or msg == "toggle" then
        if InCombatLockdown() then return end
        local anyVisible = false
        for _, anchor in pairs(ns.Castbar.anchors) do
            if anchor:IsShown() then anyVisible = true; break end
        end
        if anyVisible then
            for _, anchor in pairs(ns.Castbar.anchors) do anchor:Hide() end; HideEditGrid()
        else
            for _, anchor in pairs(ns.Castbar.anchors) do anchor:Show() end; ShowEditGrid()
        end
    elseif msg == "debug"  then ns.ToggleDebug()
    elseif msg == "debugframe" or msg == "df" then ns.ShowDebugFrame()
    elseif msg == "debugchat"  or msg == "dc" then
        nCastbarDB.profile.debugChat = not nCastbarDB.profile.debugChat
        if nCastbarDB.profile.debugChat then ns.Print("|cff6680ffDebug Chat:|r |cff00ff00ON|r")
        else ns.Print("|cff6680ffDebug Chat:|r |cffff0000OFF|r") end
    elseif msg == "minimap" or msg == "mm" then
        if ns.ModernSettings and ns.ModernSettings.ShowMinimapDialog then
            if not nCastbarDB.minimap then nCastbarDB.minimap = {hide=false} end
            ns.ModernSettings.ShowMinimapDialog(not nCastbarDB.minimap.hide)
        else ns.ToggleMinimapIcon() end
    elseif msg == "help" or msg == "?" then
        ns.Print("|cff6680ffCommands:|r /ncb  /ncb edit  /ncb debug  /ncb minimap  /ncb help")
    else
        if frame:IsShown() then frame:FadeOut() else frame:FadeIn() end
    end
end

ns.ModernSettings = {configFrame = frame}

-----------------------------
-- Init after DB load  (PLAYER_LOGIN)
-----------------------------
local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("PLAYER_LOGIN")
initFrame:SetScript("OnEvent", function(self, event)
    if event ~= "PLAYER_LOGIN" then return end

    -- Build unit tabs
    local unitMap = {
        {idx=2, unit="player"},
        {idx=3, unit="target"},
        {idx=4, unit="focus"},
        {idx=5, unit="pet"},
    }
    for _, m in ipairs(unitMap) do
        BuildUnitPanel(tabPanels[m.idx], m.unit)
    end

    -- Wire minimap toggle  (needs SavedVars)
    local mmTog = tabPanels[1]._mmToggle
    if mmTog then
        if not nCastbarDB.minimap then nCastbarDB.minimap = {hide=false} end

        -- Modal reload dialog
        local dlg = CreateFrame("Frame", "nCastbarMinimapReloadDialog", UIParent, "BackdropTemplate")
        dlg:SetSize(420, 150); dlg:SetPoint("CENTER")
        dlg:SetFrameStrata("FULLSCREEN_DIALOG"); dlg:SetFrameLevel(300)
        dlg:Hide(); dlg:EnableMouse(true)
        dlg:SetBackdrop({bgFile="Interface\\Buttons\\WHITE8X8", edgeFile="Interface\\Buttons\\WHITE8X8",
            tile=false, edgeSize=2, insets={left=0,right=0,top=0,bottom=0}})
        dlg:SetBackdropColor(0.04, 0.04, 0.06, 0.97)
        dlg:SetBackdropBorderColor(C.primary[1]*0.7, C.primary[2]*0.6, C.primary[3]*0.9, 1)

        local dlgHeader = Tex(dlg, "BORDER")
        dlgHeader:SetPoint("TOPLEFT"); dlgHeader:SetPoint("TOPRIGHT"); dlgHeader:SetHeight(32)
        dlgHeader:SetColorTexture(C.primary[1]*0.15, C.primary[2]*0.12, C.primary[3]*0.24, 1)

        local dlgTitle = FS(dlg, "GameFontNormalLarge")
        dlgTitle:SetPoint("TOPLEFT", 14, -8)
        dlgTitle:SetText(L["minimap_reload_title"])
        dlgTitle:SetTextColor(C.primary[1], C.primary[2], C.primary[3])

        local dlgBody = FS(dlg)
        dlgBody:SetPoint("TOPLEFT", 14, -44); dlgBody:SetWidth(392)
        dlgBody:SetJustifyH("LEFT"); dlgBody:SetText(L["minimap_reload_body"])
        dlgBody:SetTextColor(unpack(C.text))

        local pendingHide = false

        local cancelBtn, _ = StyledButton(dlg, L["btn_cancel"], nil, 28, function()
            -- revert toggle visual
            local on = not nCastbarDB.minimap.hide
            local tog = mmTog
            local track = tog and tog.GetChildren and tog:GetChildren()
            dlg:Hide()
        end)
        cancelBtn:SetPoint("BOTTOMLEFT", 14, 12)

        local reloadBtn, _ = StyledButton(dlg, L["btn_reload_ui"], nil, 28, function()
            nCastbarDB.minimap.hide = pendingHide; dlg:Hide(); ReloadUI()
        end)
        reloadBtn:SetPoint("BOTTOMRIGHT", -14, 12)

        -- Wire toggle track OnClick
        if mmTog then
            local track = select(1, mmTog:GetChildren())
            if track then
                track:SetScript("OnClick", function()
                    local cur = not nCastbarDB.minimap.hide
                    pendingHide = cur  -- toggling: new desired hide = old "shown" state
                    dlg:Show()
                end)
            end
        end

        ns.ModernSettings.ShowMinimapDialog = function(newHide)
            pendingHide = newHide; dlg:Show()
        end
    end

    -- Interface Options
    local op = CreateFrame("Frame", "nCastbarOptionsPanel")
    op.name = "nCastbar"
    local ot = op:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
    ot:SetPoint("TOPLEFT", 16, -16); ot:SetText("|cff6680ffnCastbar|r")
    local od = op:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
    od:SetPoint("TOPLEFT", ot, "BOTTOMLEFT", 0, -8); od:SetWidth(580); od:SetJustifyH("LEFT")
    od:SetText(L["addon_desc"])
    local ob = CreateFrame("Button", nil, op, "UIPanelButtonTemplate")
    ob:SetSize(200, 30); ob:SetPoint("TOPLEFT", od, "BOTTOMLEFT", 0, -20)
    ob:SetText(L["Open Settings"])
    ob:SetScript("OnClick", function() frame:FadeIn(); HideUIPanel(SettingsPanel) end)
    local cat = Settings.RegisterCanvasLayoutCategory(op, "nCastbar")
    cat.ID = "nCastbar"; Settings.RegisterAddOnCategory(cat)

    -- Select first tab
    SelectTab(1)
    self:UnregisterEvent("PLAYER_LOGIN")
end)
