-- New Applicants!! - UI Components
-- Version: 1.1.5

local NA = _G["NewApplicants"]
if not NA then return end

-- Create Main Frame
function NA:CreateMainFrame()
    local f = CreateFrame("Frame", "NewApplicantFrame", UIParent, "BackdropTemplate")
    f:SetSize(420, 300)
    f:SetPoint(self.db.position.point, UIParent, self.db.position.point, self.db.position.x, self.db.position.y)
    f:SetScale(self.db.scale)
    f:SetMovable(true)
    f:SetClampedToScreen(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetFrameStrata("MEDIUM")
    f:SetFrameLevel(10)
    
    f:SetBackdrop(nil)
    
    f:SetScript("OnDragStart", function(self) self:StartMoving() end)
    f:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        local point, _, _, x, y = self:GetPoint()
        NA.db.position = { point = point, x = x, y = y }
    end)
    
    local header = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    header:SetPoint("TOP", 0, -10)
    header:SetText("|cFFFFD100New Applicants!!|r")
    f.header = header
    
    ---- Header animation
    local animTime = 0
    local baseY = -10
    local animSpeed = 6
    local animHeight = 6
    
    f:SetScript("OnUpdate", function(self, elapsed)
        animTime = animTime + elapsed
        local offsetY = math.sin(animTime * animSpeed) * animHeight
        header:SetPoint("TOP", 0, baseY + offsetY)
    end)
    
    ---- Close Button
        local closeBtn = CreateFrame("Button", nil, f)
    closeBtn:SetSize(20, 20)
    closeBtn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -30, -5)
    closeBtn:SetNormalAtlas("Radial_Wheel_Icon_Close")
    closeBtn:SetHighlightAtlas("Radial_Wheel_Icon_Close")
    closeBtn:GetNormalTexture():SetVertexColor(1, 1, 1, 0.45)
    closeBtn:GetHighlightTexture():SetVertexColor(1, 1, 1, 1.0)
    closeBtn:SetScript("OnClick", function() f:Hide() end)
    closeBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_LEFT")
        GameTooltip:SetText(NA.L["Close"] or "Close", 1, 1, 1)
        GameTooltip:Show()
    end)
    closeBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)

    ---- Setting Button
    local settingsBtn = CreateFrame("Button", nil, f)
    settingsBtn:SetSize(36, 36)
    settingsBtn:SetPoint("TOPLEFT", f, "TOPLEFT", 5, -2)
    settingsBtn:SetNormalAtlas("GM-icon-settings")
    settingsBtn:SetHighlightAtlas("GM-icon-settings")
    settingsBtn:GetNormalTexture():SetVertexColor(1, 1, 1, 0.45)
    settingsBtn:GetHighlightTexture():SetVertexColor(1, 1, 1, 1.0)
    settingsBtn:SetScript("OnClick", function() NA:ShowSettingsPanel() end)
    settingsBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(NA.L and NA.L["Settings"] or "Settings", 1, 1, 1)
        GameTooltip:Show()
    end)
    settingsBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)

    ---- Lock Button
    local lockBtn = CreateFrame("Button", nil, f)
    lockBtn:SetSize(17, 17)
    lockBtn:SetPoint("TOPLEFT", settingsBtn, "TOPRIGHT", 4, -8)
    f.lockBtn = lockBtn

    if self.db.isLocked == nil then
        self.db.isLocked = false
    end

    local function UpdateLockButton()
        if NA.db.isLocked then
            lockBtn:SetNormalAtlas("friendslist-recentallies-Pin")
            lockBtn:SetHighlightAtlas("friendslist-recentallies-Pin")
            lockBtn:GetNormalTexture():SetVertexColor(1, 1, 1, 1.0)
            lockBtn:GetHighlightTexture():SetVertexColor(1, 1, 1, 1.0)
            f:SetMovable(false)
            f:EnableMouse(false)
        else
            lockBtn:SetNormalAtlas("friendslist-recentallies-Pin-yellow")
            lockBtn:SetHighlightAtlas("friendslist-recentallies-Pin-yellow")
            lockBtn:GetNormalTexture():SetVertexColor(1, 1, 1, 0.45)
            lockBtn:GetHighlightTexture():SetVertexColor(1, 1, 1, 1.0)
            f:SetMovable(true)
            f:EnableMouse(true)
        end
    end

    lockBtn:SetScript("OnClick", function()
        NA.db.isLocked = not NA.db.isLocked
        UpdateLockButton()
    end)
    lockBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        if NA.db.isLocked then
            GameTooltip:SetText(NA.L["Position locked"] or "Position locked", 1, 0.2, 0.2)
            GameTooltip:AddLine(NA.L["Click to unlock"] or "Click to unlock", 0.7, 0.7, 0.7, true)
        else
            GameTooltip:SetText(NA.L["Position unlocked"] or "Position unlocked", 0.2, 1, 0.2)
            GameTooltip:AddLine(NA.L["Click to lock"] or "Click to lock", 0.7, 0.7, 0.7, true)
        end
        GameTooltip:Show()
    end)
    lockBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)

    UpdateLockButton()

    ---- LFG eye 
    local eyeBtn = CreateFrame("Button", nil, f)
    eyeBtn:SetSize(24, 24)
    eyeBtn:SetPoint("TOPRIGHT", closeBtn, "TOPLEFT", -6, 0)
    
    local eyeNormal = eyeBtn:CreateTexture(nil, "ARTWORK")
    eyeNormal:SetAllPoints()
    eyeNormal:SetAtlas("groupfinder-eye-single")
    eyeBtn:SetNormalTexture(eyeNormal)
    
    local eyePushed = eyeBtn:CreateTexture(nil, "ARTWORK")
    eyePushed:SetAllPoints()
    eyePushed:SetAtlas("groupfinder-eye-single")
    eyePushed:SetVertexColor(0.7, 0.7, 0.7)
    eyeBtn:SetPushedTexture(eyePushed)
    
    local eyeHighlight = eyeBtn:CreateTexture(nil, "HIGHLIGHT")
    eyeHighlight:SetAllPoints()
    eyeHighlight:SetAtlas("groupfinder-eye-single")
    eyeHighlight:SetVertexColor(1, 1, 0.6)
    eyeHighlight:SetBlendMode("ADD")
    eyeBtn:SetHighlightTexture(eyeHighlight)
    
    eyeBtn:SetScript("OnClick", function() NA:OpenLFGFrame() end)
    eyeBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(NA.L and NA.L["Open Group Finder"] or "Open Group Finder", 1, 1, 1)
        GameTooltip:Show()
    end)
    eyeBtn:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)
    
    ---- scroll frame
    local scroll = CreateFrame("ScrollFrame", "NewApplicantScrollFrame", f, "UIPanelScrollFrameTemplate")
    scroll:SetPoint("TOPLEFT", 10, -35)
    scroll:SetPoint("BOTTOMRIGHT", -30, 10)
    
    local content = CreateFrame("Frame", nil, scroll)
    content:SetSize(380, 400)
    scroll:SetScrollChild(content)
    
    f.scroll = scroll
    f.content = content
    self.mainFrame = f
    f:Hide()
end

-- Create Settings Panel
function NA:ShowSettingsPanel()
    if self.settingsFrame then self.settingsFrame:Show() return end

    NA._panelCount = (NA._panelCount or 0) + 1
    local pid = NA._panelCount

    -- Main frame
    local f = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
    f:SetSize(320, 420)
    f:SetPoint("CENTER")
    f:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        tile = true, tileSize = 8, edgeSize = 32,
        insets = { left = 8, right = 8, top = 8, bottom = 8 }
    })
    f:SetBackdropColor(0.08, 0.08, 0.10, 1)
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", f.StopMovingOrSizing)
    f:SetFrameStrata("DIALOG")
    f:SetFrameLevel(100)

    -- Title
    local title = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOP", 0, -15)
    local settingsTitle = NA.L and NA.L["Settings"] or "Settings"
    title:SetText("|cFFF8D943New Applicants!! " .. settingsTitle .. "|r")

    -- Close button
    local closeBtn = CreateFrame("Button", nil, f)
    closeBtn:SetSize(14, 14)
    closeBtn:SetPoint("TOPRIGHT", -8, -8)
    closeBtn:SetNormalAtlas("Radial_Wheel_Icon_Close")
    closeBtn:SetHighlightAtlas("Radial_Wheel_Icon_Close")
    closeBtn:GetNormalTexture():SetVertexColor(1, 1, 1, 0.45)
    closeBtn:GetHighlightTexture():SetVertexColor(1, 1, 1, 1.0)
    closeBtn:SetScript("OnClick", function() f:Hide() end)

    -- ── Tab buttons ──────────────────────────────────────────────
    local TAB_TOP_Y  = -44   -- space below title (~-15)
    local CONTENT_Y  = TAB_TOP_Y - 24 - 8  -- tab height 24 + bottom margin 8

    local function MakeTabButton(parent, label, anchorFrame, anchorSide)
        local btn = CreateFrame("Button", nil, parent)
        btn:SetSize(148, 24)
        if anchorSide == "LEFT" then
            btn:SetPoint("LEFT", anchorFrame, "RIGHT", 0, 0)
        else
            btn:SetPoint("TOPLEFT", parent, "TOPLEFT", 12, TAB_TOP_Y)
        end

        local bg = btn:CreateTexture(nil, "BACKGROUND")
        bg:SetAllPoints()
        bg:SetColorTexture(0.12, 0.12, 0.12, 0.95)
        btn.bg = bg

        local hl = btn:CreateTexture(nil, "HIGHLIGHT")
        hl:SetAllPoints()
        hl:SetColorTexture(1, 1, 1, 0.06)

        local text = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        text:SetAllPoints()
        text:SetText(label)
        text:SetTextColor(0.6, 0.6, 0.6, 1)
        btn.text = text

        return btn
    end

    local tabDisplay = MakeTabButton(f, NA.L and NA.L["Display"] or "Display", nil, "FIRST")
    local tabNotify  = MakeTabButton(f, NA.L and NA.L["Notifications"] or "Notifications", tabDisplay, "LEFT")

    -- ── Tab content containers ───────────────────────────────────
    local tabContent1 = CreateFrame("Frame", nil, f)
    tabContent1:SetPoint("TOPLEFT", 0, CONTENT_Y)
    tabContent1:SetPoint("BOTTOMRIGHT", 0, 10)

    local tabContent2 = CreateFrame("Frame", nil, f)
    tabContent2:SetPoint("TOPLEFT", 0, CONTENT_Y)
    tabContent2:SetPoint("BOTTOMRIGHT", 0, 10)

    local function SwitchTab(idx)
        if idx == 1 then
            tabContent1:Show()
            tabContent2:Hide()
            tabDisplay.bg:SetColorTexture(0.22, 0.22, 0.22, 1.0)
            tabDisplay.text:SetTextColor(1, 1, 1, 1)
            tabNotify.bg:SetColorTexture(0.12, 0.12, 0.12, 0.95)
            tabNotify.text:SetTextColor(0.6, 0.6, 0.6, 1)
        else
            tabContent1:Hide()
            tabContent2:Show()
            tabNotify.bg:SetColorTexture(0.22, 0.22, 0.22, 1.0)
            tabNotify.text:SetTextColor(1, 1, 1, 1)
            tabDisplay.bg:SetColorTexture(0.12, 0.12, 0.12, 0.95)
            tabDisplay.text:SetTextColor(0.6, 0.6, 0.6, 1)
        end
    end

    tabDisplay:SetScript("OnClick", function() SwitchTab(1) end)
    tabNotify:SetScript("OnClick", function() SwitchTab(2) end)

    -- ════════════════════════════════════════════════════════════
    -- TAB 1: Display settings
    -- ════════════════════════════════════════════════════════════
    local currentText = NA.L and NA.L["Current:"] or "Current:"

    ---- 1. Background alpha
    local alphaLabel = tabContent1:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    alphaLabel:SetPoint("TOPLEFT", 20, -10)
    alphaLabel:SetText("|cFFFFD1001. " .. (NA.L and NA.L["Background Opacity"] or "Background Opacity") .. "|r")

    local alphaSlider = CreateFrame("Slider", "NAAlphaSlider"..pid, tabContent1, "OptionsSliderTemplate")
    alphaSlider:SetPoint("TOPLEFT", tabContent1, "TOPLEFT", 20, -30)
    alphaSlider:SetPoint("TOPRIGHT", tabContent1, "TOPRIGHT", -20, -30)
    alphaSlider:SetHeight(20)
    alphaSlider:SetMinMaxValues(0, 1)
    alphaSlider:SetValue(self.db.backgroundAlpha)
    alphaSlider:SetValueStep(0.05)
    alphaSlider:SetObeyStepOnDrag(true)
    _G[alphaSlider:GetName().."Low"]:SetText(NA.L and NA.L["slider_alpha_low"] or "0%")
    _G[alphaSlider:GetName().."High"]:SetText(NA.L and NA.L["slider_alpha_high"] or "100%")
    _G[alphaSlider:GetName().."Text"]:SetText("")

    local alphaValue = tabContent1:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    alphaValue:SetPoint("TOP", alphaSlider, "BOTTOM", 0, 2)
    alphaValue:SetText(string.format(currentText .. " %.0f%%", self.db.backgroundAlpha * 100))

    alphaSlider:SetScript("OnValueChanged", function(_, value)
        NA.db.backgroundAlpha = value
        if NA.mainFrame and NA.mainFrame.content and NA.mainFrame.content.applicantFrames then
            for _, cardFrame in ipairs(NA.mainFrame.content.applicantFrames) do
                cardFrame:SetAlpha(value)
            end
        end
        alphaValue:SetText(string.format(currentText .. " %.0f%%", value * 100))
    end)

    ---- 2. Max applicants
    local maxLabel = tabContent1:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    maxLabel:SetPoint("TOPLEFT", 20, -75)
    maxLabel:SetText("|cFFFFD1002. " .. (NA.L and NA.L["Max Display"] or "Max Display") .. "|r")

    local maxSlider = CreateFrame("Slider", "NAMaxSlider"..pid, tabContent1, "OptionsSliderTemplate")
    maxSlider:SetPoint("TOPLEFT", tabContent1, "TOPLEFT", 20, -95)
    maxSlider:SetPoint("TOPRIGHT", tabContent1, "TOPRIGHT", -20, -95)
    maxSlider:SetHeight(20)
    maxSlider:SetMinMaxValues(1, 8)
    maxSlider:SetValue(self.db.maxDisplay)
    maxSlider:SetValueStep(1)
    maxSlider:SetObeyStepOnDrag(true)
    _G[maxSlider:GetName().."Low"]:SetText(NA.L and NA.L["slider_max_low"] or "1")
    _G[maxSlider:GetName().."High"]:SetText(NA.L and NA.L["slider_max_high"] or "8")
    _G[maxSlider:GetName().."Text"]:SetText("")

    local maxValue = tabContent1:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    maxValue:SetPoint("TOP", maxSlider, "BOTTOM", 0, 2)
    local peopleText = NA.L and NA.L["people"] or "people"
    maxValue:SetText(string.format(currentText .. " %d" .. peopleText, self.db.maxDisplay))

    maxSlider:SetScript("OnValueChanged", function(_, value)
        NA.db.maxDisplay = value
        maxValue:SetText(string.format(currentText .. " %d" .. peopleText, value))
        if NA.UpdateDisplay then NA:UpdateDisplay() end
    end)

    ---- 3. Scale
    local scaleLabel = tabContent1:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    scaleLabel:SetPoint("TOPLEFT", 20, -140)
    scaleLabel:SetText("|cFFFFD1003. " .. (NA.L and NA.L["Scale"] or "Scale") .. "|r")

    local scaleSlider = CreateFrame("Slider", "NAScaleSlider"..pid, tabContent1, "OptionsSliderTemplate")
    scaleSlider:SetPoint("TOPLEFT", tabContent1, "TOPLEFT", 20, -160)
    scaleSlider:SetPoint("TOPRIGHT", tabContent1, "TOPRIGHT", -20, -160)
    scaleSlider:SetHeight(20)
    scaleSlider:SetMinMaxValues(0.75, 1.25)
    scaleSlider:SetValue(self.db.scale)
    scaleSlider:SetValueStep(0.05)
    scaleSlider:SetObeyStepOnDrag(true)
    _G[scaleSlider:GetName().."Low"]:SetText(NA.L and NA.L["slider_scale_low"] or "75%")
    _G[scaleSlider:GetName().."High"]:SetText(NA.L and NA.L["slider_scale_high"] or "125%")
    _G[scaleSlider:GetName().."Text"]:SetText("")

    local scaleValue = tabContent1:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    scaleValue:SetPoint("TOP", scaleSlider, "BOTTOM", 0, 2)
    scaleValue:SetText(string.format(currentText .. " %.0f%%", self.db.scale * 100))

    scaleSlider:SetScript("OnValueChanged", function(_, value)
        NA.db.scale = value
        if NA.mainFrame then NA.mainFrame:SetScale(value) end
        scaleValue:SetText(string.format(currentText .. " %.0f%%", value * 100))
    end)

    ---- 4. Show Spec Icon
    local specIconCheck = CreateFrame("CheckButton", "NASpecIconCheck"..pid, tabContent1, "InterfaceOptionsCheckButtonTemplate")
    specIconCheck:SetPoint("TOPLEFT", 14, -210)
    specIconCheck:SetSize(32, 32)
    _G[specIconCheck:GetName().."Text"]:SetText(NA.L and NA.L["Show specialization icon"] or "Show specialization icon")
    specIconCheck:SetChecked(NA.db.showSpecIcon)
    specIconCheck:SetScript("OnClick", function(btn)
        NA.db.showSpecIcon = btn:GetChecked()
        if NA.UpdateDisplay then NA:UpdateDisplay() end
    end)

    -- ════════════════════════════════════════════════════════════
    -- TAB 2: Notifications
    -- ════════════════════════════════════════════════════════════

    ---- Sound section header
    local soundSectionLabel = tabContent2:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    soundSectionLabel:SetPoint("TOPLEFT", 20, -10)
    soundSectionLabel:SetText("|cFFFFD100" .. (NA.L and NA.L["Sound"] or "Sound") .. "|r")

    -- Sound parent checkbox
    local soundCheck = CreateFrame("CheckButton", "NASoundCheck"..pid, tabContent2, "InterfaceOptionsCheckButtonTemplate")
    soundCheck:SetPoint("TOPLEFT", 14, -25)
    soundCheck:SetSize(32, 32)
    _G[soundCheck:GetName().."Text"]:SetText(NA.L and NA.L["Play sound when new applicants arrive"] or "Play sound when new applicants arrive")
    soundCheck:SetChecked(self.db.soundEnabled)

    -- Sound picker - custom scroll list
    local ITEM_H     = 22
    local VISIBLE    = 8   -- number of visible items at once

    local soundSelectLabel = tabContent2:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    soundSelectLabel:SetPoint("TOPLEFT", 36, -55)
    soundSelectLabel:SetText(NA.L and NA.L["Sound:"] or "Sound:")

    -- Selection display button (acts like a dropdown header)
    local soundPickerBtn = CreateFrame("Button", nil, tabContent2, "BackdropTemplate")
    soundPickerBtn:SetSize(210, 22)
    soundPickerBtn:SetPoint("TOPLEFT", 36, -70)
    soundPickerBtn:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 8, edgeSize = 12,
        insets = { left = 3, right = 3, top = 3, bottom = 3 }
    })
    soundPickerBtn:SetBackdropColor(0.08, 0.08, 0.08, 1)

    local pickerText = soundPickerBtn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    pickerText:SetPoint("LEFT", soundPickerBtn, "LEFT", 6, 0)
    pickerText:SetPoint("RIGHT", soundPickerBtn, "RIGHT", -18, 0)
    pickerText:SetJustifyH("LEFT")
    pickerText:SetText(NA.db.selectedSound or "xylophone")

    local arrowTex = soundPickerBtn:CreateTexture(nil, "OVERLAY")
    arrowTex:SetSize(16, 16)
    arrowTex:SetPoint("RIGHT", soundPickerBtn, "RIGHT", -2, 0)
    arrowTex:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")

    -- Popup list frame
    local SCROLLBAR_W = 16
    local LIST_W      = 210
    local POPUP_W     = LIST_W + SCROLLBAR_W + 6  -- list + scrollbar + margin
    local SEARCH_H    = 24

    local popup = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
    popup:SetSize(POPUP_W, SEARCH_H + ITEM_H * VISIBLE)
    popup:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 8, edgeSize = 12,
        insets = { left = 3, right = 3, top = 3, bottom = 3 }
    })
    popup:SetBackdropColor(0.06, 0.06, 0.06, 1)
    popup:SetFrameStrata("TOOLTIP")
    popup:SetFrameLevel(200)
    popup:Hide()

    -- Search box
    local searchBox = CreateFrame("EditBox", nil, popup, "BackdropTemplate")
    searchBox:SetSize(POPUP_W - 8, SEARCH_H - 4)
    searchBox:SetPoint("TOPLEFT", popup, "TOPLEFT", 4, -4)
    searchBox:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 8, edgeSize = 10,
        insets = { left = 3, right = 3, top = 3, bottom = 3 }
    })
    searchBox:SetBackdropColor(0.12, 0.12, 0.12, 1)
    searchBox:SetFontObject("GameFontHighlightSmall")
    searchBox:SetTextInsets(6, 6, 2, 2)
    searchBox:SetAutoFocus(false)
    searchBox:SetMaxLetters(64)

    -- Placeholder text
    local searchPlaceholder = searchBox:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    searchPlaceholder:SetPoint("LEFT", searchBox, "LEFT", 6, 0)
    searchPlaceholder:SetText(NA.L and NA.L["Search..."] or "Search...")

    -- OnTextChanged / OnEscapePressed connected below after scrollBar, RefreshList, closePopupOnClick are declared

    -- Close popup when clicking outside
    popup:SetScript("OnHide", function()
        popup.scrollOffset = 0
        searchBox:SetText("")
    end)

    -- Click detector: covers full screen at level below popup so scrollbar clicks pass through
    local closePopupOnClick = CreateFrame("Frame", nil, UIParent)
    closePopupOnClick:SetAllPoints()
    closePopupOnClick:SetFrameStrata("TOOLTIP")
    closePopupOnClick:SetFrameLevel(190)  -- below popup (200)
    closePopupOnClick:EnableMouse(true)
    closePopupOnClick:Hide()
    closePopupOnClick:SetScript("OnMouseDown", function(_, btn)
        -- clicks inside the popup are handled by the popup itself
        if btn == "LeftButton" or btn == "RightButton" then
            popup:Hide()
            closePopupOnClick:Hide()
        end
    end)

    -- List item button pool (below search box, limited to LIST_W to avoid overlapping scrollbar)
    local itemButtons = {}
    for i = 1, VISIBLE do
        local btn = CreateFrame("Button", nil, popup)
        btn:SetSize(LIST_W - 6, ITEM_H)
        btn:SetPoint("TOPLEFT", popup, "TOPLEFT", 3, -(SEARCH_H + 3) - (i - 1) * ITEM_H)

        local hl = btn:CreateTexture(nil, "HIGHLIGHT")
        hl:SetAllPoints()
        hl:SetColorTexture(1, 0.82, 0, 0.2)

        local sel = btn:CreateTexture(nil, "BACKGROUND")
        sel:SetAllPoints()
        sel:SetColorTexture(1, 0.82, 0, 0.12)
        sel:Hide()
        btn.selTex = sel

        local label = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        label:SetPoint("LEFT", btn, "LEFT", 6, 0)
        label:SetPoint("RIGHT", btn, "RIGHT", -4, 0)
        label:SetJustifyH("LEFT")
        btn.label = label

        itemButtons[i] = btn
    end

    -- Scrollbar (below search box, positioned to the right of item buttons)
    local scrollBar = CreateFrame("Slider", nil, popup)
    scrollBar:SetWidth(SCROLLBAR_W)
    scrollBar:SetPoint("TOPLEFT",    popup, "TOPLEFT", LIST_W + 2, -(SEARCH_H + 16))
    scrollBar:SetPoint("BOTTOMLEFT", popup, "BOTTOMLEFT", LIST_W + 2, 16)
    scrollBar:SetMinMaxValues(0, 0)
    scrollBar:SetValueStep(1)
    scrollBar:SetObeyStepOnDrag(true)
    scrollBar:SetValue(0)
    scrollBar:SetOrientation("VERTICAL")
    local thumb = scrollBar:CreateTexture(nil, "ARTWORK")
    thumb:SetTexture("Interface\\Buttons\\UI-ScrollBar-Knob")
    thumb:SetSize(SCROLLBAR_W - 2, 24)
    scrollBar:SetThumbTexture(thumb)
    popup.scrollOffset = 0
    popup:EnableMouseWheel(true)

    local function RefreshList()
        -- Filter by search query
        local query = searchBox:GetText():lower()
        local fullList = NA:GetSoundList()
        local list = {}
        for _, entry in ipairs(fullList) do
            if query == "" or entry.label:lower():find(query, 1, true) then
                table.insert(list, entry)
            end
        end

        local maxOff = math.max(0, #list - VISIBLE)
        scrollBar:SetMinMaxValues(0, maxOff)
        local off = math.min(popup.scrollOffset, maxOff)
        popup.scrollOffset = off

        for i = 1, VISIBLE do
            local entry = list[off + i]
            local btn   = itemButtons[i]
            if entry then
                btn:Show()
                btn.label:SetText(entry.label)
                btn.entryId = entry.id
                if entry.id == NA.db.selectedSound then
                    btn.selTex:Show()
                    btn.label:SetTextColor(1, 0.82, 0, 1)
                else
                    btn.selTex:Hide()
                    btn.label:SetTextColor(1, 1, 1, 1)
                end
                btn:SetScript("OnClick", function()
                    NA.db.selectedSound = entry.id
                    pickerText:SetText(entry.label)
                    NA:PlayRingSound()
                    popup:Hide()
                    closePopupOnClick:Hide()
                    RefreshList()
                end)
            else
                btn:Hide()
            end
        end
    end

    -- Search box callbacks (connected after scrollBar, RefreshList, closePopupOnClick are declared)
    searchBox:SetScript("OnTextChanged", function(box)
        searchPlaceholder:SetShown(box:GetText() == "")
        popup.scrollOffset = 0
        scrollBar:SetValue(0)
        RefreshList()
    end)
    searchBox:SetScript("OnEscapePressed", function()
        popup:Hide()
        closePopupOnClick:Hide()
    end)

    scrollBar:SetScript("OnValueChanged", function(_, val)
        popup.scrollOffset = math.floor(val + 0.5)
        RefreshList()
    end)

    popup:SetScript("OnMouseWheel", function(_, delta)
        local list  = NA:GetSoundList()
        local maxOff = math.max(0, #list - VISIBLE)
        local newOff = math.max(0, math.min(maxOff, popup.scrollOffset - delta))
        popup.scrollOffset = newOff
        scrollBar:SetValue(newOff)
    end)

    soundPickerBtn:SetScript("OnClick", function()
        if popup:IsShown() then
            popup:Hide()
            closePopupOnClick:Hide()
        else
            -- Position popup directly below the button
            popup:ClearAllPoints()
            popup:SetPoint("TOPLEFT", soundPickerBtn, "BOTTOMLEFT", 0, -2)
            -- Scroll to currently selected item
            local list = NA:GetSoundList()
            local selIdx = 1
            for i, e in ipairs(list) do
                if e.id == NA.db.selectedSound then selIdx = i break end
            end
            local maxOff = math.max(0, #list - VISIBLE)
            popup.scrollOffset = math.min(maxOff, math.max(0, selIdx - 1))
            scrollBar:SetValue(popup.scrollOffset)
            RefreshList()
            popup:Show()
            closePopupOnClick:Show()
            searchBox:SetFocus()
        end
    end)

    -- Initial picker text: use label if LSM available, otherwise raw id
    do
        local list = NA:GetSoundList()
        local selId = NA.db.selectedSound or "xylophone"
        for _, e in ipairs(list) do
            if e.id == selId then
                pickerText:SetText(e.label)
                break
            end
        end
    end

    -- Custom sound file
    local customPathLabel = tabContent2:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    customPathLabel:SetPoint("TOPLEFT", 36, -99)
    customPathLabel:SetText(NA.L and NA.L["Custom Sound File"] or "Custom Sound File")

    local customPathHint = tabContent2:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    customPathHint:SetPoint("TOPLEFT", 36, -110)
    customPathHint:SetText("NewApplicants\\Sounds\\mysound.mp3")

    -- Extract filename from saved full path
    local savedPath = NA.db.customSoundPath or ""
    local savedFileName = savedPath:match("([^\\]+)$") or ""

    local customPathBox = CreateFrame("EditBox", nil, tabContent2, "BackdropTemplate")
    customPathBox:SetSize(190, 20)
    customPathBox:SetPoint("TOPLEFT", 36, -124)
    customPathBox:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 8, edgeSize = 10,
        insets = { left = 3, right = 3, top = 3, bottom = 3 }
    })
    customPathBox:SetBackdropColor(0.08, 0.08, 0.08, 1)
    customPathBox:SetFontObject("GameFontHighlightSmall")
    customPathBox:SetTextInsets(4, 4, 2, 2)
    customPathBox:SetAutoFocus(false)
    customPathBox:SetMaxLetters(64)
    customPathBox:SetText(savedFileName)

    local customPathPlaceholder = customPathBox:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    customPathPlaceholder:SetPoint("LEFT", customPathBox, "LEFT", 4, 0)
    customPathPlaceholder:SetText("mysound.mp3")
    customPathPlaceholder:SetShown(savedFileName == "")

    local customPlayBtn = CreateFrame("Button", nil, tabContent2, "BackdropTemplate")
    customPlayBtn:SetSize(24, 20)
    customPlayBtn:SetPoint("LEFT", customPathBox, "RIGHT", 2, 0)
    customPlayBtn:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 8, edgeSize = 10,
        insets = { left = 3, right = 3, top = 3, bottom = 3 }
    })
    customPlayBtn:SetBackdropColor(0.15, 0.15, 0.15, 1)
    local customPlayTex = customPlayBtn:CreateTexture(nil, "ARTWORK")
    customPlayTex:SetSize(14, 14)
    customPlayTex:SetPoint("CENTER")
    customPlayTex:SetAtlas("audiooptions-musiclist-play-button")
    customPlayBtn:SetScript("OnEnter", function(btn)
        GameTooltip:SetOwner(btn, "ANCHOR_RIGHT")
        GameTooltip:SetText(NA.L and NA.L["Test"] or "Test", 1, 1, 1)
        GameTooltip:Show()
    end)
    customPlayBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)

    local function ApplyCustomPath(path)
        path = path:match("^%s*(.-)%s*$")  -- trim
        if path ~= "" then
            path = "Interface\\AddOns\\NewApplicants\\Sounds\\" .. path
        end
        NA.db.customSoundPath = path
        customPathPlaceholder:SetShown(path == "")
        if path ~= "" then
            -- Show picker as inactive
            pickerText:SetText("|cFF888888" .. (NA.L and NA.L["(Custom)"] or "(Custom)") .. "|r")
        else
            -- Restore picker text
            local list = NA:GetSoundList()
            local selId = NA.db.selectedSound or "xylophone"
            for _, e in ipairs(list) do
                if e.id == selId then pickerText:SetText(e.label) break end
            end
        end
    end

    customPathBox:SetScript("OnTextChanged", function(box)
        customPathPlaceholder:SetShown(box:GetText() == "")
    end)
    customPathBox:SetScript("OnEnterPressed", function(box)
        ApplyCustomPath(box:GetText())
        box:ClearFocus()
        NA:PlayRingSound()
    end)
    customPathBox:SetScript("OnEscapePressed", function(box)
        local p = NA.db.customSoundPath or ""
        box:SetText(p:match("([^\\]+)$") or "")
        box:ClearFocus()
    end)

    customPlayBtn:SetScript("OnClick", function()
        ApplyCustomPath(customPathBox:GetText())
        NA:PlayRingSound()
    end)

    -- Initial picker text: show (Custom) if custom path is set
    if (NA.db.customSoundPath or "") ~= "" then
        pickerText:SetText("|cFF888888" .. (NA.L and NA.L["(Custom)"] or "(Custom)") .. "|r")
    end

    -- Sound radios
    local soundRadioAlways = CreateFrame("CheckButton", "NASoundRadioAlways"..pid, tabContent2, "UIRadioButtonTemplate")
    soundRadioAlways:SetPoint("TOPLEFT", 36, -149)
    _G[soundRadioAlways:GetName().."Text"]:SetText(NA.L and NA.L["Always"] or "Always")
    soundRadioAlways:SetChecked(not self.db.soundOnlyActive)

    local soundRadioOnly = CreateFrame("CheckButton", "NASoundRadioOnly"..pid, tabContent2, "UIRadioButtonTemplate")
    soundRadioOnly:SetPoint("TOPLEFT", 36, -167)
    _G[soundRadioOnly:GetName().."Text"]:SetText(NA.L and NA.L["Only when I am recruiting"] or "Only when I am recruiting")
    soundRadioOnly:SetChecked(self.db.soundOnlyActive)

    soundRadioAlways:SetScript("OnClick", function()
        NA.db.soundOnlyActive = false
        soundRadioAlways:SetChecked(true)
        soundRadioOnly:SetChecked(false)
    end)
    soundRadioOnly:SetScript("OnClick", function()
        NA.db.soundOnlyActive = true
        soundRadioAlways:SetChecked(false)
        soundRadioOnly:SetChecked(true)
    end)

    -- Sound exclude raid
    local soundExcludeRaid = CreateFrame("CheckButton", "NASoundExcludeRaid"..pid, tabContent2, "UICheckButtonTemplate")
    soundExcludeRaid:SetSize(20, 20)
    soundExcludeRaid:SetPoint("TOPLEFT", 39, -187)
    soundExcludeRaid:SetChecked(not self.db.playOnRaid)
    local soundExcludeRaidText = soundExcludeRaid:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    soundExcludeRaidText:SetPoint("LEFT", soundExcludeRaid, "RIGHT", 3, 0)
    soundExcludeRaidText:SetText(NA.L and NA.L["Exclude raid listings"] or "Exclude raid listings")
    soundExcludeRaid:SetScript("OnClick", function(btn)
        NA.db.playOnRaid = not btn:GetChecked()
    end)

    local function UpdateSoundChildren()
        local enabled = NA.db.soundEnabled
        if enabled then
            soundRadioAlways:Enable()
            soundRadioOnly:Enable()
            soundExcludeRaid:Enable()
            soundPickerBtn:Enable()
            customPathBox:Enable()
            customPlayBtn:Enable()
            soundSelectLabel:SetAlpha(1.0)
            pickerText:SetAlpha(1.0)
            customPathLabel:SetAlpha(1.0)
            customPathBox:SetAlpha(1.0)
            customPlayBtn:SetAlpha(1.0)
        else
            soundRadioAlways:Disable()
            soundRadioOnly:Disable()
            soundExcludeRaid:Disable()
            soundPickerBtn:Disable()
            customPathBox:Disable()
            customPlayBtn:Disable()
            popup:Hide()
            closePopupOnClick:Hide()
            soundSelectLabel:SetAlpha(0.4)
            pickerText:SetAlpha(0.4)
            customPathLabel:SetAlpha(0.4)
            customPathBox:SetAlpha(0.4)
            customPlayBtn:SetAlpha(0.4)
        end
    end
    UpdateSoundChildren()

    soundCheck:SetScript("OnClick", function(btn)
        NA.db.soundEnabled = btn:GetChecked()
        if btn:GetChecked() then NA:PlayRingSound() end
        UpdateSoundChildren()
    end)

    ---- Divider
    local divider = tabContent2:CreateTexture(nil, "ARTWORK")
    divider:SetHeight(1)
    divider:SetPoint("TOPLEFT", 14, -209)
    divider:SetPoint("TOPRIGHT", -14, -209)
    divider:SetColorTexture(1, 1, 1, 0.15)

    ---- Cards section header
    local cardSectionLabel = tabContent2:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    cardSectionLabel:SetPoint("TOPLEFT", 20, -217)
    cardSectionLabel:SetText("|cFFFFD100" .. (NA.L and NA.L["Cards"] or "Cards") .. "|r")

    -- Cards parent checkbox
    local showCardsCheck = CreateFrame("CheckButton", "NAShowCardsCheck"..pid, tabContent2, "InterfaceOptionsCheckButtonTemplate")
    showCardsCheck:SetPoint("TOPLEFT", 14, -232)
    showCardsCheck:SetSize(32, 32)
    _G[showCardsCheck:GetName().."Text"]:SetText(NA.L and NA.L["Show cards when new applicants arrive"] or "Show cards when new applicants arrive")
    showCardsCheck:SetChecked(self.db.showCards)

    -- Cards radios
    local cardRadioAlways = CreateFrame("CheckButton", "NACardRadioAlways"..pid, tabContent2, "UIRadioButtonTemplate")
    cardRadioAlways:SetPoint("TOPLEFT", 36, -259)
    _G[cardRadioAlways:GetName().."Text"]:SetText(NA.L and NA.L["Always"] or "Always")
    cardRadioAlways:SetChecked(not self.db.cardOnlyActive)

    local cardRadioOnly = CreateFrame("CheckButton", "NACardRadioOnly"..pid, tabContent2, "UIRadioButtonTemplate")
    cardRadioOnly:SetPoint("TOPLEFT", 36, -277)
    _G[cardRadioOnly:GetName().."Text"]:SetText(NA.L and NA.L["Only when I am recruiting"] or "Only when I am recruiting")
    cardRadioOnly:SetChecked(self.db.cardOnlyActive)

    cardRadioAlways:SetScript("OnClick", function()
        NA.db.cardOnlyActive = false
        cardRadioAlways:SetChecked(true)
        cardRadioOnly:SetChecked(false)
        if NA.UpdateDisplay then NA:UpdateDisplay() end
    end)
    cardRadioOnly:SetScript("OnClick", function()
        NA.db.cardOnlyActive = true
        cardRadioAlways:SetChecked(false)
        cardRadioOnly:SetChecked(true)
        if NA.UpdateDisplay then NA:UpdateDisplay() end
    end)

    -- Cards exclude raid
    local cardExcludeRaid = CreateFrame("CheckButton", "NACardExcludeRaid"..pid, tabContent2, "UICheckButtonTemplate")
    cardExcludeRaid:SetSize(20, 20)
    cardExcludeRaid:SetPoint("TOPLEFT", 39, -297)
    cardExcludeRaid:SetChecked(not self.db.showCardsOnRaid)
    local cardExcludeRaidText = cardExcludeRaid:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    cardExcludeRaidText:SetPoint("LEFT", cardExcludeRaid, "RIGHT", 3, 0)
    cardExcludeRaidText:SetText(NA.L and NA.L["Exclude raid listings"] or "Exclude raid listings")
    cardExcludeRaid:SetScript("OnClick", function(btn)
        NA.db.showCardsOnRaid = not btn:GetChecked()
        if NA.UpdateDisplay then NA:UpdateDisplay() end
    end)

    local function UpdateCardChildren()
        if NA.db.showCards then
            cardRadioAlways:Enable()
            cardRadioOnly:Enable()
            cardExcludeRaid:Enable()
        else
            cardRadioAlways:Disable()
            cardRadioOnly:Disable()
            cardExcludeRaid:Disable()
        end
    end
    UpdateCardChildren()

    showCardsCheck:SetScript("OnClick", function(btn)
        NA.db.showCards = btn:GetChecked()
        UpdateCardChildren()
        if NA.UpdateDisplay then NA:UpdateDisplay() end
    end)

    -- Default to Tab 1
    SwitchTab(1)

    self.settingsFrame = f
    f:Show()
end

-- Update Display
function NA:UpdateDisplay()
    if not self.mainFrame then self:CreateMainFrame() end
    local content = self.mainFrame.content
    
    if content.applicantFrames then
        for _, frame in ipairs(content.applicantFrames) do
            frame:Hide()
            frame:ClearAllPoints()
            frame:SetParent(nil)
        end
    end
    content.applicantFrames = {}
    
    if #self.applicantDataList == 0 then self.mainFrame:Hide() return end

    if not self.db.showCards then
        self.mainFrame:Hide()
        return
    end

    if not self.db.showCardsOnRaid and self.isRaidListing then
        self.mainFrame:Hide()
        return
    end

    if self.db.cardOnlyActive and not self.hasActiveListing then
        self.mainFrame:Hide()
        return
    end
    
    self.mainFrame:Show()
    local maxDisplay = self.db.maxDisplay
    local totalCount = #self.applicantDataList
    local yOffset = 0

    local CARD_STEP_NORMAL = 80
    local CARD_STEP_GROUP  = 120

    -- Fix mainFrame height based on maxDisplay (header 45px + 80px per card + bottom margin 15px)
    local frameHeight = 45 + (maxDisplay * CARD_STEP_NORMAL) + 15
    self.mainFrame:SetHeight(frameHeight)

    for i = 1, totalCount do
        local data = self.applicantDataList[i]
        local isGroup = data.groupMembers and #data.groupMembers > 0
        local frame = self:CreateApplicantFrame(data)
        frame:SetPoint("TOPLEFT", content, "TOPLEFT", 0, yOffset)
        if NA.pendingRemoval and (NA.pendingRemoval[data.applicantID] or NA.pendingRemoval[data]) then
            if frame.dimOverlay then frame.dimOverlay:SetAlpha(0.8) end
        end
        frame:Show()
        table.insert(content.applicantFrames, frame)
        yOffset = yOffset - (isGroup and CARD_STEP_GROUP or CARD_STEP_NORMAL)
    end
    content:SetHeight(math.abs(yOffset) + 10)

    ---- Scroll show / hide
    local scrollFrame = self.mainFrame.scroll
    if totalCount <= maxDisplay then
        scrollFrame.ScrollBar:Hide()
    else
        scrollFrame.ScrollBar:Show()
    end
end

-- Create Applicant Frame
function NA:CreateApplicantFrame(data)
    local isGroup = data.groupMembers and #data.groupMembers > 0
    local cardHeight = isGroup and 115 or 75

    local f = CreateFrame("Button", nil, self.mainFrame.content)
    f:SetSize(370, cardHeight)
    f:SetAlpha(NA.db.backgroundAlpha)
    
    ---- Class frame
    local classTexture = self:GetClassFrameTexture(data.class)
    if classTexture then
        local bg = f:CreateTexture(nil, "BACKGROUND")
        bg:SetAllPoints()
        bg:SetTexture(classTexture)
        f.bg = bg
    end
    
    local indentX = 25 
    
    -- Anchor reference for name text (changes based on showSpecIcon)
    local iconAnchor

    if NA.db.showSpecIcon then
        ---- spec icon (rounded, 20x20 + gold border)
        local specIconHolder = CreateFrame("Frame", nil, f)
        specIconHolder:SetSize(20, 20)
        specIconHolder:SetPoint("TOPLEFT", indentX, -8)

        local specIconMask = specIconHolder:CreateMaskTexture()
        specIconMask:SetAllPoints()
        specIconMask:SetTexture("Interface\\CharacterFrame\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")

        -- spec icon with mask
        local specIcon = specIconHolder:CreateTexture(nil, "ARTWORK")
        specIcon:SetPoint("TOPLEFT", 1, -1)
        specIcon:SetPoint("BOTTOMRIGHT", -1, 1)
        specIcon:AddMaskTexture(specIconMask)

        -- Use custom high-res spec icon texture if available, otherwise fall back to role icon
        local specTexCoord = (data.specID and data.specID > 0) and NA:GetSpecTexCoord(data.specID) or nil
        if specTexCoord then
            specIcon:SetTexture("Interface\\AddOns\\NewApplicants\\Textures\\New_Applicaants_classicon.png")
            specIcon:SetTexCoord(specTexCoord[1], specTexCoord[2], specTexCoord[3], specTexCoord[4])
        else
            if data.tank then
                specIcon:SetAtlas("UI-LFG-RoleIcon-Tank")
            elseif data.healer then
                specIcon:SetAtlas("UI-LFG-RoleIcon-Healer")
            else
                specIcon:SetAtlas("UI-LFG-RoleIcon-DPS")
            end
        end

        -- gold border frame (above specIconHolder, no clipping)
        local specBorderFrame = CreateFrame("Frame", nil, f)
        specBorderFrame:SetSize(21, 21)
        specBorderFrame:SetPoint("CENTER", specIconHolder, "CENTER", 0, 0)
        specBorderFrame:SetFrameLevel(specIconHolder:GetFrameLevel() + 5)
        local specIconBorder = specBorderFrame:CreateTexture(nil, "OVERLAY")
        specIconBorder:SetAllPoints()
        specIconBorder:SetTexture("Interface\\AddOns\\NewApplicants\\Textures\\yellow-circle-mask2.png")

        -- role icon overlapping spec icon, highest frame level
        local roleIconFrame = CreateFrame("Frame", nil, f)
        roleIconFrame:SetSize(15, 15)
        roleIconFrame:SetPoint("BOTTOMRIGHT", specIconHolder, "BOTTOMRIGHT", 4, -4)
        roleIconFrame:SetFrameLevel(specIconHolder:GetFrameLevel() + 10)

        local roleIconMask = roleIconFrame:CreateMaskTexture()
        roleIconMask:SetSize(13, 13)
        roleIconMask:SetPoint("CENTER", roleIconFrame, "CENTER", 0, 0)
        roleIconMask:SetTexture("Interface\\CharacterFrame\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")

        local roleIconBg = roleIconFrame:CreateTexture(nil, "BACKGROUND")
        roleIconBg:SetAllPoints()
        roleIconBg:SetColorTexture(0, 0, 0, 1)
        roleIconBg:AddMaskTexture(roleIconMask)

        local roleIcon = roleIconFrame:CreateTexture(nil, "OVERLAY")
        roleIcon:SetAllPoints()
        if data.tank then
            roleIcon:SetAtlas("UI-LFG-RoleIcon-Tank")
        elseif data.healer then
            roleIcon:SetAtlas("UI-LFG-RoleIcon-Healer")
        else
            roleIcon:SetAtlas("UI-LFG-RoleIcon-DPS")
        end

        iconAnchor = specIconHolder
    else
        ---- role icon only (no spec icon)
        local roleIconFrame = CreateFrame("Frame", nil, f)
        roleIconFrame:SetSize(20, 20)
        roleIconFrame:SetPoint("TOPLEFT", indentX, -8)
        roleIconFrame:SetFrameLevel(f:GetFrameLevel() + 5)

        local roleIcon = roleIconFrame:CreateTexture(nil, "OVERLAY")
        roleIcon:SetAllPoints()
        if data.tank then
            roleIcon:SetAtlas("UI-LFG-RoleIcon-Tank")
        elseif data.healer then
            roleIcon:SetAtlas("UI-LFG-RoleIcon-Healer")
        else
            roleIcon:SetAtlas("UI-LFG-RoleIcon-DPS")
        end

        iconAnchor = roleIconFrame
    end

    ---- character name
	local nameText = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
	nameText:SetPoint("LEFT", iconAnchor, "RIGHT", 4, 0)
	nameText:SetWidth(120)
	nameText:SetWordWrap(false)
	nameText:SetNonSpaceWrap(false)
	nameText:SetJustifyH("LEFT")
    
    local classColor = C_ClassColor.GetClassColor(data.class)
    if classColor then
        nameText:SetTextColor(classColor.r, classColor.g, classColor.b)
    end
    
    nameText:SetText(data.charName .. "-" .. data.serverName)

    -- Group member icon+name stack at the bottom of the card (shown when numMembers > 1)
    if isGroup then
        local ICON_SIZE   = 20
        local MAX_SHOWN   = 3
        local ITEM_GAP    = 10
        -- 가용 너비: 카드(370) - 시작x - 우측여백(총N명 약 55px + 12px)
        local TOTAL_W     = 370 - indentX - 67
        local MAX_NAME_W  = (TOTAL_W - MAX_SHOWN * (ICON_SIZE + 4 + ITEM_GAP)) / MAX_SHOWN
        local members     = data.groupMembers
        local showCount   = math.min(MAX_SHOWN, #members)
        local xCursor     = indentX

        -- Divider
        local dividerFrame = CreateFrame("Frame", nil, f)
        dividerFrame:SetHeight(1)
        dividerFrame:SetPoint("BOTTOMLEFT",  f, "BOTTOMLEFT",  indentX + 5, 38)
        dividerFrame:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -12,         38)
        dividerFrame:SetFrameLevel(f:GetFrameLevel() + 1)
        local divider = dividerFrame:CreateTexture(nil, "ARTWORK")
        divider:SetAllPoints()
        divider:SetColorTexture(1, 1, 1, 0.3)

        for idx = 1, showCount do
            local member = members[idx]

            -- Truncate name to fit MAX_NAME_W pixels
            local displayName = member.charName
            local measureFS = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
            measureFS:SetFont(measureFS:GetFont(), 11)
            measureFS:SetText(displayName)
            if measureFS:GetStringWidth() > MAX_NAME_W then
                while measureFS:GetStringWidth() > MAX_NAME_W and #displayName > 1 do
                    displayName = displayName:sub(1, -2)
                    measureFS:SetText(displayName .. "…")
                end
                displayName = displayName .. "…"
            end
            measureFS:Hide()

            -- Circular icon
            local iconHolder = CreateFrame("Frame", nil, f)
            iconHolder:SetSize(ICON_SIZE, ICON_SIZE)
            iconHolder:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", xCursor, 10)
            iconHolder:SetFrameLevel(f:GetFrameLevel() + 3)

            local mask = iconHolder:CreateMaskTexture()
            mask:SetAllPoints()
            mask:SetTexture("Interface\\CharacterFrame\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")

            local icon = iconHolder:CreateTexture(nil, "ARTWORK")
            icon:SetPoint("TOPLEFT", 1, -1)
            icon:SetPoint("BOTTOMRIGHT", -1, 1)
            icon:AddMaskTexture(mask)

            local specTexCoord = (NA.db.showSpecIcon and member.specID and member.specID > 0) and NA:GetSpecTexCoord(member.specID) or nil
            if specTexCoord then
                icon:SetTexture("Interface\\AddOns\\NewApplicants\\Textures\\New_Applicaants_classicon.png")
                icon:SetTexCoord(specTexCoord[1], specTexCoord[2], specTexCoord[3], specTexCoord[4])
            elseif member.tank then
                icon:SetAtlas("UI-LFG-RoleIcon-Tank")
            elseif member.healer then
                icon:SetAtlas("UI-LFG-RoleIcon-Healer")
            else
                icon:SetAtlas("UI-LFG-RoleIcon-DPS")
            end

            -- Gold circle border (spec icon only, matches main card 21x21)
            if specTexCoord then
                local borderFrame = CreateFrame("Frame", nil, f)
                borderFrame:SetSize(21, 21)
                borderFrame:SetPoint("CENTER", iconHolder, "CENTER", 0, 0)
                borderFrame:SetFrameLevel(iconHolder:GetFrameLevel() + 2)
                local border = borderFrame:CreateTexture(nil, "OVERLAY")
                border:SetAllPoints()
                border:SetTexture("Interface\\AddOns\\NewApplicants\\Textures\\yellow-circle-mask2.png")
            end

            -- Role badge (bottom-right of spec icon)
            if specTexCoord then
                local roleFrame = CreateFrame("Frame", nil, f)
                roleFrame:SetSize(15, 15)
                roleFrame:SetPoint("BOTTOMRIGHT", iconHolder, "BOTTOMRIGHT", 4, -4)
                roleFrame:SetFrameLevel(iconHolder:GetFrameLevel() + 10)

                local roleMask = roleFrame:CreateMaskTexture()
                roleMask:SetSize(13, 13)
                roleMask:SetPoint("CENTER", roleFrame, "CENTER", 0, 0)
                roleMask:SetTexture("Interface\\CharacterFrame\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")

                local roleBg = roleFrame:CreateTexture(nil, "BACKGROUND")
                roleBg:SetAllPoints()
                roleBg:SetColorTexture(0, 0, 0, 1)
                roleBg:AddMaskTexture(roleMask)

                local roleIcon = roleFrame:CreateTexture(nil, "OVERLAY")
                roleIcon:SetAllPoints()
                if member.tank then
                    roleIcon:SetAtlas("UI-LFG-RoleIcon-Tank")
                elseif member.healer then
                    roleIcon:SetAtlas("UI-LFG-RoleIcon-Healer")
                else
                    roleIcon:SetAtlas("UI-LFG-RoleIcon-DPS")
                end
            end

            -- Name label next to icon
            local nameLabel = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
            nameLabel:SetFont(nameLabel:GetFont(), 11)
            nameLabel:SetPoint("LEFT", iconHolder, "RIGHT", 4, 0)
            nameLabel:SetText(displayName)
            local memberClassColor = C_ClassColor.GetClassColor(member.class)
            if memberClassColor then
                nameLabel:SetTextColor(memberClassColor:GetRGBA())
            else
                nameLabel:SetTextColor(1, 1, 1)
            end

            -- Tooltip on hover (covers icon + name area)
            -- memberIdx: leader = 1, group members start at 2
            local memberIdx = idx + 1
            local tipW = ICON_SIZE + 4 + nameLabel:GetStringWidth()
            local tipBtnParent = CreateFrame("Frame", nil, f)
            tipBtnParent:SetSize(tipW, ICON_SIZE)
            tipBtnParent:SetPoint("LEFT", iconHolder, "LEFT", 0, 0)
            tipBtnParent:SetPoint("BOTTOM", iconHolder, "BOTTOM", 0, 0)
            tipBtnParent.applicantID = data.applicantID

            local tipBtn = CreateFrame("Button", nil, tipBtnParent)
            tipBtn:SetAllPoints()
            tipBtn.memberIdx = memberIdx
            tipBtn:SetFrameLevel(iconHolder:GetFrameLevel() + 5)
            if data.applicantID and data.applicantID > 0 then
                tipBtn:SetScript("OnEnter", function(btn)
                    LFGListApplicantMember_OnEnter(btn)
                    if RaiderIO and RaiderIO.ShowProfile then
                        local fullName = select(1, C_LFGList.GetApplicantMemberInfo(data.applicantID, memberIdx))
                        if fullName then
                            RaiderIO.ShowProfile(GameTooltip, fullName)
                        end
                    end
                    GameTooltip:Show()
                end)
                tipBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
            else
                -- fallback for test data (applicantID = 0)
                tipBtn:SetScript("OnEnter", function(btn)
                    GameTooltip:SetOwner(btn, "ANCHOR_TOP")
                    local clr = C_ClassColor.GetClassColor(member.class)
                    local nameStr = clr and ("|c" .. clr:GenerateHexColorMarkup() .. member.charName .. "|r") or member.charName
                    GameTooltip:SetText(nameStr)
                    local role = member.tank and "Tank" or (member.healer and "Healer" or "DPS")
                    GameTooltip:AddLine(member.class:sub(1,1) .. member.class:sub(2):lower() .. "  |cFFFFFFFF" .. role .. "|r", 0.8, 0.8, 0.8)
                    GameTooltip:AddLine("ILV: |cFFFFFFFF" .. math.floor(member.itemLevel) .. "|r", 0.8, 0.8, 0.8)
                    GameTooltip:Show()
                end)
                tipBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
            end

            xCursor = xCursor + ICON_SIZE + 4 + nameLabel:GetStringWidth() + ITEM_GAP
        end

        -- Total member count (leader + group members)
        local totalText = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        totalText:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -12, 10)
        totalText:SetFont(totalText:GetFont(), 14)
        totalText:SetJustifyH("RIGHT")
        totalText:SetTextColor(0.7, 0.7, 0.7)
        local locale = GetLocale()
        if locale == "koKR" then
            totalText:SetText("총 " .. data.numMembers .. "명")
        else
            totalText:SetText(data.numMembers .. " members")
        end
    end

    ---- Second row position
    local secondRowY = -43 

    ---- faction icon
    local factionIcon = f:CreateTexture(nil, "ARTWORK")
    factionIcon:SetSize(20, 20)
    factionIcon:SetPoint("TOPLEFT", indentX, secondRowY)
    factionIcon:SetAtlas(data.factionGroup == 1 and "QuestPortraitIcon-Alliance" or "QuestPortraitIcon-Horde")

    ---- ILV info
    local ilvlLabel = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    ilvlLabel:SetPoint("BOTTOMLEFT", factionIcon, "BOTTOMRIGHT", 5, 0)
    ilvlLabel:SetTextColor(0.5, 0.5, 0.5)
    ilvlLabel:SetText("ILV")

    local ilvlValue = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightHuge")
    --ilvlValue:SetPoint("BOTTOMLEFT", ilvlLabel, "BOTTOMRIGHT", 5, -2)
    ilvlValue:SetPoint("LEFT", ilvlLabel, "RIGHT", 3, 1) 
    ilvlValue:SetText(math.floor(data.itemLevel))

    ---- seperate bar 1
    local sep1Frame = CreateFrame("Frame", nil, f)
    sep1Frame:SetSize(1, 30)
    sep1Frame:SetPoint("BOTTOMLEFT", factionIcon, "BOTTOMRIGHT", 82, -3)
    sep1Frame:SetFrameLevel(f:GetFrameLevel() + 1)
    local sep1 = sep1Frame:CreateTexture(nil, "ARTWORK")
    sep1:SetAllPoints()
    sep1:SetColorTexture(1, 1, 1, 0.3)

    ---- rioRoles icon (max 2)
    if data.rioRoles and #data.rioRoles > 0 then
        local count = math.min(2, #data.rioRoles)
        local yOffsets = count == 1 and { 0 } or { 8, -8 }
        for idx = 1, count do
            local rioRoleIcon = f:CreateTexture(nil, "ARTWORK")
            rioRoleIcon:SetSize(16, 16)
            rioRoleIcon:SetPoint("CENTER", sep1Frame, "CENTER", 13, yOffsets[idx])
            rioRoleIcon:SetAtlas(self:GetRoleAtlas(data.rioRoles[idx].spec, data.rioRoles[idx].role))
        end
    end

    
    ---- SCORE section
	local scoreLabel = f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    scoreLabel:SetPoint("BOTTOM", sep1, "TOPRIGHT", 50, -5)
    scoreLabel:SetText("SCORE")
    scoreLabel:SetTextColor(1, 0.8, 0)

	local scoreNum = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightHuge")
    scoreNum:SetPoint("TOP", scoreLabel, "BOTTOM", 0, -7)
    scoreNum:SetFont(scoreNum:GetFont(), 18, "OUTLINE")

    if data.rioScore and data.rioScore > 0 then
        local r, g, b = self:GetScoreColor(data.rioScore)
        scoreNum:SetTextColor(r, g, b)
        scoreNum:SetText(data.rioScore)
    else
        scoreNum:SetTextColor(0.5, 0.5, 0.5)
        local noDataText = self.L and self.L["No data"] or "No data"
        scoreNum:SetText(noDataText)
        scoreNum:SetFont(scoreNum:GetFont(), 15, "OUTLINE")
    end

    ---- Invisible button over score area for tooltip on hover
    -- Parent frame holds applicantID as LFGListApplicantMember_OnEnter reads from GetParent()
    local scoreBtnParent = CreateFrame("Frame", nil, f)
    scoreBtnParent:SetSize(80, 40)
    scoreBtnParent:SetPoint("CENTER", scoreLabel, "CENTER", 0, -10)
    scoreBtnParent.applicantID = data.applicantID

    local scoreBtn = CreateFrame("Button", nil, scoreBtnParent)
    scoreBtn:SetAllPoints()
    scoreBtn.memberIdx = 1
    if data.applicantID and data.applicantID > 0 then
        scoreBtn:SetScript("OnEnter", function(btn)
            LFGListApplicantMember_OnEnter(btn)
            if RaiderIO and RaiderIO.ShowProfile then
                local fullName = select(1, C_LFGList.GetApplicantMemberInfo(data.applicantID, 1))
                if fullName then
                    RaiderIO.ShowProfile(GameTooltip, fullName)
                end
            end
            GameTooltip:Show()
        end)
        scoreBtn:SetScript("OnLeave", function()
            GameTooltip:Hide()
        end)
    end
    

    ---- seperate bar 2
    local sep2Frame = CreateFrame("Frame", nil, f)
    sep2Frame:SetSize(1, 30)
    sep2Frame:SetPoint("BOTTOMLEFT", factionIcon, "BOTTOMRIGHT", 180, -3)
    sep2Frame:SetFrameLevel(f:GetFrameLevel() + 1)
    local sep2 = sep2Frame:CreateTexture(nil, "ARTWORK")
    sep2:SetAllPoints()
    sep2:SetColorTexture(1, 1, 1, 0.3)

    ---- Max dungeon chests
    if data.rioMaxLevel and data.rioMaxLevel ~= "" then
        local dungeonText = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        dungeonText:SetPoint("BOTTOMLEFT", sep2, "BOTTOMRIGHT", 8, 3)
        dungeonText:SetText(data.rioMaxLevel .. " " .. (data.rioMaxDungeon or ""))
        
        local chestText = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        chestText:SetPoint("BOTTOMLEFT", dungeonText, "TOPLEFT", 0, 0)
        
        local chestPlus = ""
        if data.rioMaxChests and data.rioMaxChests > 0 then
            for _ = 1, data.rioMaxChests do
                chestPlus = chestPlus .. "+"
            end
        end
        chestText:SetText("|cffA335EE" .. chestPlus .. "|r")
    end

    ---- raid progress
    if data.raidProgress then
        local raidText = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
        raidText:SetPoint("TOPRIGHT", -10, -10)
        local diffColor = self:GetDifficultyColor(data.raidProgress.difficulty)
        local raidAbbr = self.RAID_ID_ABBR[data.raidProgress.raidId] or "RAID"
        local raidFont, _, raidFlags = raidText:GetFont()
        if #raidAbbr > 5 then
            raidText:SetFont(raidFont, 11.5, raidFlags)
        else
            raidText:SetFont(raidFont, 13, raidFlags)
        end
        local bossIcon = "|A:UI-HUD-UnitFrame-Target-PortraitOn-Boss-Rare:18:18|a"
        
        raidText:SetText(string.format("|cff808080[%s]|r|cff%02x%02x%02x%s|r %d/%d %s", 
            raidAbbr, diffColor.r*255, diffColor.g*255, diffColor.b*255,
            self:GetDifficultyName(data.raidProgress.difficulty),
            data.raidProgress.progress, data.raidProgress.bossCount, bossIcon))
    end
    
    local isLeader = UnitIsGroupLeader("player")

    if isLeader then
        local declineBtn = CreateFrame("Button", nil, f, "UIPanelCloseButton")
        declineBtn:SetSize(24, 24)
        declineBtn:SetPoint("BOTTOMRIGHT", -5, isGroup and 48 or 8)
        declineBtn:SetScript("OnClick", function() 
            C_LFGList.DeclineApplicant(data.applicantID) 
            NA:UpdateApplicants() 
        end)
        declineBtn:SetScript("OnEnter", function(btn)
            GameTooltip:SetOwner(btn, "ANCHOR_TOP")
            GameTooltip:SetText(NA.L["Decline"] or "Decline", 1, 0.2, 0.2)
            GameTooltip:Show()
        end)
        declineBtn:SetScript("OnLeave", function()
            GameTooltip:Hide()
        end)

        local inviteBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
        inviteBtn:SetSize(50, 22)
        inviteBtn:SetPoint("RIGHT", declineBtn, "LEFT", -2, 0)
        inviteBtn:SetText(NA.L["Invite"] or "Invite")
        inviteBtn:SetNormalFontObject("GameFontNormalSmall")
        inviteBtn:SetScript("OnClick", function() 
            C_LFGList.InviteApplicant(data.applicantID) 
            NA:UpdateApplicants() 
        end)
        inviteBtn:SetScript("OnEnter", function(btn)
            GameTooltip:SetOwner(btn, "ANCHOR_TOP")
            GameTooltip:SetText(NA.L["Invite to party"] or "Invite to party", 0.2, 1, 0.2)
            GameTooltip:Show()
        end)
        inviteBtn:SetScript("OnLeave", function()
            GameTooltip:Hide()
        end)
    end

    return f
end