-- ============================================================================
-- Housing Decor Guide - UI Framework
-- Reusable UI components with theme support
-- Uses Theme Registry pattern for live theme switching
-- ============================================================================

local addonName, privateDB = ...

-- Use the addon's global namespace (created in HDG_Core.lua)
local HDG = _G.HousingDecorGuide

HDG.UI = {}

-- ============================================================================
-- CENTRALIZED BACKDROP CONSTANTS
-- ============================================================================

local BACKDROP_FLAT = {
    bgFile = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Buttons\\WHITE8x8",
    edgeSize = 1,
    insets = { left = 0, right = 0, top = 0, bottom = 0 }
}

local BACKDROP_BORDERLESS = {
    bgFile = "Interface\\Buttons\\WHITE8x8",
    edgeFile = nil,
    tile = false,
}

-- Expose backdrops for external use
HDG.UI.BACKDROP_FLAT = BACKDROP_FLAT
HDG.UI.BACKDROP_BORDERLESS = BACKDROP_BORDERLESS

-- ============================================================================
-- HELPER FUNCTIONS (delegating to ThemeEngine canonical helpers)
-- ============================================================================

local function GetScheme() return HDG.Theme.GetCurrentScheme() end
local function RegisterWidget(widget, widgetType) HDG.Theme.RegisterWidget(widget, widgetType) end

-- ============================================================================
-- WINDOW (Main Frame)
-- ============================================================================

function HDG.UI:Window(parent, width, height, titleText, dbKey)
    local scheme = GetScheme()
    local defaultWidth = width or 800
    local defaultHeight = height or 600
    local minWidth = math.floor(defaultWidth * 0.5)
    local minHeight = math.floor(defaultHeight * 0.5)
    local maxWidth = math.floor(defaultWidth * 2)
    local maxHeight = math.floor(defaultHeight * 2)

    local frame = CreateFrame("Frame", nil, parent or UIParent, "BackdropTemplate")
    frame:SetSize(defaultWidth, defaultHeight)
    frame:SetPoint("CENTER")
    frame:EnableMouse(true)
    frame:SetMovable(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetFrameStrata("MEDIUM")  -- Consistent with main window (HDG_UI.lua)
    frame:SetToplevel(true)  -- Raise to top when clicked
    frame:SetClampedToScreen(true)

    -- Enable resizing
    frame:SetResizable(true)
    frame:SetResizeBounds(minWidth, minHeight, maxWidth, maxHeight)

    -- Store dbKey for layout persistence
    frame._dbKey = dbKey

    -- Restore saved layout if dbKey provided
    if dbKey and HDG.Store then
        local state = HDG.Store:GetState()
        if state and state.layout and state.layout[dbKey] then
            local layout = state.layout[dbKey]
            frame:ClearAllPoints()
            frame:SetPoint(layout.point or "CENTER", UIParent, layout.relPoint or "CENTER", layout.x or 0, layout.y or 0)
            frame:SetSize(layout.width or defaultWidth, layout.height or defaultHeight)
        end
    end

    -- Save layout on drag stop
    frame:SetScript("OnDragStart", function(self)
        self:StartMoving()
    end)
    frame:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        if self._dbKey and HDG.Store then
            local point, _, relPoint, x, y = self:GetPoint()
            HDG.Store:Dispatch("UPDATE_LAYOUT", {
                frame = self._dbKey,
                width = self:GetWidth(),
                height = self:GetHeight(),
                point = point,
                relPoint = relPoint,
                x = x,
                y = y,
            })
        end
    end)

    frame:SetBackdrop(BACKDROP_FLAT)
    if scheme then
        frame:SetBackdropColor(scheme.bg.r, scheme.bg.g, scheme.bg.b, scheme.bg.a)
        frame:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
    end

    -- Title text
    if titleText then
        local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        title:SetPoint("TOP", 0, -8)
        if scheme then
            HDG.Theme.ApplyFont(title, scheme, "header")
            title:SetTextColor(scheme.text_header.r, scheme.text_header.g, scheme.text_header.b, scheme.text_header.a)
        else
            title:SetFontObject("GameFontNormalLarge")
        end
        title:SetText(titleText)
        frame.title = title
    end

    -- ========================================================================
    -- RESIZE GRIP (Bottom-right corner)
    -- ========================================================================
    local resizeGrip = CreateFrame("Button", nil, frame)
    resizeGrip:SetSize(16, 16)
    resizeGrip:SetPoint("BOTTOMRIGHT", -2, 2)
    resizeGrip:EnableMouse(true)

    -- Resize grip texture (diagonal lines)
    local gripTexture = resizeGrip:CreateTexture(nil, "OVERLAY")
    gripTexture:SetAllPoints()
    gripTexture:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
    resizeGrip.texture = gripTexture

    -- Hover texture
    resizeGrip:SetScript("OnEnter", function(self)
        self.texture:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Highlight")
    end)
    resizeGrip:SetScript("OnLeave", function(self)
        self.texture:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
    end)

    -- Resize behavior
    resizeGrip:SetScript("OnMouseDown", function(self, button)
        if button == "LeftButton" then
            frame:StartSizing("BOTTOMRIGHT")
        end
    end)
    resizeGrip:SetScript("OnMouseUp", function(self, button)
        frame:StopMovingOrSizing()
        if frame._dbKey and HDG.Store then
            local point, _, relPoint, x, y = frame:GetPoint()
            HDG.Store:Dispatch("UPDATE_LAYOUT", {
                frame = frame._dbKey,
                width = frame:GetWidth(),
                height = frame:GetHeight(),
                point = point,
                relPoint = relPoint,
                x = x,
                y = y,
            })
        end
        -- Trigger event for any listeners that need to respond to size change
        if HDG.EventBus then
            HDG.EventBus:Trigger("HDG_FRAME_RESIZED", { frame = frame._dbKey })
        end
    end)

    frame.resizeGrip = resizeGrip

    -- Register with theme engine
    RegisterWidget(frame, "Frame")

    return frame
end

-- ============================================================================
-- PANEL
-- ============================================================================

function HDG.UI:Panel(parent, config)
    config = config or {}
    local scheme = GetScheme()

    local panel = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    if config.width and config.height then
        panel:SetSize(config.width, config.height)
    end

    panel:SetBackdrop(BACKDROP_FLAT)
    if scheme then
        panel:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
        panel:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
    end

    -- Register with theme engine
    RegisterWidget(panel, "Panel")

    return panel
end

-- ============================================================================
-- BUTTON
-- ============================================================================

function HDG.UI:Button(parent, text, width, height, onClick)
    local scheme = GetScheme()

    local btn = CreateFrame("Button", nil, parent)
    btn:SetSize(width or 100, height or 24)

    -- Atlas-based button textures
    local normal = btn:CreateTexture(nil, "BACKGROUND")
    normal:SetAllPoints()
    normal:SetAtlas("UI-DialogBox-Button-Up")
    btn:SetNormalTexture(normal)

    local pushed = btn:CreateTexture(nil, "BACKGROUND")
    pushed:SetAllPoints()
    pushed:SetAtlas("UI-DialogBox-Button-Down")
    btn:SetPushedTexture(pushed)

    local highlight = btn:CreateTexture(nil, "HIGHLIGHT")
    highlight:SetAllPoints()
    highlight:SetAtlas("UI-DialogBox-Button-Highlight")
    btn:SetHighlightTexture(highlight)

    local disabled = btn:CreateTexture(nil, "BACKGROUND")
    disabled:SetAllPoints()
    disabled:SetAtlas("UI-DialogBox-Button-Disabled")
    btn:SetDisabledTexture(disabled)

    -- Font string
    local fs = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    fs:SetPoint("CENTER", 0, 0)
    fs:SetText(text or "")
    btn:SetFontString(fs)

    if scheme then
        fs:SetTextColor(scheme.button_text_norm.r, scheme.button_text_norm.g, scheme.button_text_norm.b, scheme.button_text_norm.a)
        HDG.Theme.ApplyFont(fs, scheme, "body")
        btn._scheme = scheme
    end

    if onClick then
        btn:SetScript("OnClick", onClick)
    end

    -- Register with theme engine
    RegisterWidget(btn, "Button")

    return btn
end

-- ============================================================================
-- LABEL (Text)
-- ============================================================================

function HDG.UI:Label(parent, text, isHeader)
    local scheme = GetScheme()

    local label = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    label.isHeader = isHeader

    if scheme then
        if isHeader then
            HDG.Theme.ApplyFont(label, scheme, "header")
            label:SetTextColor(scheme.text_header.r, scheme.text_header.g, scheme.text_header.b, scheme.text_header.a)
        else
            HDG.Theme.ApplyFont(label, scheme, "body")
            label:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
        end
    else
        label:SetFontObject(isHeader and "GameFontNormalLarge" or "GameFontNormal")
    end
    label:SetText(text or "")

    -- Register with theme engine
    RegisterWidget(label, "Text")

    return label
end

-- ============================================================================
-- PROGRESS BAR
-- ============================================================================

function HDG.UI:ProgressBar(parent, options)
    options = options or {}
    local width = options.width or 200
    local height = options.height or 16
    local scheme = GetScheme()

    local container = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    container:SetSize(width, height)
    container:SetBackdrop(BACKDROP_FLAT)

    if scheme then
        container:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
        container:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
    end

    -- Fill bar
    local fill = container:CreateTexture(nil, "ARTWORK")
    fill:SetTexture("Interface\\Buttons\\WHITE8x8")
    fill:SetPoint("TOPLEFT", 2, -2)
    fill:SetPoint("BOTTOMLEFT", 2, 2)
    fill:SetWidth(1)
    if scheme then
        fill:SetVertexColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, scheme.accent.a)
    end
    container.fill = fill

    -- Progress text
    local text = container:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    text:SetPoint("CENTER")
    if scheme then
        HDG.Theme.ApplyFont(text, scheme, "small")
        text:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
    else
        text:SetFontObject("GameFontNormalSmall")
    end
    container.text = text

    -- SetProgress method
    function container:SetProgress(current, max)
        local pct = max > 0 and (current / max) or 0
        pct = math.min(1, math.max(0, pct))

        local fillWidth = math.max(1, (self:GetWidth() - 4) * pct)
        self.fill:SetWidth(fillWidth)
        self.text:SetText(string.format("%d / %d", current, max))
    end

    -- Register with theme engine
    RegisterWidget(container, "ProgressBar")

    return container
end

-- ============================================================================
-- SCROLL FRAME
-- ============================================================================

function HDG.UI:ScrollFrame(parent)
    local scheme = GetScheme()

    local scrollFrame = CreateFrame("ScrollFrame", nil, parent, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", 0, 0)
    scrollFrame:SetPoint("BOTTOMRIGHT", -1, 0)

    -- Style scrollbar
    local scrollBar = scrollFrame.ScrollBar
    if scrollBar then
        scrollBar:SetWidth(8)

        local thumb = scrollBar:GetThumbTexture()
        if thumb then
            if scheme and scheme.atlas and scheme.atlas.scrollThumb then
                thumb:SetAtlas(scheme.atlas.scrollThumb)
            else
                thumb:SetTexture("Interface\\Buttons\\WHITE8x8")
                if scheme then
                    thumb:SetVertexColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, scheme.accent.a)
                end
            end
            thumb:SetSize(6, 40)
        end

        -- Hide scroll buttons
        if scrollBar.ScrollUpButton then
            scrollBar.ScrollUpButton:SetAlpha(0)
            scrollBar.ScrollUpButton:EnableMouse(false)
        end
        if scrollBar.ScrollDownButton then
            scrollBar.ScrollDownButton:SetAlpha(0)
            scrollBar.ScrollDownButton:EnableMouse(false)
        end
    end

    -- Content container
    local content = CreateFrame("Frame", nil, scrollFrame)
    content:SetWidth(scrollFrame:GetWidth())
    content:SetHeight(1) -- Will be updated dynamically
    scrollFrame:SetScrollChild(content)
    scrollFrame.content = content

    -- Register with theme engine
    RegisterWidget(scrollFrame, "ScrollFrame")

    return scrollFrame, content
end

-- ============================================================================
-- CHECKBOX
-- ============================================================================

function HDG.UI:Checkbox(parent, size, labelText)
    local scheme = GetScheme()
    size = size or 16

    local container = CreateFrame("Frame", nil, parent)
    container:SetSize(size + (labelText and 100 or 0), size)

    -- Checkbox box using minimal atlas
    local box = CreateFrame("Button", nil, container)
    box:SetSize(size, size)
    box:SetPoint("LEFT", 0, 0)

    local boxTex = box:CreateTexture(nil, "ARTWORK")
    boxTex:SetAllPoints()
    boxTex:SetAtlas("checkbox-minimal")
    container.boxTex = boxTex

    -- Checkmark using minimal atlas
    local check = box:CreateTexture(nil, "OVERLAY")
    check:SetAllPoints()
    check:SetAtlas("checkmark-minimal")
    check:Hide()
    container.check = check

    -- Label
    if labelText then
        local text = container:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        text:SetPoint("LEFT", box, "RIGHT", 6, 0)
        if scheme then
            HDG.Theme.ApplyFont(text, scheme, "small")
            text:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
        else
            text:SetFontObject("GameFontNormalSmall")
        end
        text:SetText(labelText)
        container.text = text
    end

    -- State
    container.checked = false

    -- Toggle function
    function container:SetChecked(checked)
        self.checked = checked
        if checked then
            self.check:Show()
        else
            self.check:Hide()
        end
    end

    function container:GetChecked()
        return self.checked
    end

    -- Click handler
    box:SetScript("OnClick", function()
        container:SetChecked(not container.checked)
        if container.onChanged then
            container.onChanged(container.checked)
        end
    end)

    -- Register with theme engine
    RegisterWidget(container, "Checkbox")

    return container
end

-- ============================================================================
-- HDG-SPECIFIC: RECIPE ROW
-- ============================================================================

function HDG.UI:RecipeRow(parent, recipe)
    local scheme = GetScheme()

    local row = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    row:SetHeight(24)
    row:SetBackdrop(BACKDROP_FLAT)

    row.isKnown = recipe and recipe.known or false

    if scheme then
        if row.isKnown then
            row:SetBackdropColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, 0.15)
        else
            row:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
        end
        row:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
        row._scheme = scheme
    end

    -- Recipe name
    local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    nameText:SetPoint("LEFT", 8, 0)
    nameText:SetPoint("RIGHT", -80, 0)
    nameText:SetJustifyH("LEFT")
    if scheme then
        HDG.Theme.ApplyFont(nameText, scheme, "small")
        if row.isKnown then
            nameText:SetTextColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, scheme.accent.a)
        else
            nameText:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
        end
    else
        nameText:SetFontObject("GameFontNormalSmall")
    end
    if recipe then
        nameText:SetText(recipe.name or "Unknown")
    end
    row.nameText = nameText

    -- Profession text
    local professionText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    professionText:SetPoint("RIGHT", -8, 0)
    professionText:SetJustifyH("RIGHT")
    if scheme then
        HDG.Theme.ApplyFont(professionText, scheme, "small")
        professionText:SetTextColor(scheme.text_dim.r, scheme.text_dim.g, scheme.text_dim.b, scheme.text_dim.a)
    else
        professionText:SetFontObject("GameFontNormalSmall")
    end
    if recipe then
        professionText:SetText(recipe.profession or "")
    end
    row.professionText = professionText

    -- Hover effects
    row:EnableMouse(true)
    row:SetScript("OnEnter", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.button_hover.r, c.button_hover.g, c.button_hover.b, 0.3)
        end
    end)
    row:SetScript("OnLeave", function(self)
        local c = self._scheme or GetScheme()
        if c then
            if self.isKnown then
                self:SetBackdropColor(c.accent.r, c.accent.g, c.accent.b, 0.15)
            else
                self:SetBackdropColor(c.panel.r, c.panel.g, c.panel.b, c.panel.a)
            end
        end
    end)

    -- Register with theme engine
    RegisterWidget(row, "RecipeRow")

    return row
end

-- ============================================================================
-- HDG-SPECIFIC: MATERIAL ROW
-- ============================================================================

function HDG.UI:MaterialRow(parent, material)
    local scheme = GetScheme()

    local row = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    row:SetHeight(20)
    row:SetBackdrop(BACKDROP_FLAT)

    row.hasEnough = material and material.have >= material.need or false

    if scheme then
        row:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
        row:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
        row._scheme = scheme
    end

    -- Material name
    local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    nameText:SetPoint("LEFT", 8, 0)
    nameText:SetPoint("RIGHT", -60, 0)
    nameText:SetJustifyH("LEFT")
    if scheme then
        HDG.Theme.ApplyFont(nameText, scheme, "small")
        nameText:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
    else
        nameText:SetFontObject("GameFontNormalSmall")
    end
    if material then
        nameText:SetText(material.name or "Unknown")
    end
    row.nameText = nameText

    -- Count text (have/need)
    local countText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    countText:SetPoint("RIGHT", -8, 0)
    countText:SetJustifyH("RIGHT")
    if scheme then
        HDG.Theme.ApplyFont(countText, scheme, "small")
        if row.hasEnough then
            countText:SetTextColor(scheme.success.r, scheme.success.g, scheme.success.b, scheme.success.a)
        else
            countText:SetTextColor(scheme.error.r, scheme.error.g, scheme.error.b, scheme.error.a)
        end
    else
        countText:SetFontObject("GameFontNormalSmall")
    end
    if material then
        countText:SetText(string.format("%d/%d", material.have or 0, material.need or 0))
    end
    row.countText = countText

    -- Hover effects
    row:EnableMouse(true)
    row:SetScript("OnEnter", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.button_hover.r, c.button_hover.g, c.button_hover.b, 0.3)
        end
    end)
    row:SetScript("OnLeave", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.panel.r, c.panel.g, c.panel.b, c.panel.a)
        end
    end)

    -- Register with theme engine
    RegisterWidget(row, "MaterialRow")

    return row
end

-- ============================================================================
-- HDG-SPECIFIC: LUMBER ROW
-- ============================================================================

function HDG.UI:LumberRow(parent, lumberData)
    local scheme = GetScheme()

    local row = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    row:SetHeight(20)
    row:SetBackdrop(BACKDROP_FLAT)

    local have = lumberData and lumberData.have or 0
    local need = lumberData and lumberData.need or 0
    row.hasEnough = have >= need

    if scheme then
        row:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
        row:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
        row._scheme = scheme
    end

    -- Lumber name
    local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    nameText:SetPoint("LEFT", 8, 0)
    nameText:SetWidth(100)
    nameText:SetJustifyH("LEFT")
    if scheme then
        HDG.Theme.ApplyFont(nameText, scheme, "small")
        nameText:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
    else
        nameText:SetFontObject("GameFontNormalSmall")
    end
    if lumberData then
        nameText:SetText(lumberData.name or "Unknown")
    end
    row.nameText = nameText

    -- Needed text
    local neededText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    neededText:SetPoint("LEFT", nameText, "RIGHT", 8, 0)
    neededText:SetWidth(50)
    neededText:SetJustifyH("RIGHT")
    if scheme then
        HDG.Theme.ApplyFont(neededText, scheme, "small")
        neededText:SetTextColor(scheme.text_dim.r, scheme.text_dim.g, scheme.text_dim.b, scheme.text_dim.a)
    else
        neededText:SetFontObject("GameFontNormalSmall")
    end
    if lumberData then
        neededText:SetText(string.format("Need: %d", need))
    end
    row.neededText = neededText

    -- Have text
    local haveText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    haveText:SetPoint("RIGHT", -8, 0)
    haveText:SetJustifyH("RIGHT")
    if scheme then
        HDG.Theme.ApplyFont(haveText, scheme, "small")
        if row.hasEnough then
            haveText:SetTextColor(scheme.success.r, scheme.success.g, scheme.success.b, scheme.success.a)
        else
            haveText:SetTextColor(scheme.warning.r, scheme.warning.g, scheme.warning.b, scheme.warning.a)
        end
    else
        haveText:SetFontObject("GameFontNormalSmall")
    end
    if lumberData then
        haveText:SetText(string.format("Have: %d", have))
    end
    row.haveText = haveText

    -- Hover effects
    row:EnableMouse(true)
    row:SetScript("OnEnter", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.button_hover.r, c.button_hover.g, c.button_hover.b, 0.3)
        end
    end)
    row:SetScript("OnLeave", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.panel.r, c.panel.g, c.panel.b, c.panel.a)
        end
    end)

    -- Register with theme engine
    RegisterWidget(row, "LumberRow")

    return row
end

-- ============================================================================
-- HDG-SPECIFIC: FILTER BUTTON
-- ============================================================================

function HDG.UI:FilterButton(parent, text, isActive)
    local scheme = GetScheme()

    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetSize(80, 22)
    btn:SetText(text or "Filter")
    btn:SetBackdrop(BACKDROP_FLAT)

    btn.isActive = isActive or false

    if scheme then
        if btn.isActive then
            btn:SetBackdropColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, 0.3)
            btn:SetBackdropBorderColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, 0.8)
        else
            btn:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
            btn:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
        end

        local fs = btn:GetFontString()
        if fs then
            if btn.isActive then
                fs:SetTextColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, scheme.accent.a)
            else
                fs:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
            end
            HDG.Theme.ApplyFont(fs, scheme, "small")
        end

        btn._scheme = scheme
    end

    -- SetActive method
    function btn:SetActive(active)
        self.isActive = active
        local c = self._scheme or GetScheme()
        if c then
            if active then
                self:SetBackdropColor(c.accent.r, c.accent.g, c.accent.b, 0.3)
                self:SetBackdropBorderColor(c.accent.r, c.accent.g, c.accent.b, 0.8)
            else
                self:SetBackdropColor(c.panel.r, c.panel.g, c.panel.b, c.panel.a)
                self:SetBackdropBorderColor(c.border.r, c.border.g, c.border.b, c.border.a)
            end
            local fs = self:GetFontString()
            if fs then
                if active then
                    fs:SetTextColor(c.accent.r, c.accent.g, c.accent.b, c.accent.a)
                else
                    fs:SetTextColor(c.text.r, c.text.g, c.text.b, c.text.a)
                end
            end
        end
    end

    -- Hover effects (only when not active)
    btn:SetScript("OnEnter", function(self)
        if not self.isActive then
            local c = self._scheme or GetScheme()
            if c then
                self:SetBackdropColor(c.button_hover.r, c.button_hover.g, c.button_hover.b, 0.3)
            end
        end
    end)
    btn:SetScript("OnLeave", function(self)
        if not self.isActive then
            local c = self._scheme or GetScheme()
            if c then
                self:SetBackdropColor(c.panel.r, c.panel.g, c.panel.b, c.panel.a)
            end
        end
    end)

    -- Register with theme engine
    RegisterWidget(btn, "FilterButton")

    return btn
end

-- ============================================================================
-- HDG-SPECIFIC: TAB BUTTON
-- ============================================================================

function HDG.UI:TabButton(parent, text, isActive)
    local scheme = GetScheme()

    local btn = CreateFrame("Button", nil, parent)
    btn:SetSize(90, 24)
    btn.isActive = isActive or false

    -- Atlas textures
    local normalTex = btn:CreateTexture(nil, "BACKGROUND")
    normalTex:SetAllPoints()
    normalTex:SetAtlas("UI-DialogBox-Button-Up")
    btn.normalTex = normalTex

    local activeTex = btn:CreateTexture(nil, "BACKGROUND")
    activeTex:SetAllPoints()
    activeTex:SetAtlas("UI-DialogBox-Button-Down")
    activeTex:Hide()
    btn.activeTex = activeTex

    local highlight = btn:CreateTexture(nil, "HIGHLIGHT")
    highlight:SetAllPoints()
    highlight:SetAtlas("UI-DialogBox-Button-Highlight")
    btn:SetHighlightTexture(highlight)

    -- Font string
    local fs = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    fs:SetPoint("CENTER", 0, 0)
    fs:SetText(text or "Tab")
    btn:SetFontString(fs)

    if scheme then
        if btn.isActive then
            fs:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
        else
            fs:SetTextColor(scheme.text_dim.r, scheme.text_dim.g, scheme.text_dim.b, scheme.text_dim.a)
        end
        HDG.Theme.ApplyFont(fs, scheme, "small")
        btn._scheme = scheme
    end

    -- Apply active/inactive visual state
    local function ApplyState(self)
        if self.isActive then
            self.normalTex:Hide()
            self.activeTex:Show()
        else
            self.normalTex:Show()
            self.activeTex:Hide()
        end
        local c = self._scheme or GetScheme()
        if c then
            local f = self:GetFontString()
            if f then
                if self.isActive then
                    f:SetTextColor(c.text.r, c.text.g, c.text.b, c.text.a)
                else
                    f:SetTextColor(c.text_dim.r, c.text_dim.g, c.text_dim.b, c.text_dim.a)
                end
            end
        end
    end

    function btn:SetActive(active)
        self.isActive = active
        ApplyState(self)
    end

    ApplyState(btn)

    -- Register with theme engine
    RegisterWidget(btn, "TabButton")

    return btn
end

-- ============================================================================
-- HDG-SPECIFIC: PROFESSION HEADER (Collapsible)
-- ============================================================================

function HDG.UI:ProfessionHeader(parent, professionName, count, isCollapsed)
    local scheme = GetScheme()

    local header = CreateFrame("Button", nil, parent, "BackdropTemplate")
    header:SetHeight(28)
    header:SetBackdrop(BACKDROP_FLAT)

    if scheme then
        header:SetBackdropColor(scheme.bg.r, scheme.bg.g, scheme.bg.b, 0.8)
        header:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
        header._scheme = scheme
    end

    -- Accent bar on left
    local accentBar = header:CreateTexture(nil, "ARTWORK")
    accentBar:SetWidth(3)
    accentBar:SetPoint("TOPLEFT", 0, 0)
    accentBar:SetPoint("BOTTOMLEFT", 0, 0)
    accentBar:SetTexture("Interface\\Buttons\\WHITE8x8")
    if scheme then
        accentBar:SetVertexColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, scheme.accent.a)
    end
    header.accentBar = accentBar

    -- Collapse indicator
    local collapseText = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    collapseText:SetPoint("LEFT", 8, 0)
    if scheme then
        HDG.Theme.ApplyFont(collapseText, scheme, "small")
        collapseText:SetTextColor(scheme.text_dim.r, scheme.text_dim.g, scheme.text_dim.b, scheme.text_dim.a)
    else
        collapseText:SetFontObject("GameFontNormalSmall")
    end
    collapseText:SetText(isCollapsed and "+" or "-")
    header.collapseText = collapseText

    -- Profession name
    local nameText = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    nameText:SetPoint("LEFT", collapseText, "RIGHT", 6, 0)
    if scheme then
        HDG.Theme.ApplyFont(nameText, scheme, "body")
        nameText:SetTextColor(scheme.text_header.r, scheme.text_header.g, scheme.text_header.b, scheme.text_header.a)
    else
        nameText:SetFontObject("GameFontNormal")
    end
    nameText:SetText(professionName or "Profession")
    header.nameText = nameText

    -- Recipe count
    local countText = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    countText:SetPoint("RIGHT", -8, 0)
    if scheme then
        HDG.Theme.ApplyFont(countText, scheme, "small")
        countText:SetTextColor(scheme.text_dim.r, scheme.text_dim.g, scheme.text_dim.b, scheme.text_dim.a)
    else
        countText:SetFontObject("GameFontNormalSmall")
    end
    countText:SetText(count and string.format("(%d)", count) or "")
    header.countText = countText

    -- Collapsed state
    header.isCollapsed = isCollapsed or false

    function header:SetCollapsed(collapsed)
        self.isCollapsed = collapsed
        self.collapseText:SetText(collapsed and "+" or "-")
    end

    -- Hover effects
    header:SetScript("OnEnter", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.button_hover.r, c.button_hover.g, c.button_hover.b, 0.3)
        end
    end)
    header:SetScript("OnLeave", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.bg.r, c.bg.g, c.bg.b, 0.8)
        end
    end)

    -- Register with theme engine
    RegisterWidget(header, "ProfessionHeader")

    return header
end

-- ============================================================================
-- HDG-SPECIFIC: SHOPPING ITEM ROW
-- ============================================================================

function HDG.UI:ShoppingItem(parent, item)
    local scheme = GetScheme()

    local row = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    row:SetHeight(22)
    row:SetBackdrop(BACKDROP_FLAT)

    if scheme then
        row:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
        row:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
        row._scheme = scheme
    end

    -- Item name
    local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    nameText:SetPoint("LEFT", 8, 0)
    nameText:SetPoint("RIGHT", -50, 0)
    nameText:SetJustifyH("LEFT")
    if scheme then
        HDG.Theme.ApplyFont(nameText, scheme, "small")
        nameText:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
    else
        nameText:SetFontObject("GameFontNormalSmall")
    end
    if item then
        nameText:SetText(item.name or "Unknown")
    end
    row.nameText = nameText

    -- Quantity text
    local qtyText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    qtyText:SetPoint("RIGHT", -8, 0)
    qtyText:SetJustifyH("RIGHT")
    if scheme then
        HDG.Theme.ApplyFont(qtyText, scheme, "small")
        qtyText:SetTextColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, scheme.accent.a)
    else
        qtyText:SetFontObject("GameFontNormalSmall")
    end
    if item then
        qtyText:SetText(string.format("x%d", item.qty or 1))
    end
    row.qtyText = qtyText

    -- Hover effects
    row:EnableMouse(true)
    row:SetScript("OnEnter", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.button_hover.r, c.button_hover.g, c.button_hover.b, 0.3)
        end
    end)
    row:SetScript("OnLeave", function(self)
        local c = self._scheme or GetScheme()
        if c then
            self:SetBackdropColor(c.panel.r, c.panel.g, c.panel.b, c.panel.a)
        end
    end)

    -- Register with theme engine
    RegisterWidget(row, "ShoppingItem")

    return row
end

-- ============================================================================
-- TAB PANEL (v2.2.x - Standardized tab content panels)
-- Creates a complete panel structure for tab content
-- ============================================================================

function HDG.UI:TabPanel(parent, config)
    config = config or {}
    local scheme = GetScheme()
    local CONFIG = privateDB.CONFIG

    -- Get constants from CONFIG.TAB_PANEL
    local TAB = CONFIG.TAB_PANEL
    local BANNER_HEIGHT = CONFIG.BANNER_HEIGHT or 34
    local TAB_HEIGHT = CONFIG.TAB_HEIGHT or 32

    -- Calculate panel positioning
    local topOffset = -(BANNER_HEIGHT + TAB_HEIGHT + (TAB.TOP_OFFSET or 8))
    local sidePadding = TAB.SIDE_PADDING or CONFIG.SPACING.PANEL_PADDING or 4
    local bottomMargin = TAB.BOTTOM_MARGIN or 24

    -- Result object to return
    local result = {}

    -- ========================================================================
    -- MAIN PANEL FRAME
    -- ========================================================================
    local panel = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    panel:SetBackdrop(BACKDROP_FLAT)

    -- Handle single vs multi-column layout
    if config.layout == "columns" and config.columns then
        -- Multi-column: position based on columnIndex
        local colIndex = config.columnIndex or 1
        local colGap = TAB.COLUMN_GAP or 8
        local xOffset = sidePadding

        -- Calculate x offset based on previous columns
        for i = 1, colIndex - 1 do
            xOffset = xOffset + (config.columns[i].width or 200) + colGap
        end

        local colWidth = config.columns[colIndex].width or 200
        panel:SetPoint("TOPLEFT", xOffset, topOffset)
        panel:SetPoint("BOTTOMLEFT", xOffset, bottomMargin)
        panel:SetWidth(colWidth)
    else
        -- Single panel: full width
        panel:SetPoint("TOPLEFT", sidePadding, topOffset)
        panel:SetPoint("BOTTOMRIGHT", -sidePadding, bottomMargin)
    end

    if scheme then
        panel:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
        panel:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
    end

    RegisterWidget(panel, "Panel")
    result.frame = panel

    -- ========================================================================
    -- HEADER BAR (optional)
    -- ========================================================================
    if config.hasHeaderBar ~= false and config.title then
        local headerHeight = TAB.HEADER_HEIGHT or 24

        local headerBg = CreateFrame("Frame", nil, panel, "BackdropTemplate")
        headerBg:SetHeight(headerHeight)
        headerBg:SetPoint("TOPLEFT", 0, 0)
        headerBg:SetPoint("TOPRIGHT", 0, 0)
        headerBg:SetBackdrop(BACKDROP_FLAT)

        if scheme then
            local ph = scheme.panel_header or scheme.panel
            headerBg:SetBackdropColor(ph.r, ph.g, ph.b, ph.a or 1)
            headerBg:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
        end

        RegisterWidget(headerBg, "PanelHeader")
        result.headerBg = headerBg

        -- Title text with wood sign background
        local title = headerBg:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        title:SetPoint("CENTER", 0, 0)
        if scheme then
            HDG.Theme.ApplyFont(title, scheme, "body")
            title:SetTextColor(scheme.text_header.r, scheme.text_header.g, scheme.text_header.b, scheme.text_header.a or 1)
        end
        title:SetText(config.title)

        -- Add wood sign background (fills header bar)
        HDG.Theme.StyleHeaderTitle(title, headerBg)
        result.title = title
    end

    -- ========================================================================
    -- SCROLL FRAME (optional)
    -- ========================================================================
    if config.hasScroll ~= false then
        local scrollTopOffset = config.hasHeaderBar ~= false and config.title
            and (TAB.SCROLL_TOP_WITH_HEADER or -30)
            or (TAB.SCROLL_TOP_NO_HEADER or -8)

        local scrollFrame = CreateFrame("ScrollFrame", nil, panel, "UIPanelScrollFrameTemplate")
        scrollFrame:SetPoint("TOPLEFT", TAB.SCROLL_LEFT_MARGIN or 8, scrollTopOffset)
        scrollFrame:SetPoint("BOTTOMRIGHT", TAB.SCROLL_RIGHT_MARGIN or -28, TAB.SCROLL_BOTTOM_MARGIN or 8)

        -- Style scrollbar
        local scrollBar = scrollFrame.ScrollBar
        if scrollBar then
            scrollBar:SetWidth(8)
            local thumb = scrollBar:GetThumbTexture()
            if thumb then
                if scheme and scheme.atlas and scheme.atlas.scrollThumb then
                    thumb:SetAtlas(scheme.atlas.scrollThumb)
                else
                    thumb:SetTexture("Interface\\Buttons\\WHITE8x8")
                    if scheme then
                        thumb:SetVertexColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, scheme.accent.a)
                    end
                end
                thumb:SetSize(6, 40)
            end
            -- Hide scroll buttons
            if scrollBar.ScrollUpButton then
                scrollBar.ScrollUpButton:SetAlpha(0)
                scrollBar.ScrollUpButton:EnableMouse(false)
            end
            if scrollBar.ScrollDownButton then
                scrollBar.ScrollDownButton:SetAlpha(0)
                scrollBar.ScrollDownButton:EnableMouse(false)
            end
        end

        -- Scroll child
        local scrollChild = CreateFrame("Frame", nil, scrollFrame)
        scrollChild:SetWidth(scrollFrame:GetWidth() or 200)
        scrollChild:SetHeight(1) -- Will be updated dynamically
        scrollFrame:SetScrollChild(scrollChild)

        RegisterWidget(scrollFrame, "ScrollFrame")
        result.scrollFrame = scrollFrame
        result.scrollChild = scrollChild
    end

    return result
end

-- ============================================================================
-- SECTION TITLE (v2.2.x - Standardized section headers)
-- Creates an accent-colored section title
-- ============================================================================

function HDG.UI:SectionTitle(parent, text, yOffset)
    local scheme = GetScheme()

    local title = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    if yOffset then
        title:SetPoint("TOPLEFT", 10, yOffset)
    end

    if scheme then
        HDG.Theme.ApplyFont(title, scheme, "header")
        title:SetTextColor(scheme.accent.r, scheme.accent.g, scheme.accent.b, scheme.accent.a or 1)
    end
    title:SetText(text or "")

    RegisterWidget(title, "SectionHeader")

    return title
end

-- ============================================================================
-- FLOATING WIDGET (v2.2.x - Standalone floating windows)
-- Creates a movable, draggable widget parented to UIParent
-- ============================================================================

function HDG.UI:FloatingWidget(config)
    config = config or {}
    local scheme = GetScheme()
    local CONFIG = privateDB.CONFIG
    local FW = CONFIG.FLOATING_WIDGET

    -- Result object to return
    local result = {}

    -- ========================================================================
    -- MAIN FRAME (parented to UIParent for independence)
    -- ========================================================================
    local widget = CreateFrame("Frame", config.name, UIParent, "BackdropTemplate")
    widget:SetSize(config.width or FW.LUMBER_WIDTH, config.height or 200)
    widget:SetFrameStrata(config.strata or "DIALOG")
    widget:SetToplevel(true)
    widget:SetFrameLevel(100)
    widget:SetClampedToScreen(true)
    widget:SetMovable(true)
    widget:EnableMouse(true)
    widget:RegisterForDrag("LeftButton")

    -- Default position (can be overridden by config.position or restored from saved)
    if config.position then
        widget:SetPoint(config.position[1], UIParent, config.position[2] or config.position[1],
            config.position[3] or 0, config.position[4] or 0)
    else
        widget:SetPoint("RIGHT", UIParent, "RIGHT", -50, 0)
    end

    widget:SetBackdrop(BACKDROP_FLAT)
    if scheme then
        widget:SetBackdropColor(scheme.bg.r, scheme.bg.g, scheme.bg.b, scheme.bg.a)
        widget:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
    end

    -- Dragging
    widget:SetScript("OnDragStart", function(self) self:StartMoving() end)
    widget:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        if config.onPositionSaved then
            local point, _, relativePoint, xOfs, yOfs = self:GetPoint()
            config.onPositionSaved(point, relativePoint, xOfs, yOfs)
        end
    end)

    RegisterWidget(widget, "Frame")
    result.frame = widget

    -- ========================================================================
    -- HEADER ROW (title + action buttons)
    -- ========================================================================

    -- Title texture (replaces icon + text approach)
    if config.titleTexture then
        local titleTex = widget:CreateTexture(nil, "ARTWORK")
        titleTex:SetSize(config.titleTextureSize and config.titleTextureSize[1] or 160,
                         config.titleTextureSize and config.titleTextureSize[2] or 35)
        titleTex:SetPoint("LEFT", 4, 0)
        titleTex:SetPoint("TOP", 0, 0)
        titleTex:SetTexture(config.titleTexture)
        result.titleTexture = titleTex
    end

    -- Action buttons container (right side of header)
    result.headerButtons = {}
    local btnX = -(FW.HEADER_BTN_MARGIN)

    -- Helper to create header buttons
    local function CreateHeaderBtn(size, tooltip)
        local btn = CreateFrame("Button", nil, widget, "BackdropTemplate")
        btn:SetSize(size or FW.HEADER_BTN_SIZE, size or FW.HEADER_BTN_SIZE)
        btn:SetPoint("TOPRIGHT", btnX, -FW.HEADER_BTN_MARGIN)
        btn:SetBackdrop(BACKDROP_FLAT)

        local btnScheme = GetScheme()
        if btnScheme then
            btn:SetBackdropColor(btnScheme.button_normal.r, btnScheme.button_normal.g, btnScheme.button_normal.b, btnScheme.button_normal.a)
            btn:SetBackdropBorderColor(btnScheme.border.r, btnScheme.border.g, btnScheme.border.b, btnScheme.border.a)
        end

        btn:SetScript("OnEnter", function(self)
            local hScheme = GetScheme()
            if hScheme then
                self:SetBackdropColor(hScheme.button_hover.r, hScheme.button_hover.g, hScheme.button_hover.b, hScheme.button_hover.a)
            end
            if tooltip then
                GameTooltip:SetOwner(self, HDG_UIHelpers and HDG_UIHelpers.TOOLTIP_ANCHOR or "ANCHOR_RIGHT")
                GameTooltip:SetText(tooltip, 1, 1, 1)
                GameTooltip:Show()
            end
        end)
        btn:SetScript("OnLeave", function(self)
            local lScheme = GetScheme()
            if lScheme then
                self:SetBackdropColor(lScheme.button_normal.r, lScheme.button_normal.g, lScheme.button_normal.b, lScheme.button_normal.a)
            end
            GameTooltip:Hide()
        end)

        RegisterWidget(btn, "Button")
        btnX = btnX - (size or FW.HEADER_BTN_SIZE) - FW.HEADER_BTN_GAP
        return btn
    end
    result.CreateHeaderButton = CreateHeaderBtn

    -- ========================================================================
    -- CONTENT PANELS
    -- ========================================================================

    -- Helper to create a panel section
    function result:CreatePanel(panelConfig)
        panelConfig = panelConfig or {}
        local panel = CreateFrame("Frame", nil, widget, "BackdropTemplate")

        if panelConfig.points then
            for _, pt in ipairs(panelConfig.points) do
                panel:SetPoint(unpack(pt))
            end
        end
        if panelConfig.height then
            panel:SetHeight(panelConfig.height)
        end

        panel:SetBackdrop(BACKDROP_FLAT)
        local pScheme = GetScheme()
        if pScheme then
            panel:SetBackdropColor(pScheme.panel.r, pScheme.panel.g, pScheme.panel.b, pScheme.panel.a)
            panel:SetBackdropBorderColor(pScheme.border.r, pScheme.border.g, pScheme.border.b, pScheme.border.a)
        end

        RegisterWidget(panel, "Panel")

        -- Optional header row within panel
        if panelConfig.hasHeader then
            local headerBg = CreateFrame("Frame", nil, panel, "BackdropTemplate")
            headerBg:SetHeight(FW.SECTION_HEADER_HEIGHT)
            headerBg:SetPoint("TOPLEFT", 1, -1)
            headerBg:SetPoint("TOPRIGHT", -1, -1)
            headerBg:SetBackdrop({ bgFile = CONFIG.TEXTURES.WHITE_BG, tile = false })

            local hScheme = GetScheme()
            if hScheme then
                headerBg:SetBackdropColor(hScheme.bg.r, hScheme.bg.g, hScheme.bg.b, 0.8)
            end

            panel.headerBg = headerBg

            if panelConfig.headerTitle then
                local headerTitle = headerBg:CreateFontString(nil, "OVERLAY", "GameFontNormal")
                headerTitle:SetPoint("LEFT", 8, 0)
                if hScheme then
                    HDG.Theme.ApplyFont(headerTitle, hScheme, "body")
                end
                headerTitle:SetText("|cFFFFFFFF" .. panelConfig.headerTitle .. "|r")
                panel.headerTitle = headerTitle
            end
        end

        -- Content area (static frame for direct children, or scroll frame)
        if panelConfig.hasScroll then
            local scrollFrame = CreateFrame("ScrollFrame", nil, panel, "UIPanelScrollFrameTemplate")
            local scrollTop = panelConfig.hasHeader and -(FW.SECTION_HEADER_HEIGHT + 2) or -4
            scrollFrame:SetPoint("TOPLEFT", 4, scrollTop)
            scrollFrame:SetPoint("BOTTOMRIGHT", -20, 4)

            -- Style scrollbar
            local scrollBar = scrollFrame.ScrollBar
            if scrollBar then
                scrollBar:SetWidth(8)
                local thumb = scrollBar:GetThumbTexture()
                if thumb then
                    local sScheme = GetScheme()
                    if sScheme and sScheme.atlas and sScheme.atlas.scrollThumb then
                        thumb:SetAtlas(sScheme.atlas.scrollThumb)
                    else
                        thumb:SetTexture("Interface\\Buttons\\WHITE8x8")
                        if sScheme then
                            thumb:SetVertexColor(sScheme.accent.r, sScheme.accent.g, sScheme.accent.b, sScheme.accent.a)
                        end
                    end
                    thumb:SetSize(6, 40)
                end
                if scrollBar.ScrollUpButton then
                    scrollBar.ScrollUpButton:SetAlpha(0)
                    scrollBar.ScrollUpButton:EnableMouse(false)
                end
                if scrollBar.ScrollDownButton then
                    scrollBar.ScrollDownButton:SetAlpha(0)
                    scrollBar.ScrollDownButton:EnableMouse(false)
                end
            end

            local scrollChild = CreateFrame("Frame", nil, scrollFrame)
            scrollChild:SetWidth(scrollFrame:GetWidth() or 200)
            scrollChild:SetHeight(1)
            scrollFrame:SetScrollChild(scrollChild)

            RegisterWidget(scrollFrame, "ScrollFrame")
            panel.scrollFrame = scrollFrame
            panel.scrollChild = scrollChild
            panel.content = scrollChild
        else
            -- Static content area
            local content = CreateFrame("Frame", nil, panel)
            local contentTop = panelConfig.hasHeader and -(FW.SECTION_HEADER_HEIGHT + 2) or -4
            content:SetPoint("TOPLEFT", 4, contentTop)
            content:SetPoint("TOPRIGHT", -4, contentTop)
            content:SetHeight(panelConfig.contentHeight or 100)
            panel.content = content
        end

        return panel
    end

    return result
end

-- ============================================================================
-- DROPDOWN (Reusable dropdown selector with shared popup)
-- ============================================================================

-- Shared singleton popup frame (reused across all Dropdown instances)
local dropdownPopup = nil

local function CloseDropdownPopup()
    if dropdownPopup then dropdownPopup:Hide() end
end

HDG.UI.CloseDropdownPopup = CloseDropdownPopup

function HDG.UI:Dropdown(parent, config)
    config = config or {}
    local scheme = GetScheme()
    local width = config.width or 120

    local container = CreateFrame("Frame", nil, parent)
    container:SetSize(width, 22)
    container._currentValue = config.currentValue or "All"
    container._items = config.items or {}

    local btn = CreateFrame("Button", nil, container, "BackdropTemplate")
    btn:SetAllPoints()
    btn:SetBackdrop(BACKDROP_FLAT)
    if scheme then
        btn:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, scheme.panel.a)
        btn:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, scheme.border.a)
    end
    container._btn = btn

    local text = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    text:SetPoint("LEFT", 4, 0)
    text:SetPoint("RIGHT", -16, 0)
    text:SetJustifyH("LEFT")
    text:SetWordWrap(false)
    if scheme then
        HDG.Theme.ApplyFont(text, scheme, "small")
        text:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b, scheme.text.a)
    end
    text:SetText((container._currentValue == "All" and config.allLabel) or container._currentValue)
    container._text = text

    -- Arrow indicator (texture — WoW doesn't support Unicode glyphs)
    local arrow = btn:CreateTexture(nil, "OVERLAY")
    arrow:SetSize(10, 10)
    arrow:SetPoint("RIGHT", -3, 0)
    arrow:SetTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Up")
    arrow:SetTexCoord(0.25, 0.75, 0.25, 0.75)
    arrow:SetVertexColor(0.67, 0.67, 0.67)

    btn:SetScript("OnEnter", function(self)
        local c = GetScheme()
        if c then self:SetBackdropColor(c.button_hover.r, c.button_hover.g, c.button_hover.b, c.button_hover.a) end
    end)
    btn:SetScript("OnLeave", function(self)
        local c = GetScheme()
        if c then self:SetBackdropColor(c.panel.r, c.panel.g, c.panel.b, c.panel.a) end
    end)

    btn:SetScript("OnClick", function(self)
        if dropdownPopup and dropdownPopup:IsShown() and dropdownPopup._owner == container then
            CloseDropdownPopup()
            return
        end

        -- Create shared popup once
        if not dropdownPopup then
            dropdownPopup = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
            dropdownPopup:SetFrameStrata("TOOLTIP")
            dropdownPopup:SetClampedToScreen(true)
            dropdownPopup:SetBackdrop(BACKDROP_FLAT)

            local sf = CreateFrame("ScrollFrame", nil, dropdownPopup, "UIPanelScrollFrameTemplate")
            sf:SetPoint("TOPLEFT", 2, -2)
            sf:SetPoint("BOTTOMRIGHT", -12, 2)
            local bar = sf.ScrollBar
            if bar then
                bar:SetWidth(8)
                if bar.ScrollUpButton then bar.ScrollUpButton:SetAlpha(0); bar.ScrollUpButton:SetSize(1, 1) end
                if bar.ScrollDownButton then bar.ScrollDownButton:SetAlpha(0); bar.ScrollDownButton:SetSize(1, 1) end
                local trackBg = bar:CreateTexture(nil, "BACKGROUND")
                trackBg:SetAtlas("!minimal-scrollbar-track-middle")
                trackBg:SetPoint("TOPLEFT", 0, 0)
                trackBg:SetPoint("BOTTOMRIGHT", 0, 0)
                trackBg:SetAlpha(0.3)
                local thumb = bar:GetThumbTexture()
                if thumb then
                    thumb:SetAtlas("minimal-scrollbar-thumb-middle")
                    thumb:SetWidth(8)
                    thumb:SetAlpha(0.7)
                end
            end
            local content = CreateFrame("Frame", nil, sf)
            content:SetWidth(1)
            content:SetHeight(1)
            sf:SetScrollChild(content)
            dropdownPopup.scrollFrame = sf
            dropdownPopup.content = content

            dropdownPopup:SetScript("OnKeyDown", function(popup, key)
                if key == "ESCAPE" then
                    popup:SetPropagateKeyboardInput(false)
                    CloseDropdownPopup()
                else
                    popup:SetPropagateKeyboardInput(true)
                end
            end)
            dropdownPopup:EnableKeyboard(true)
            RegisterWidget(dropdownPopup, "Dropdown")
        end

        local popup = dropdownPopup
        popup._owner = container
        local s = GetScheme()

        -- Clear old children
        for _, child in ipairs({popup.content:GetChildren()}) do
            child:Hide()
            child:SetParent(nil)
        end

        -- Build item list: "All" + items
        local allItems = {"All"}
        for _, v in ipairs(container._items) do table.insert(allItems, v) end

        local ROW_H = 18
        local popupWidth = math.max(width, 160)
        local maxVisible = math.min(#allItems, 20)
        local popupHeight = maxVisible * ROW_H + 4

        popup:SetSize(popupWidth, popupHeight)
        popup.content:SetWidth(popupWidth - 16)
        popup.content:SetHeight(#allItems * ROW_H)

        if s then
            popup:SetBackdropColor(s.bg.r, s.bg.g, s.bg.b, 0.95)
            popup:SetBackdropBorderColor(s.border.r, s.border.g, s.border.b, s.border.a)
        end

        for i, itemVal in ipairs(allItems) do
            local row = CreateFrame("Button", nil, popup.content, "BackdropTemplate")
            row:SetSize(popupWidth - 16, ROW_H)
            row:SetPoint("TOPLEFT", 0, -((i - 1) * ROW_H))
            row:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8" })

            local isSelected = (container._currentValue == itemVal)
            if s then
                if isSelected then
                    row:SetBackdropColor(s.accent.r, s.accent.g, s.accent.b, 0.3)
                else
                    row:SetBackdropColor(0, 0, 0, 0)
                end
            end

            local rowText = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            rowText:SetPoint("LEFT", 6, 0)
            rowText:SetPoint("RIGHT", -4, 0)
            rowText:SetJustifyH("LEFT")
            rowText:SetWordWrap(false)
            rowText:SetText((itemVal == "All" and config.allLabel) or itemVal)
            if s then
                HDG.Theme.ApplyFont(rowText, s, "small")
                local itemColor = config.colorFn and config.colorFn(itemVal)
                if itemColor then
                    rowText:SetTextColor(itemColor[1], itemColor[2], itemColor[3], 1)
                else
                    rowText:SetTextColor(s.text.r, s.text.g, s.text.b, s.text.a)
                end
            end

            row:SetScript("OnEnter", function(r)
                local c = GetScheme()
                if c then r:SetBackdropColor(c.button_hover.r, c.button_hover.g, c.button_hover.b, 0.5) end
            end)
            row:SetScript("OnLeave", function(r)
                local c = GetScheme()
                if c then
                    if container._currentValue == itemVal then
                        r:SetBackdropColor(c.accent.r, c.accent.g, c.accent.b, 0.3)
                    else
                        r:SetBackdropColor(0, 0, 0, 0)
                    end
                end
            end)

            row:SetScript("OnClick", function()
                container._currentValue = itemVal
                container._text:SetText((itemVal == "All" and config.allLabel) or itemVal)
                local selColor = config.colorFn and config.colorFn(itemVal)
                if selColor then
                    container._text:SetTextColor(selColor[1], selColor[2], selColor[3], 1)
                elseif s then
                    container._text:SetTextColor(s.text.r, s.text.g, s.text.b, s.text.a)
                end
                if config.onChange then config.onChange(itemVal) end
                CloseDropdownPopup()
            end)
        end

        popup:ClearAllPoints()
        popup:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -2)
        popup:Show()
    end)

    -- Public API
    function container:SetValue(val)
        self._currentValue = val
        self._text:SetText((val == "All" and config.allLabel) or val)
    end

    function container:GetValue()
        return self._currentValue
    end

    function container:SetItems(newItems)
        self._items = newItems
    end

    RegisterWidget(btn, "Dropdown")
    return container
end

-- ============================================================================
-- EXPOSE TO PRIVATEDB FOR OTHER MODULES
-- ============================================================================

privateDB.HDG_UI = HDG.UI
