-- Settings.lua
-- Settings panel and configuration management module

local ADDON_NAME, BFL = ...
local L = BFL.L

-- Import UI constants
local UI = BFL.UI.CONSTANTS

-- Import Settings Components Library
local Components = BFL.SettingsComponents

-- Localization
local L = BFL.L or BFL_L

-- Register the Settings module
local Settings = BFL:RegisterModule("Settings", {})

-- Local references
local settingsFrame = nil
local currentTab = 1
local draggedGroupButton = nil
local groupButtons = {}
local categoryButtons = {} -- Store vertical category buttons
local DELETE_ICON_FALLBACK = "Interface\\AddOns\\BetterFriendlist\\Icons\\trash-2"

-- Helper to get Database module
local function GetDB()
	return BFL and BFL:GetModule("DB")
end

-- Helper to get Groups module
local function GetGroups()
	return BFL and BFL:GetModule("Groups")
end

local function SafeResetVerticalScroll(scrollFrame)
	if not scrollFrame or not scrollFrame.SetVerticalScroll then
		return false
	end
	local ok = pcall(scrollFrame.SetVerticalScroll, scrollFrame, 0)
	return ok
end

local function SetDeleteIconTexture(texture)
	if not texture then
		return
	end
	if BFL.SetTextureOrAtlas then
		BFL.SetTextureOrAtlas(texture, "transmog-icon-remove", DELETE_ICON_FALLBACK, true)
	else
		texture:SetTexture(DELETE_ICON_FALLBACK)
	end
end

local function HideComponentList(container)
	if not container or not container.components then
		return
	end
	for _, component in ipairs(container.components) do
		if component and component.Hide then
			component:Hide()
		end
	end
	container.components = {}
end

function Settings:GetFilterSortTabHost()
	if self.filterSortCustomHost and self.filterSortCustomHost.tab then
		return self.filterSortCustomHost.tab
	end
	local content = settingsFrame and settingsFrame.ContentScrollFrame and settingsFrame.ContentScrollFrame.Content
	return content and content.FilterSortTab or nil
end

function Settings:GetFriendTagsTabHost()
	local content = settingsFrame and settingsFrame.ContentScrollFrame and settingsFrame.ContentScrollFrame.Content
	return content and content.FriendTagsTab or nil
end

function Settings:GetContactMemoryTabHost()
	local content = settingsFrame and settingsFrame.ContentScrollFrame and settingsFrame.ContentScrollFrame.Content
	return content and content.ContactMemoryTab or nil
end

function Settings:RequestFilterSortLayout()
	local host = self.filterSortCustomHost
	if host and host.requestLayout then
		host.requestLayout()
		return
	end
	self:AdjustContentHeight(11)
end

function Settings:RenderFilterSortEditor(parent, opts)
	if not parent then
		return nil
	end
	opts = opts or {}
	parent.BFL_SettingsCenterBuilderSkin = opts.settingsCenter == true and opts.inlineEditor == true
	parent.components = parent.components or {}
	parent.bflBuilderKind = parent.bflBuilderKind or self.filterSortBuilderKind or "filter"
	local focusID = tostring(opts.focusID or "")
	local createKind, createTemplate = focusID:match("^create%-([%a]+):?(.*)$")
	local entryKind, entryID = focusID:match("^(filter):(.+)$")
	if not entryKind then
		entryKind, entryID = focusID:match("^(sorter):(.+)$")
	end

	if createKind then
		createKind = createKind == "sorter" and "sorter" or "filter"
		parent.bflBuilderKind = createKind
		parent.bflFilterSortCreate = {
			kind = createKind,
			templateId = createTemplate ~= "" and createTemplate or "__blank",
		}
	elseif entryKind and entryID and entryID ~= "" then
		parent.bflBuilderKind = entryKind
		parent.bflFilterSortCreate = nil
		if entryKind == "filter" then
			parent.bflSelectedFilterId = entryID
		else
			parent.bflSelectedSorterId = entryID
		end
	elseif opts.focusID == "sorter" or opts.focusID == "create-sorter" then
		parent.bflBuilderKind = "sorter"
	elseif opts.focusID == "filter" or opts.focusID == "create-filter" or opts.focusID == "builder" then
		parent.bflBuilderKind = "filter"
	end
	if (not createKind) and (opts.focusID == "create-sorter" or opts.focusID == "create-filter") then
		parent.bflFilterSortCreate = {
			kind = opts.focusID == "create-sorter" and "sorter" or "filter",
			templateId = "__blank",
		}
	end
	self.filterSortBuilderKind = parent.bflBuilderKind

	self.filterSortCustomHost = {
		tab = parent,
		anchor = opts.anchorFrame or parent,
		inlineEditor = opts.inlineEditor == true,
		settingsCenter = opts.settingsCenter == true,
		viewMode = opts.viewMode or "legacy",
		openEditorPage = opts.openEditorPage,
		openManagerPage = opts.openManagerPage,
		requestLayout = opts.requestLayout,
	}
	self:RefreshFilterSortTab()
	return {
		Release = function()
			HideComponentList(parent)
			parent.BFL_SettingsCenterBuilderSkin = nil
			if self.filterSortCustomHost and self.filterSortCustomHost.tab == parent then
				self.filterSortCustomHost = nil
			end
			self:HideFilterSortEditorPanel()
		end,
		Refresh = function()
			self:RefreshFilterSortTab()
		end,
	}
end

function Settings:GetFilterSortEditorPageHeight()
	return math.max(560, self.filterSortInlinePageHeight or 840)
end

function Settings:GetGlobalSyncTabHost()
	if self.globalSyncCustomHost and self.globalSyncCustomHost.tab then
		return self.globalSyncCustomHost.tab
	end
	local content = settingsFrame and settingsFrame.ContentScrollFrame and settingsFrame.ContentScrollFrame.Content
	return content and content.GlobalSyncTab or nil
end

function Settings:RenderGlobalSyncEditor(parent, opts)
	if not parent then
		return nil
	end
	opts = opts or {}
	if opts.focusID == "restore" then
		self.showDeletedFriends = true
	end
	parent.components = parent.components or {}
	self.globalSyncCustomHost = {
		tab = parent,
		requestLayout = opts.requestLayout,
	}
	self:RefreshGlobalSyncTab()
	return {
		Release = function()
			HideComponentList(parent)
			if self.globalSyncCustomHost and self.globalSyncCustomHost.tab == parent then
				self.globalSyncCustomHost = nil
			end
		end,
		Refresh = function()
			self:RefreshGlobalSyncTab()
		end,
	}
end

local FONT_FLAG_VALUES = { "NONE", "OUTLINE", "THICKOUTLINE", "MONOCHROME", "SLUG" }
local FONT_SETTINGS_CONTROL_WIDTH = 220

local function GetFontFlagLabel(value)
	if value == "NONE" then
		return L.SETTINGS_FONT_OUTLINE_NONE or "None"
	elseif value == "OUTLINE" then
		return L.SETTINGS_FONT_OUTLINE_NORMAL or "Outline"
	elseif value == "THICKOUTLINE" then
		return L.SETTINGS_FONT_OUTLINE_THICK or "Thick Outline"
	elseif value == "MONOCHROME" then
		return L.SETTINGS_FONT_OUTLINE_MONOCHROME or "Monochrome"
	elseif value == "SLUG" then
		return L.SETTINGS_FONT_FLAG_SLUG or "Slug Rendering"
	end
	return tostring(value)
end

local function CreateFontFlagsDropdown(parent, db, dbKey, refreshCallback, controlWidth)
	local function GetFlagSet()
		if BFL.FontManager and BFL.FontManager.GetFontFlagSet then
			return BFL.FontManager:GetFontFlagSet(db:Get(dbKey, ""))
		end
		return {}
	end

	local function GetSelectionText()
		local flagSet = GetFlagSet()
		local parts = {}
		for _, value in ipairs(FONT_FLAG_VALUES) do
			if value ~= "NONE" and flagSet[value] then
				parts[#parts + 1] = GetFontFlagLabel(value)
			end
		end
		if #parts == 0 then
			return GetFontFlagLabel("NONE")
		end
		return table.concat(parts, ", ")
	end

	local flagOptions = {
		labels = {
			GetFontFlagLabel("NONE"),
			GetFontFlagLabel("OUTLINE"),
			GetFontFlagLabel("THICKOUTLINE"),
			GetFontFlagLabel("MONOCHROME"),
			GetFontFlagLabel("SLUG"),
		},
		values = FONT_FLAG_VALUES,
		useCheckboxes = true,
		getSelectionText = GetSelectionText,
	}

	local holder
	holder = Components:CreateDropdown(
		parent,
		L.SETTINGS_FONT_FLAGS or "Font Flags:",
		flagOptions,
		function(value)
			local flagSet = GetFlagSet()
			if value == "NONE" then
				return next(flagSet) == nil
			end
			return flagSet[value] == true
		end,
		function(value)
			local flagSet = GetFlagSet()
			if value == "NONE" then
				flagSet = {}
			else
				flagSet[value] = not flagSet[value]
				if value == "OUTLINE" and flagSet.OUTLINE then
					flagSet.THICKOUTLINE = nil
				elseif value == "THICKOUTLINE" and flagSet.THICKOUTLINE then
					flagSet.OUTLINE = nil
				end
			end

			local flags = BFL.FontManager and BFL.FontManager.SerializeFontFlags
					and BFL.FontManager:SerializeFontFlags(flagSet)
				or ""
			db:Set(dbKey, flags)

			if holder and holder.DropDown then
				BFL.SetDropdownText(holder.DropDown, GetSelectionText())
			end

			if refreshCallback then
				refreshCallback()
			end
		end,
		controlWidth
	)

	if holder.SetTooltip then
		holder:SetTooltip(
			L.SETTINGS_FONT_FLAGS or "Font Flags:",
			L.SETTINGS_FONT_FLAGS_TOOLTIP
				or "Choose one or more rendering flags for this font. Slug is applied only on clients that support it."
		)
	end

	return holder
end

--------------------------------------------------------------------------
-- PRIVATE HELPER FUNCTIONS
--------------------------------------------------------------------------

-- Create a single group button for the Groups tab
local function CreateGroupButton(parent, groupId, groupName, orderIndex)
	local button = CreateFrame("Button", nil, parent)
	button.groupId = groupId
	button.orderIndex = orderIndex

	button:SetSize(310, 32)
	button:SetNormalTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
	button:GetNormalTexture():SetAlpha(UI.ALPHA_DIMMED)
	button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
	button:GetHighlightTexture():SetAlpha(0.7)

	-- Background
	button.bg = button:CreateTexture(nil, "BACKGROUND")
	button.bg:SetAllPoints()
	button.bg:SetColorTexture(unpack(UI.BG_COLOR_DARK))

	-- Drag Handle (:::)
	button.dragHandle = button:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormal")
	button.dragHandle:SetPoint("LEFT", UI.SPACING_SMALL, 0)
	button.dragHandle:SetText(":::")
	button.dragHandle:SetTextColor(unpack(UI.TEXT_COLOR_GRAY))

	-- Order Number
	button.orderText = button:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormal")
	button.orderText:SetPoint("LEFT", button.dragHandle, "RIGHT", UI.SPACING_MEDIUM, 0)
	button.orderText:SetText(orderIndex)
	button.orderText:SetTextColor(0.7, 0.7, 0.7)

	-- Group Name
	button.nameText = button:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormal")
	button.nameText:SetPoint("LEFT", button.orderText, "RIGHT", UI.SPACING_LARGE, 0)
	button.nameText:SetText(groupName)
	button.nameText:SetTextColor(1, 1, 1)

	-- Edit/Rename Button (only for custom groups)
	local isBuiltIn = (groupId == "favorites" or groupId == "nogroup")

	if not isBuiltIn then
		button.editButton = CreateFrame("Button", nil, button)
		button.editButton:SetSize(24, 24)
		button.editButton:SetPoint("RIGHT", -70, 0)

		local bg = button.editButton:CreateTexture(nil, "BACKGROUND")
		bg:SetAllPoints()
		bg:SetColorTexture(0, 0, 0, 1)

		local texture = button.editButton:CreateTexture(nil, "ARTWORK")
		texture:SetPoint("TOPLEFT", button.editButton, "TOPLEFT", UI.SPACING_TINY, -UI.SPACING_TINY)
		texture:SetPoint("BOTTOMRIGHT", button.editButton, "BOTTOMRIGHT", -UI.SPACING_TINY, UI.SPACING_TINY)
		texture:SetTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Up")
		button.editButton.texture = texture

		button.editButton:SetHighlightTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Up")
		local highlightTex = button.editButton:GetHighlightTexture()
		if highlightTex then
			highlightTex:ClearAllPoints()
			highlightTex:SetPoint("TOPLEFT", button.editButton, "TOPLEFT", UI.SPACING_TINY, -UI.SPACING_TINY)
			highlightTex:SetPoint("BOTTOMRIGHT", button.editButton, "BOTTOMRIGHT", -UI.SPACING_TINY, UI.SPACING_TINY)
		end

		button.editButton:SetScript("OnClick", function(self)
			Settings:RenameGroup(groupId, groupName)
		end)

		button.editButton:SetScript("OnEnter", function(self)
			BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
			BFL_Tooltip:SetText(L.TOOLTIP_RENAME_GROUP, 1, 1, 1)
			BFL_Tooltip:AddLine(L.TOOLTIP_RENAME_DESC, 0.8, 0.8, 0.8, true)
			BFL_Tooltip:Show()
		end)

		button.editButton:SetScript("OnLeave", function(self)
			BFL_Tooltip:Hide()
		end)
	end

	-- Color Picker Button
	button.colorButton = CreateFrame("Button", nil, button)
	button.colorButton:SetSize(24, 24)
	button.colorButton:SetPoint("RIGHT", -40, 0)

	button.colorBorder = button.colorButton:CreateTexture(nil, "BACKGROUND")
	button.colorBorder:SetAllPoints(button.colorButton)
	button.colorBorder:SetColorTexture(0, 0, 0, 0)

	button.colorSwatch = button.colorButton:CreateTexture(nil, "ARTWORK")
	button.colorSwatch:SetPoint("TOPLEFT", button.colorButton, "TOPLEFT", UI.SPACING_TINY, -UI.SPACING_TINY)
	button.colorSwatch:SetPoint("BOTTOMRIGHT", button.colorButton, "BOTTOMRIGHT", -UI.SPACING_TINY, UI.SPACING_TINY)
	button.colorSwatch:SetColorTexture(1, 1, 1)

	-- Load current color from Groups module
	local Groups = GetGroups()
	if Groups then
		local group = Groups:Get(groupId)
		if group and group.color then
			local alpha = group.color.a
			if alpha == nil then
				alpha = 1
			end
			button.colorSwatch:SetColorTexture(group.color.r, group.color.g, group.color.b, alpha)
		else
			button.colorSwatch:SetColorTexture(1, 0.82, 0, 1)
		end
	end

	-- Highlight removed by user request
	-- button.colorButton:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")

	button.colorButton:SetScript("OnClick", function(self)
		Settings:ShowColorPicker(groupId, groupName, button.colorSwatch)
	end)

	button.colorButton:SetScript("OnEnter", function(self)
		BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
		BFL_Tooltip:SetText(L.TOOLTIP_GROUP_COLOR, GetAccentColor(1, 0.82, 0, 1))
		BFL_Tooltip:AddLine(L.TOOLTIP_GROUP_COLOR_DESC, 0.8, 0.8, 0.8, true)
		BFL_Tooltip:Show()
	end)

	button.colorButton:SetScript("OnLeave", function(self)
		BFL_Tooltip:Hide()
	end)

	-- Delete Button (only for custom groups)
	if not isBuiltIn then
		button.deleteButton = CreateFrame("Button", nil, button)
		button.deleteButton:SetSize(24, 24)
		button.deleteButton:SetPoint("RIGHT", -UI.SPACING_MEDIUM, 0)

		local bg = button.deleteButton:CreateTexture(nil, "BACKGROUND")
		bg:SetAllPoints()
		bg:SetColorTexture(0, 0, 0, 1)

		local texture = button.deleteButton:CreateTexture(nil, "ARTWORK")
		texture:SetPoint("TOPLEFT", button.deleteButton, "TOPLEFT", UI.SPACING_TINY, -UI.SPACING_TINY)
		texture:SetPoint("BOTTOMRIGHT", button.deleteButton, "BOTTOMRIGHT", -UI.SPACING_TINY, UI.SPACING_TINY)
		SetDeleteIconTexture(texture)
		texture:SetVertexColor(0.9, 0.2, 0.2)
		button.deleteButton.texture = texture

		local highlight = button.deleteButton:CreateTexture(nil, "HIGHLIGHT")
		highlight:SetPoint("TOPLEFT", button.deleteButton, "TOPLEFT", UI.SPACING_TINY, -UI.SPACING_TINY)
		highlight:SetPoint("BOTTOMRIGHT", button.deleteButton, "BOTTOMRIGHT", -UI.SPACING_TINY, UI.SPACING_TINY)
		SetDeleteIconTexture(highlight)
		highlight:SetVertexColor(1, 0.4, 0.4)
		highlight:SetAlpha(0.8)

		button.deleteButton:SetScript("OnClick", function(self)
			Settings:DeleteGroup(groupId, groupName)
		end)

		button.deleteButton:SetScript("OnEnter", function(self)
			BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
			BFL_Tooltip:SetText(L.TOOLTIP_DELETE_GROUP, 1, 0.2, 0.2)
			BFL_Tooltip:AddLine(L.TOOLTIP_DELETE_DESC, 0.8, 0.8, 0.8, true)
			BFL_Tooltip:Show()
		end)

		button.deleteButton:SetScript("OnLeave", function(self)
			BFL_Tooltip:Hide()
		end)
	end

	-- Drag functionality
	button:RegisterForDrag("LeftButton")
	button:SetScript("OnDragStart", function(self)
		draggedGroupButton = self
		self:SetAlpha(UI.ALPHA_DIMMED)

		self:SetScript("OnUpdate", function(updateSelf)
			local cursorX, cursorY = GetCursorPosition()
			local scale = UIParent:GetEffectiveScale()
			cursorX = cursorX / scale
			cursorY = cursorY / scale

			for _, btn in ipairs(groupButtons) do
				if btn ~= updateSelf then
					btn.bg:SetColorTexture(unpack(UI.BG_COLOR_DARK))
				end
			end

			for _, btn in ipairs(groupButtons) do
				if btn ~= updateSelf and btn:IsVisible() then
					local left, bottom, width, height = btn:GetRect()
					if
						left
						and cursorX >= left
						and cursorX <= left + width
						and cursorY >= bottom
						and cursorY <= bottom + height
					then
						btn.bg:SetColorTexture(0.3, 0.3, 0.3, 0.7)
						break
					end
				end
			end
		end)
	end)

	button:SetScript("OnDragStop", function(self)
		self:SetAlpha(1.0)
		self:SetScript("OnUpdate", nil)

		if not draggedGroupButton then
			return
		end

		local cursorX, cursorY = GetCursorPosition()
		local scale = UIParent:GetEffectiveScale()
		cursorX = cursorX / scale
		cursorY = cursorY / scale

		local targetButton = nil
		for _, btn in ipairs(groupButtons) do
			if btn ~= draggedGroupButton and btn:IsVisible() then
				local left, bottom, width, height = btn:GetRect()
				if
					left
					and cursorX >= left
					and cursorX <= left + width
					and cursorY >= bottom
					and cursorY <= bottom + height
				then
					targetButton = btn
					break
				end
			end
		end

		if targetButton then
			local draggedIndex = draggedGroupButton.orderIndex
			local targetIndex = targetButton.orderIndex

			draggedGroupButton.orderIndex = targetIndex
			targetButton.orderIndex = draggedIndex
		end

		for _, btn in ipairs(groupButtons) do
			btn.bg:SetColorTexture(unpack(UI.BG_COLOR_DARK))
		end

		draggedGroupButton = nil
		Settings:SaveGroupOrder()
	end)

	button:SetScript("OnEnter", function(self)
		if draggedGroupButton and draggedGroupButton ~= self then
			self.bg:SetColorTexture(0.3, 0.3, 0.3, 0.7)
		end
	end)

	button:SetScript("OnLeave", function(self)
		if not draggedGroupButton or draggedGroupButton == self then
			self.bg:SetColorTexture(unpack(UI.BG_COLOR_DARK))
		end
	end)

	return button
end

-- Parse FriendGroups note format: "ActualNote#Group1#Group2#Group3"
local function ParseFriendGroupsNote(noteText)
	if not noteText or noteText == "" then
		return "", {}
	end

	BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r     Parsing note:", noteText)

	local parts = { strsplit("#", noteText) }
	local actualNote = parts[1] or ""
	local groups = {}

	BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r     Split into", #parts, "parts")

	for i = 2, #parts do
		local groupName = strtrim(parts[i])
		if groupName ~= "" then
			table.insert(groups, groupName)
			BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r     Found group:", groupName)
		end
	end

	BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r     Actual note:", actualNote)
	BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r     Total groups found:", #groups)

	return actualNote, groups
end

--------------------------------------------------------------------------
-- PUBLIC API
--------------------------------------------------------------------------

-- Initialize the settings frame
function Settings:OnLoad(frame)
	settingsFrame = frame
	if not settingsFrame then
		return
	end

	-- Setup close button
	if settingsFrame.CloseButton then
		settingsFrame.CloseButton:SetScript("OnClick", function()
			self:Cancel()
		end)
	end

	settingsFrame:HookScript("OnHide", function()
		self:ClearFilterSortPreview(true)
		self:HideFilterSortEditorPanel()
	end)
end

function Settings:IsSettingsCenterEnabled()
	return BetterFriendlistDB and BetterFriendlistDB.enableSettingsCenterBeta == true
end

function Settings:ShowSettingsCenter(pageID, focusControlID)
	local Designer = BFL:GetModule("SettingsDesigner")
	if Designer and Designer.Show then
		local frame = Designer:Show(pageID, focusControlID)
		if frame then
			return true
		end
	end
	return false
end

function Settings:HideLegacyWindow()
	self:ClearFilterSortPreview(true)
	self:HideFilterSortEditorPanel()
	if settingsFrame then
		settingsFrame:Hide()
	end
end

function Settings:OnSettingsCenterBetaChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	checked = checked == true
	DB:Set("enableSettingsCenterBeta", checked)

	if checked then
		self:HideLegacyWindow()
		self:ShowSettingsCenter("advanced.beta", "enableSettingsCenterBeta")
		BFL:DebugPrint(L.SETTINGS_CENTER_BETA_ENABLED or "New Settings Center |cff00ff00ENABLED|r")
	else
		local Designer = BFL:GetModule("SettingsDesigner")
		if Designer and Designer.Hide then
			Designer:Hide()
		end
		self:ShowLegacy(4, "betaFeatures")
		BFL:DebugPrint(L.SETTINGS_CENTER_BETA_DISABLED or "New Settings Center |cffff0000DISABLED|r")
	end
end

-- Show the active settings window. Legacy remains the default until the
-- Settings Center beta is explicitly enabled.
function Settings:Show(pageID, focusControlID)
	if self:IsSettingsCenterEnabled() and self:ShowSettingsCenter(pageID, focusControlID) then
		return
	end

	self:ShowLegacy()
end

-- Show the legacy XML settings window.
function Settings:ShowLegacy(categoryID, focusSection)
	local Designer = BFL:GetModule("SettingsDesigner")
	if Designer and Designer.Hide then
		Designer:Hide()
	end

	if not settingsFrame then
		settingsFrame = BetterFriendlistSettingsFrame
	end

	if settingsFrame then
		if _G.BetterFriendsFrame and not _G.BetterFriendsFrame:IsShown() then
			if _G.ToggleBetterFriendsFrame then
				_G.ToggleBetterFriendsFrame()
			end
		end
		self:LoadSettings()
		self:RefreshCategories() -- Update category visibility based on Beta features
		settingsFrame:Show()
		self:SelectCategory(categoryID or currentTab or 1) -- Restore or show first category
		if focusSection == "betaFeatures" then
			self:ScrollToLegacyBetaFeatures()
		end
	else
		-- BFL:DebugPrint("|cffff0000BetterFriendlist Settings:|r Frame not initialized!")
	end
end

function Settings:ScrollToLegacyBetaFeatures()
	local scrollFrame = settingsFrame and settingsFrame.ContentScrollFrame
	local content = scrollFrame and scrollFrame.Content
	local anchor = self.legacyBetaFeaturesAnchor
	if not (scrollFrame and content and anchor and anchor.GetTop and scrollFrame.SetVerticalScroll) then
		return
	end

	if scrollFrame.UpdateScrollChildRect then
		pcall(scrollFrame.UpdateScrollChildRect, scrollFrame)
	end

	local contentTop = content:GetTop()
	local anchorTop = anchor:GetTop()
	if not (contentTop and anchorTop) then
		return
	end

	local offset = math.max(0, contentTop - anchorTop - 8)
	pcall(scrollFrame.SetVerticalScroll, scrollFrame, offset)
end

-- ===========================================
-- TAB SYSTEM: Central Definition
-- ===========================================
local TAB_DEFINITIONS = {
	-- Stable Tabs (always visible)
	{
		id = 1,
		name = L.SETTINGS_TAB_GENERAL,
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\settings.blp",
		beta = false,
	},
	{
		id = 10,
		name = L.SETTINGS_TAB_THEME or "Theme",
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\sliders.blp",
		beta = false,
		-- Theme is a standard Retail and Classic settings tab.
	},
	{
		id = 2,
		name = L.SETTINGS_TAB_FONTS,
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\type.blp",
		beta = false,
	},
	{
		id = 3,
		name = L.SETTINGS_TAB_GROUPS,
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\users.blp",
		beta = false,
	},
	{
		id = 8,
		name = L.SETTINGS_TAB_RAID,
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\shield.blp",
		beta = false,
	},
	{
		id = 9,
		name = L.SETTINGS_TAB_WHO or "Who",
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\search.blp",
		beta = false,
	},
	{
		id = 4,
		name = L.SETTINGS_TAB_ADVANCED,
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\sliders.blp",
		beta = false,
	},
	{
		id = 11,
		name = L.SETTINGS_TAB_FILTER_SORT or "QuickFilter & Sorter",
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\filter.blp",
		beta = true,
	},
	{
		id = 14,
		name = L.CONTACT_MEMORY_TITLE or "Private Notes",
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\file-text.blp",
		beta = true,
	},
	{
		id = 13,
		name = L.SETTINGS_TAB_FRIEND_TAGS or "Friend Tags",
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\tag.blp",
		beta = true,
	},
	{
		id = 12,
		name = L.SETTINGS_TAB_GUILD or GUILD or "Guild",
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\guild.blp",
		beta = true,
		retailOnly = true,
	},

	-- Data Broker & Global Sync (Stable)
	{
		id = 5,
		name = L.SETTINGS_TAB_DATABROKER,
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\activity.blp",
		beta = false,
	},
	{
		id = 6,
		name = L.SETTINGS_TAB_GLOBAL_SYNC,
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\globe.blp",
		beta = false,
	},

	-- Special Tabs (Force to new line)
	{
		id = 7,
		name = L.STREAMER_MODE_TITLE,
		icon = "Interface\\AddOns\\BetterFriendlist\\Icons\\twitch.blp",
		beta = false,
	},
	-- Future beta tabs go here...
}

-- Color scheme
local COLOR_STABLE = "|cffffff00" -- Gold
local COLOR_BETA = "|cffff8800" -- Orange

local function GetAccentColor(fallbackR, fallbackG, fallbackB, fallbackA)
	if BFL.GetThemeAccentColor then
		return BFL:GetThemeAccentColor(fallbackR or 1, fallbackG or 0.82, fallbackB or 0, fallbackA or 1)
	end
	return fallbackR or 1, fallbackG or 0.82, fallbackB or 0, fallbackA or 1
end

local function IsTabAvailableForClient(tabDef)
	return not tabDef.retailOnly or BFL.IsRetail == true
end

local function ShouldShowLegacyElvUISkinSetting()
	if BFL.ShouldShowLegacyElvUISkinSetting then
		return BFL:ShouldShowLegacyElvUISkinSetting()
	end
	if not (BFL.IsElvUIAvailable and BFL:IsElvUIAvailable()) then
		return false
	end
	if BFL.ShouldUseLegacyElvUISkinSetting then
		return BFL:ShouldUseLegacyElvUISkinSetting()
	end
	return false
end

local function IsLegacyElvUISkinSelected(DB)
	if not DB then
		return false
	end
	return DB:Get("theme", "blizzard") == "elvui" or DB:Get("enableElvUISkin", false) == true
end

-- Get all tabs that should be visible based on current settings
local function GetVisibleTabs()
	local betaEnabled = BetterFriendlistDB and BetterFriendlistDB.enableBetaFeatures or false
	local visibleTabs = {}

	for _, tabDef in ipairs(TAB_DEFINITIONS) do
		-- Show tab if: it's stable OR (it's beta AND beta features are enabled)
		if IsTabAvailableForClient(tabDef) and (not tabDef.beta or betaEnabled) then
			table.insert(visibleTabs, tabDef)
		end
	end

	return visibleTabs
end

local function IsTabVisible(tabID)
	for _, tabDef in ipairs(GetVisibleTabs()) do
		if tabDef.id == tabID then
			return true
		end
	end
	return false
end

-- Get all beta tab IDs (for auto-switching when disabling beta)
local function GetBetaTabIds()
	local betaTabIds = {}
	for _, tabDef in ipairs(TAB_DEFINITIONS) do
		if tabDef.beta and IsTabAvailableForClient(tabDef) then
			table.insert(betaTabIds, tabDef.id)
		end
	end
	return betaTabIds
end

-- Refresh category visibility (Beta features toggle)
function Settings:RefreshCategories()
	if not settingsFrame then
		settingsFrame = BetterFriendlistSettingsFrame
	end
	if not settingsFrame then
		return
	end

	-- Ensure CategoryList exists
	local listFrame = settingsFrame.CategoryList
	if not listFrame then
		return
	end

	local visibleCategories = GetVisibleTabs()

	-- Layout constants
	local BUTTON_HEIGHT = 32
	local SPACING = 2
	local MAX_TEXT_WIDTH = 0

	-- Update category buttons
	for i, catDef in ipairs(visibleCategories) do
		local button = categoryButtons[i]
		if not button then
			button = CreateFrame("Button", nil, listFrame)
			button:SetHeight(BUTTON_HEIGHT)

			-- Background/Highlight
			button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
			button:GetHighlightTexture():SetAlpha(UI.ALPHA_DIMMED or 0.3)
			button:GetHighlightTexture():SetBlendMode("ADD")

			-- Selected State Indicator
			button.selectedTex = button:CreateTexture(nil, "BACKGROUND")
			button.selectedTex:SetAllPoints()
			button.selectedTex:SetColorTexture(1, 1, 1, 0.1)
			button.selectedTex:Hide()

			-- Icon
			button.icon = button:CreateTexture(nil, "ARTWORK")
			button.icon:SetSize(18, 18)
			button.icon:SetPoint("LEFT", 10, 0)

			-- Text
			button.text = button:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormal")
			button.text:SetPoint("LEFT", button.icon, "RIGHT", 10, 0)
			button.text:SetJustifyH("LEFT")

			-- Click Handler
			button:SetScript("OnClick", function(self)
				Settings:SelectCategory(self.id)
			end)

			categoryButtons[i] = button
		end

		-- Update Button Data
		button.id = catDef.id
		button:Show()
		button:ClearAllPoints()
		button:SetPoint("TOPLEFT", listFrame, "TOPLEFT", 0, -((i - 1) * (BUTTON_HEIGHT + SPACING)))
		button:SetPoint("TOPRIGHT", listFrame, "TOPRIGHT", 0, -((i - 1) * (BUTTON_HEIGHT + SPACING)))

		-- Styling
		local r, g, b = GetAccentColor(1, 0.82, 0) -- Default Gold
		if catDef.beta then
			r, g, b = 1, 0.53, 0 -- Orange
		end

		button.text:SetText(catDef.name)
		button.text:SetTextColor(r, g, b)

		if catDef.icon then
			button.icon:SetTexture(catDef.icon)
			button.icon:SetVertexColor(r, g, b) -- Match text color exactly
			button.icon:Show()
		else
			button.icon:Hide()
		end

		-- Measure width
		local width = button.text:GetStringWidth()
		if width > MAX_TEXT_WIDTH then
			MAX_TEXT_WIDTH = width
		end
	end

	-- Hide unused buttons
	for i = #visibleCategories + 1, #categoryButtons do
		categoryButtons[i]:Hide()
	end

	-- Dynamic Width Calculation (Icon + Gap + Text + Padding)
	local requiredWidth = 10 + 18 + 10 + MAX_TEXT_WIDTH + 20
	local sidebarWidth = math.max(requiredWidth, 150)
	listFrame:SetWidth(sidebarWidth)

	-- Update Content ScrollChild width to match visible area
	-- SettingsFrame (800) - Sidebar (sidebarWidth) - Margins
	-- MainInset Width = 790 - (sidebarWidth + 20) = 770 - sidebarWidth
	-- ScrollChild Width = MainInset Width - 33 (8 left + 25 right anchors)
	local contentWidth = (770 - sidebarWidth) - 33

	if settingsFrame.ContentScrollFrame and settingsFrame.ContentScrollFrame.Content then
		settingsFrame.ContentScrollFrame.Content:SetWidth(contentWidth)

		-- Force layout update for active tab components if needed
		local content = settingsFrame.ContentScrollFrame.Content
		if content.GeneralTab and content.GeneralTab:IsVisible() then
			-- Re-trigger logic if strictly needed, but OnSizeChanged usually fires automatically if width changed
		end
	end
end

-- Hide the settings window
function Settings:Hide()
	local Designer = BFL:GetModule("SettingsDesigner")
	if Designer and Designer.Hide then
		Designer:Hide()
	end

	self:ClearFilterSortPreview(true)
	self:HideFilterSortEditorPanel()
	if settingsFrame then
		settingsFrame:Hide()
	end
end

-- Select a settings category
function Settings:SelectCategory(categoryID)
	if not settingsFrame then
		return
	end

	if not IsTabVisible(categoryID) then
		categoryID = 1
	end

	currentTab = categoryID
	if categoryID ~= 11 then
		self:ClearFilterSortPreview(true)
		self:HideFilterSortEditorPanel()
	end

	-- Update Button Selection States
	for _, button in pairs(categoryButtons) do
		-- Determine if beta category for color restoration
		local isBeta = false
		for _, def in ipairs(TAB_DEFINITIONS) do
			if def.id == button.id and def.beta then
				isBeta = true
				break
			end
		end

		if button.id == categoryID then
			-- SELECTED STATE
			if button.selectedTex then
				button.selectedTex:Show()
			end
			button.text:SetTextColor(1, 1, 1) -- White Text
			if button.icon then
				button.icon:SetVertexColor(1, 1, 1)
			end -- White Icon
		else
			-- UNSELECTED STATE
			if button.selectedTex then
				button.selectedTex:Hide()
			end

			local r, g, b
			if isBeta then
				r, g, b = 1, 0.53, 0 -- Beta Orange
			else
				r, g, b = GetAccentColor(1, 0.82, 0) -- Normal Gold
			end

			button.text:SetTextColor(r, g, b)
			if button.icon then
				button.icon:SetVertexColor(r, g, b)
			end
		end
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if content then
		if content.GeneralTab then
			content.GeneralTab:Hide()
		end
		if content.ThemeTab then
			content.ThemeTab:Hide()
		end
		if content.FontsTab then
			content.FontsTab:Hide()
		end
		if content.GroupsTab then
			content.GroupsTab:Hide()
		end
		if content.AdvancedTab then
			content.AdvancedTab:Hide()
		end
		if content.FilterSortTab then
			content.FilterSortTab:Hide()
		end
		if content.ContactMemoryTab then
			content.ContactMemoryTab:Hide()
		end
		if content.FriendTagsTab then
			content.FriendTagsTab:Hide()
		end
		if content.GuildTab then
			content.GuildTab:Hide()
		end
		if content.StreamerTab then
			content.StreamerTab:Hide()
		end
		if content.BrokerTab then
			content.BrokerTab:Hide()
		end
		if content.GlobalSyncTab then
			content.GlobalSyncTab:Hide()
		end
		if content.RaidTab then
			content.RaidTab:Hide()
		end
		if content.WhoTab then
			content.WhoTab:Hide()
		end

		if categoryID == 1 and content.GeneralTab then
			content.GeneralTab:Show()
			self:RefreshGeneralTab()
		elseif categoryID == 10 and content.ThemeTab then
			content.ThemeTab:Show()
			self:RefreshThemeTab()
		elseif categoryID == 2 and content.FontsTab then
			content.FontsTab:Show()
			self:RefreshFontsTab()
		elseif categoryID == 3 and content.GroupsTab then
			content.GroupsTab:Show()
			self:RefreshGroupsTab()
		elseif categoryID == 4 and content.AdvancedTab then
			content.AdvancedTab:Show()
			self:RefreshAdvancedTab()
		elseif categoryID == 11 and content.FilterSortTab then
			content.FilterSortTab:Show()
			self:RefreshFilterSortTab()
		elseif categoryID == 14 and content.ContactMemoryTab then
			content.ContactMemoryTab:Show()
			self:RefreshContactMemoryTab()
		elseif categoryID == 13 and content.FriendTagsTab then
			content.FriendTagsTab:Show()
			self:RefreshFriendTagsTab()
		elseif categoryID == 12 and content.GuildTab then
			content.GuildTab:Show()
			self:RefreshGuildTab()
		elseif categoryID == 5 and content.BrokerTab then
			content.BrokerTab:Show()
			self:RefreshBrokerTab()
		elseif categoryID == 6 and content.GlobalSyncTab then
			content.GlobalSyncTab:Show()
			self:RefreshGlobalSyncTab()
		elseif categoryID == 7 and content.StreamerTab then
			content.StreamerTab:Show()
			self:RefreshStreamerTab()
		elseif categoryID == 8 and content.RaidTab then
			content.RaidTab:Show()
			self:RefreshRaidTab()
		elseif categoryID == 9 and content.WhoTab then
			content.WhoTab:Show()
			self:RefreshWhoTab()
		end

		-- Adjust content height dynamically after tab is shown
		self:AdjustContentHeight(categoryID)

		-- Reset scroll position to top (User Request: Fix scroll staying down when switching tabs)
		if settingsFrame.ContentScrollFrame then
			SafeResetVerticalScroll(settingsFrame.ContentScrollFrame)
		end
	end
end

-- Adjust the Content frame height based on the active tab's content
function Settings:AdjustContentHeight(tabID)
	if not settingsFrame then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content then
		return
	end

	local activeTab = nil
	if tabID == 1 then
		activeTab = content.GeneralTab
	elseif tabID == 10 then
		activeTab = content.ThemeTab
	elseif tabID == 2 then
		activeTab = content.FontsTab
	elseif tabID == 3 then
		activeTab = content.GroupsTab
	elseif tabID == 4 then
		activeTab = content.AdvancedTab
	elseif tabID == 11 then
		activeTab = content.FilterSortTab
	elseif tabID == 14 then
		activeTab = content.ContactMemoryTab
	elseif tabID == 13 then
		activeTab = content.FriendTagsTab
	elseif tabID == 12 then
		activeTab = content.GuildTab
	elseif tabID == 5 then
		activeTab = content.BrokerTab
	elseif tabID == 6 then
		activeTab = content.GlobalSyncTab
	elseif tabID == 7 then
		activeTab = content.StreamerTab
	elseif tabID == 8 then
		activeTab = content.RaidTab
	elseif tabID == 9 then
		activeTab = content.WhoTab
	end

	if not activeTab then
		return
	end

	-- Calculate required height by measuring all visible children
	local maxHeight = self:CalculateTabContentHeight(activeTab)

	-- Get available scroll frame height
	local scrollFrame = settingsFrame.ContentScrollFrame
	local availableHeight = scrollFrame:GetHeight()

	-- Set content height to maximum of calculated height or available height
	-- This ensures no unnecessary scrolling for small content
	local newHeight = math.max(maxHeight, availableHeight)
	content:SetHeight(newHeight)
end

-- Calculate the actual content height of a tab
function Settings:CalculateTabContentHeight(tab)
	if not tab then
		return 600
	end -- fallback

	local maxY = 0
	local minY = 0

	-- Iterate through all regions (textures, fontstrings, frames, etc.)
	local regions = { tab:GetRegions() }
	for _, region in ipairs(regions) do
		if region:IsShown() then
			local bottom = region:GetBottom()
			local top = region:GetTop()
			if bottom and top then
				local tabTop = tab:GetTop()
				if tabTop then
					minY = math.min(minY, bottom - tabTop)
					maxY = math.max(maxY, top - tabTop)
				end
			end
		end
	end

	-- Check all child frames
	local children = { tab:GetChildren() }
	for _, child in ipairs(children) do
		if child:IsShown() then
			local bottom = child:GetBottom()
			local top = child:GetTop()
			if bottom and top then
				local tabTop = tab:GetTop()
				if tabTop then
					minY = math.min(minY, bottom - tabTop)
					maxY = math.max(maxY, top - tabTop)
				end
			end
		end
	end

	-- Calculate total height needed (absolute value of minY gives depth below anchor)
	local totalHeight = math.abs(minY) + 20 -- Add 20px padding at bottom

	return math.max(totalHeight, 200) -- Minimum 200px height
end

-- Load settings from database into UI
function Settings:LoadSettings()
	local DB = GetDB()
	if not DB then
		return
	end

	if not settingsFrame or not settingsFrame.ContentScrollFrame then
		return
	end
	local content = settingsFrame.ContentScrollFrame.Content

	-- General tab is now populated via RefreshGeneralTab() which reads DB directly
	-- No need to manually set values here anymore

	if content and content.GroupsTab then
		local groupsTab = content.GroupsTab

		if groupsTab.ShowFavoritesGroup then
			local value = DB:Get("showFavoritesGroup", true)
			groupsTab.ShowFavoritesGroup:SetChecked(value)
		end
	end

	if content and content.AppearanceTab then
		local appearanceTab = content.AppearanceTab

		if appearanceTab.ColorClassNames then
			local value = DB:Get("colorClassNames", true)
			appearanceTab.ColorClassNames:SetChecked(value)
		end

		if appearanceTab.HideEmptyGroups then
			local value = DB:Get("hideEmptyGroups", false)
			appearanceTab.HideEmptyGroups:SetChecked(value)
		end

		if appearanceTab.ShowFactionIcons then
			local value = DB:Get("showFactionIcons", false)
			appearanceTab.ShowFactionIcons:SetChecked(value)
		end

		if appearanceTab.ShowRealmName then
			local value = DB:Get("showRealmName", false)
			appearanceTab.ShowRealmName:SetChecked(value)
		end

		if appearanceTab.GrayOtherFaction then
			local value = DB:Get("grayOtherFaction", false)
			appearanceTab.GrayOtherFaction:SetChecked(value)
		end

		if appearanceTab.ShowMobileAsAFK then
			local value = DB:Get("showMobileAsAFK", false)
			appearanceTab.ShowMobileAsAFK:SetChecked(value)
		end

		if appearanceTab.ShowMobileText then
			local value = DB:Get("showMobileText", false)
			appearanceTab.ShowMobileText:SetChecked(value)
		end

		if appearanceTab.HideMaxLevel then
			local value = DB:Get("hideMaxLevel", false)
			appearanceTab.HideMaxLevel:SetChecked(value)
		end
		if appearanceTab.AccordionGroups then
			local value = DB:Get("accordionGroups", false)
			appearanceTab.AccordionGroups:SetChecked(value)
		end

		if appearanceTab.CompactMode then
			local value = DB:Get("compactMode", false)
			appearanceTab.CompactMode:SetChecked(value)
		end
	end
end

-- Set font size and save immediately
function Settings:SetFontSize(size)
	-- REMOVED: Global font scaling disabled (User Request 2026-01-20)
	-- Do nothing
end

-- Reset to defaults
function Settings:ResetToDefaults()
	StaticPopup_Show("BETTER_FRIENDLIST_RESET_SETTINGS")
end

-- Perform the actual reset
function Settings:DoReset()
	local DB = GetDB()
	if not DB then
		-- BFL:DebugPrint("|cffff0000BetterFriendlist Settings:|r Database not available!")
		return
	end

	self:OnThemeChanged("blizzard")
	DB:Set("showBlizzardOption", false)
	DB:Set("compactMode", false)
	-- DB:Set("fontSize", "normal") -- REMOVED
	DB:Set("colorClassNames", true)
	DB:Set("hideEmptyGroups", false)
	DB:Set("showFactionIcons", false)
	DB:Set("showFactionBg", false)
	DB:Set("showRealmName", false)
	DB:Set("grayOtherFaction", false)
	DB:Set("showMobileAsAFK", false)
	DB:Set("treatMobileAsOffline", false) -- NEW: Feature Request
	DB:Set("showNotesAsName", false) -- NEW: Feature Request
	DB:Set("showNicknameAsName", false) -- NEW: Feature Request
	DB:Set("showNicknameInName", false) -- NEW: Feature Request
	DB:Set("windowScale", 1.0) -- NEW: Feature Request
	DB:Set("hideMaxLevel", false)
	DB:Set("accordionGroups", false)
	DB:Set("favoriteIconStyle", "bfl")
	DB:Set("friendListClickWhisperEnabled", false)
	DB:Set("friendListClickWhisperMode", "double")

	self:LoadSettings()

	-- Reset window scale to 100%
	local MainFrameEditMode = BFL.Modules and BFL.Modules.MainFrameEditMode
	if MainFrameEditMode and MainFrameEditMode.ApplyScale then
		MainFrameEditMode:ApplyScale()
	elseif BetterFriendsFrame then
		BetterFriendsFrame:SetScale(1.0)
	end

	-- Force full display refresh - reset affects all display settings
	BFL:ForceRefreshFriendsList()

	BFL:DebugPrint("|cff20ff20BetterFriendlist:|r " .. L.SETTINGS_RESET_SUCCESS)
end

-- Refresh the group list in the Groups tab (Legacy wrapper)
function Settings:RefreshGroupList()
	-- Now uses RefreshGroupsTab which generates everything dynamically
	self:RefreshGroupsTab()
	return
end

-- Old implementation (kept for reference, but not used)
function Settings:RefreshGroupList_OLD()
	local frame = BetterFriendlistSettingsFrame
	if
		not frame
		or not frame.ContentScrollFrame
		or not frame.ContentScrollFrame.Content
		or not frame.ContentScrollFrame.Content.GroupsTab
	then
		return
	end

	local container = frame.ContentScrollFrame.Content.GroupsTab.GroupOrderSection.Container

	-- Clear existing buttons
	for _, button in ipairs(groupButtons) do
		button:Hide()
		button:SetParent(nil)
	end
	groupButtons = {}

	local DB = GetDB()
	local groupOrder = DB and DB:Get("groupOrder") or nil

	local Groups = GetGroups()
	if not Groups then
		-- BFL:DebugPrint("|cffff0000BetterFriendlist Settings:|r Groups module not available!")
		return
	end

	local allGroups = Groups:GetAll()
	local orderedGroups = {}

	if groupOrder and #groupOrder > 0 then
		for i, groupId in ipairs(groupOrder) do
			local group = allGroups[groupId]
			if group then
				table.insert(orderedGroups, { id = groupId, name = group.name, order = i })
			end
		end
		for groupId, group in pairs(allGroups) do
			local found = false
			for _, ordered in ipairs(orderedGroups) do
				if ordered.id == groupId then
					found = true
					break
				end
			end
			if not found then
				table.insert(orderedGroups, { id = groupId, name = group.name, order = #orderedGroups + 1 })
			end
		end
	else
		table.insert(orderedGroups, { id = "favorites", name = L.GROUP_FAVORITES, order = 1 })

		local customGroups = {}
		for groupId, group in pairs(allGroups) do
			if groupId ~= "favorites" and groupId ~= "nogroup" then
				table.insert(customGroups, { id = groupId, name = group.name })
			end
		end
		table.sort(customGroups, function(a, b)
			return a.name < b.name
		end)
		for _, group in ipairs(customGroups) do
			table.insert(orderedGroups, { id = group.id, name = group.name, order = #orderedGroups + 1 })
		end

		if allGroups["nogroup"] then
			table.insert(orderedGroups, { id = "nogroup", name = L.GROUP_NO_GROUP, order = #orderedGroups + 1 })
		end
	end

	local yOffset = UI.TOP_OFFSET
	for i, groupData in ipairs(orderedGroups) do
		local button = CreateGroupButton(container, groupData.id, groupData.name, i)
		button:SetPoint("TOPLEFT", UI.SPACING_SMALL, yOffset)
		button:Show()
		table.insert(groupButtons, button)
		yOffset = yOffset - UI.BUTTON_HEIGHT_STANDARD
	end

	container:SetHeight(math.max(1, #orderedGroups * UI.BUTTON_HEIGHT_STANDARD + UI.SPACING_MEDIUM))
end

-- Save group order after drag/drop
function Settings:SaveGroupOrder()
	local newOrder = {}

	local sortedButtons = {}
	for _, button in ipairs(groupButtons) do
		table.insert(sortedButtons, button)
	end
	table.sort(sortedButtons, function(a, b)
		return a.orderIndex < b.orderIndex
	end)

	for _, button in ipairs(sortedButtons) do
		table.insert(newOrder, button.groupId)
	end

	local DB = GetDB()
	if DB then
		DB:Set("groupOrder", newOrder)
		BFL:DebugPrint("|cff20ff20BetterFriendlist:|r " .. L.SETTINGS_GROUP_ORDER_SAVED)

		local Groups = GetGroups()
		if Groups and Groups.Initialize then
			Groups:Initialize()
		end

		if BetterFriendsFrame_UpdateFriendsList then
			BetterFriendsFrame_UpdateFriendsList()
		end
	end

	self:RefreshGroupList()
end

-- Show color picker for a group
function Settings:ShowGroupCountColorPicker(groupId, groupName, colorSwatch, isReset)
	local DB = GetDB()
	if not DB then
		return
	end

	-- Handle Reset (Right Click) - Snapshot Group Color (Fix #34: includes alpha)
	if isReset then
		local Groups = GetGroups()
		local group = Groups and Groups:Get(groupId)
		local r, g, b, a = 1, 1, 1, 1
		if group and group.color then
			r, g, b = group.color.r, group.color.g, group.color.b
			a = group.color.a or 1
		end

		local groupCountColors = DB:Get("groupCountColors") or {}
		groupCountColors[groupId] = { r = r, g = g, b = b, a = a }
		DB:Set("groupCountColors", groupCountColors)

		if group then
			group.countColor = { r = r, g = g, b = b, a = a }
		end

		BFL:ForceRefreshFriendsList()
		return
	end

	local Groups = GetGroups()
	local r, g, b = 1, 1, 1

	if Groups then
		local group = Groups:Get(groupId)
		if group then
			-- Start with current count color, or fallback to group color (or default white)
			if group.countColor then
				r, g, b = group.countColor.r, group.countColor.g, group.countColor.b
			elseif group.color then
				r, g, b = group.color.r, group.color.g, group.color.b
			end
		end
	end

	-- Robust Cleanup: Ensure no stale callbacks or state exist
	ColorPickerFrame:Hide()
	ColorPickerFrame.func = nil
	ColorPickerFrame.opacityFunc = nil
	ColorPickerFrame.cancelFunc = nil

	-- Fix #34: Get existing alpha value for count color
	local a = 1.0
	if Groups then
		local group = Groups:Get(groupId)
		if group and group.countColor and group.countColor.a then
			a = group.countColor.a
		elseif group and group.color and group.color.a then
			a = group.color.a -- Inherit from group color
		end
	end

	local info = {}
	info.r = r
	info.g = g
	info.b = b
	info.opacity = a
	info.hasOpacity = true -- Fix #34: Enable alpha slider
	info.swatchFunc = function()
		local newR, newG, newB = ColorPickerFrame:GetColorRGB()
		local newA = ColorPickerFrame:GetColorAlpha() or 1

		colorSwatch:SetColorTexture(newR, newG, newB, newA)

		local groupCountColors = DB:Get("groupCountColors") or {}
		groupCountColors[groupId] = { r = newR, g = newG, b = newB, a = newA }
		DB:Set("groupCountColors", groupCountColors)

		local Groups = GetGroups()
		if Groups then
			local group = Groups:Get(groupId)
			if group then
				group.countColor = { r = newR, g = newG, b = newB, a = newA }
			end
		end

		BFL:ForceRefreshFriendsList()
	end
	info.opacityFunc = info.swatchFunc -- Fix #34: Update on alpha slider change
	info.cancelFunc = function(previousValues)
		local prevA = previousValues.a or 1
		colorSwatch:SetColorTexture(previousValues.r, previousValues.g, previousValues.b, prevA)

		local groupCountColors = DB:Get("groupCountColors") or {}
		groupCountColors[groupId] = { r = previousValues.r, g = previousValues.g, b = previousValues.b, a = prevA }
		DB:Set("groupCountColors", groupCountColors)

		local Groups = GetGroups()
		if Groups then
			local group = Groups:Get(groupId)
			if group then
				group.countColor = { r = previousValues.r, g = previousValues.g, b = previousValues.b, a = prevA }
			end
		end

		BFL:ForceRefreshFriendsList()
	end

	if ColorPickerFrame.SetupColorPickerAndShow then
		ColorPickerFrame:SetupColorPickerAndShow(info)
		-- Force update the color RGB again to ensure internal wheel state is synced
		-- This fixes an issue in some WoW versions where the picker remembers previous drag position
		if ColorPickerFrame.SetColorRGB then
			ColorPickerFrame:SetColorRGB(info.r, info.g, info.b)
		end
	else
		ColorPickerFrame.func = info.swatchFunc
		ColorPickerFrame.cancelFunc = info.cancelFunc
		ColorPickerFrame.opacityFunc = nil
		ColorPickerFrame.hasOpacity = info.hasOpacity
		ColorPickerFrame.opacity = info.opacity
		ColorPickerFrame:SetColorRGB(info.r, info.g, info.b)
		ColorPickerFrame:Show()
	end
end

function Settings:ShowGroupArrowColorPicker(groupId, groupName, colorSwatch, isReset)
	local DB = GetDB()
	if not DB then
		return
	end

	-- Handle Reset (Right Click) - Snapshot Group Color (Fix #34: includes alpha)
	if isReset then
		local Groups = GetGroups()
		local group = Groups and Groups:Get(groupId)
		local r, g, b, a = 1, 1, 1, 1
		if group and group.color then
			r, g, b = group.color.r, group.color.g, group.color.b
			a = group.color.a or 1
		end

		local groupArrowColors = DB:Get("groupArrowColors") or {}
		groupArrowColors[groupId] = { r = r, g = g, b = b, a = a }
		DB:Set("groupArrowColors", groupArrowColors)

		if group then
			group.arrowColor = { r = r, g = g, b = b, a = a }
		end

		BFL:ForceRefreshFriendsList()
		return
	end

	local Groups = GetGroups()
	local r, g, b = 1, 1, 1

	if Groups then
		local group = Groups:Get(groupId)
		if group then
			if group.arrowColor then
				r, g, b = group.arrowColor.r, group.arrowColor.g, group.arrowColor.b
			elseif group.color then
				r, g, b = group.color.r, group.color.g, group.color.b
			end
		end
	end

	-- Robust Cleanup
	ColorPickerFrame:Hide()
	ColorPickerFrame.func = nil
	ColorPickerFrame.opacityFunc = nil
	ColorPickerFrame.cancelFunc = nil

	-- Fix #34: Get existing alpha value for arrow color
	local a = 1.0
	if Groups then
		local group = Groups:Get(groupId)
		if group and group.arrowColor and group.arrowColor.a then
			a = group.arrowColor.a
		elseif group and group.color and group.color.a then
			a = group.color.a -- Inherit from group color
		end
	end

	local info = {}
	info.r = r
	info.g = g
	info.b = b
	info.opacity = a
	info.hasOpacity = true -- Fix #34: Enable alpha slider
	info.swatchFunc = function()
		local newR, newG, newB = ColorPickerFrame:GetColorRGB()
		local newA = ColorPickerFrame:GetColorAlpha() or 1

		colorSwatch:SetColorTexture(newR, newG, newB, newA)

		local groupArrowColors = DB:Get("groupArrowColors") or {}
		groupArrowColors[groupId] = { r = newR, g = newG, b = newB, a = newA }
		DB:Set("groupArrowColors", groupArrowColors)

		local Groups = GetGroups()
		if Groups then
			local group = Groups:Get(groupId)
			if group then
				group.arrowColor = { r = newR, g = newG, b = newB, a = newA }
			end
		end

		BFL:ForceRefreshFriendsList()
	end
	info.opacityFunc = info.swatchFunc -- Fix #34: Update on alpha slider change
	info.cancelFunc = function(previousValues)
		local prevA = previousValues.a or 1
		colorSwatch:SetColorTexture(previousValues.r, previousValues.g, previousValues.b, prevA)

		local groupArrowColors = DB:Get("groupArrowColors") or {}
		groupArrowColors[groupId] = { r = previousValues.r, g = previousValues.g, b = previousValues.b, a = prevA }
		DB:Set("groupArrowColors", groupArrowColors)

		local Groups = GetGroups()
		if Groups then
			local group = Groups:Get(groupId)
			if group then
				group.arrowColor = { r = previousValues.r, g = previousValues.g, b = previousValues.b, a = prevA }
			end
		end

		BFL:ForceRefreshFriendsList()
	end

	if ColorPickerFrame.SetupColorPickerAndShow then
		ColorPickerFrame:SetupColorPickerAndShow(info)
		-- Fix for sticky color wheel state
		if ColorPickerFrame.SetColorRGB then
			ColorPickerFrame:SetColorRGB(info.r, info.g, info.b)
		end
	else
		ColorPickerFrame.func = info.swatchFunc
		ColorPickerFrame.cancelFunc = info.cancelFunc
		ColorPickerFrame.opacityFunc = nil
		ColorPickerFrame.hasOpacity = info.hasOpacity
		ColorPickerFrame.opacity = info.opacity
		ColorPickerFrame:SetColorRGB(info.r, info.g, info.b)
		ColorPickerFrame:Show()
	end
end

function Settings:ShowColorPicker(groupId, groupName, colorSwatch)
	local DB = GetDB()
	if not DB then
		return
	end

	local Groups = GetGroups()
	local r, g, b = 1, 1, 1

	if Groups then
		local group = Groups:Get(groupId)
		if group and group.color then
			r, g, b = group.color.r, group.color.g, group.color.b
		else
			r, g, b = 1, 0.82, 0
		end
	end

	-- Robust Cleanup
	ColorPickerFrame:Hide()
	ColorPickerFrame.func = nil
	ColorPickerFrame.opacityFunc = nil
	ColorPickerFrame.cancelFunc = nil

	-- Fix #34: Get existing alpha value
	local a = 1.0
	if Groups then
		local group = Groups:Get(groupId)
		if group and group.color and group.color.a then
			a = group.color.a
		end
	end

	local info = {}
	info.r = r
	info.g = g
	info.b = b
	info.opacity = a
	info.hasOpacity = true -- Fix #34: Enable alpha slider
	info.swatchFunc = function()
		local newR, newG, newB = ColorPickerFrame:GetColorRGB()
		local newA = ColorPickerFrame:GetColorAlpha() or 1

		colorSwatch:SetColorTexture(newR, newG, newB, newA)

		local groupColors = DB:Get("groupColors") or {}
		groupColors[groupId] = { r = newR, g = newG, b = newB, a = newA }
		DB:Set("groupColors", groupColors)

		local Groups = GetGroups()
		if Groups then
			local group = Groups:Get(groupId)
			if group then
				group.color = { r = newR, g = newG, b = newB, a = newA }
			end
		end

		-- Force full display refresh for immediate color update
		BFL:ForceRefreshFriendsList()
	end
	info.opacityFunc = info.swatchFunc -- Fix #34: Update on alpha slider change
	info.cancelFunc = function(previousValues)
		local prevA = previousValues.a or 1
		colorSwatch:SetColorTexture(previousValues.r, previousValues.g, previousValues.b, prevA)

		local groupColors = DB:Get("groupColors") or {}
		groupColors[groupId] = { r = previousValues.r, g = previousValues.g, b = previousValues.b, a = prevA }
		DB:Set("groupColors", groupColors)

		local Groups = GetGroups()
		if Groups then
			local group = Groups:Get(groupId)
			if group then
				group.color = { r = previousValues.r, g = previousValues.g, b = previousValues.b, a = prevA }
			end
		end

		-- Force full display refresh for immediate color update
		BFL:ForceRefreshFriendsList()
	end

	if ColorPickerFrame.SetupColorPickerAndShow then
		ColorPickerFrame:SetupColorPickerAndShow(info)
		-- Fix for sticky color wheel state
		if ColorPickerFrame.SetColorRGB then
			ColorPickerFrame:SetColorRGB(info.r, info.g, info.b)
		end
	else
		ColorPickerFrame.func = info.swatchFunc
		ColorPickerFrame.cancelFunc = info.cancelFunc
		ColorPickerFrame.opacityFunc = nil
		ColorPickerFrame.hasOpacity = info.hasOpacity
		ColorPickerFrame.opacity = info.opacity
		ColorPickerFrame:SetColorRGB(info.r, info.g, info.b)
		ColorPickerFrame:Show()
	end
end

-- Delete a custom group
function Settings:DeleteGroup(groupId, groupName)
	StaticPopupDialogs["BETTERFRIENDLIST_DELETE_GROUP"] = {
		text = string.format(L.DIALOG_DELETE_GROUP_SETTINGS, groupName),
		button1 = L.DIALOG_DELETE_GROUP_BTN1,
		button2 = L.DIALOG_DELETE_GROUP_BTN2,
		OnAccept = function()
			local Groups = GetGroups()
			local DB = GetDB()

			if not Groups or not DB then
				BFL:DebugPrint("|cffff0000BetterFriendlist:|r " .. L.ERROR_FAILED_DELETE_GROUP)
				return
			end

			local success, err = Groups:Delete(groupId)
			if success then
				BFL:DebugPrint("|cff00ff00BetterFriendlist:|r " .. string.format(L.MSG_GROUP_DELETED, groupName))

				Settings:RefreshGroupList()

				-- Force full display refresh - groups affect display list structure
				BFL:ForceRefreshFriendsList()
			else
				BFL:DebugPrint(
					"|cffff0000BetterFriendlist:|r " .. string.format(L.ERROR_FAILED_DELETE, (err or L.UNKNOWN_ERROR))
				)
			end
		end,
		timeout = 0,
		whileDead = true,
		hideOnEscape = true,
		preferredIndex = 3,
	}
	StaticPopup_Show("BETTERFRIENDLIST_DELETE_GROUP")
end

-- Rename a custom group
function Settings:RenameGroup(groupId, currentName)
	-- Use the centralized rename dialog from Dialogs.lua which has full validation support.
	-- Pass groupId as data (4th arg) so OnShow/OnAccept can use it.
	StaticPopup_Show("BETTER_FRIENDLIST_RENAME_GROUP", nil, nil, groupId)
end

-- Migrate from FriendGroups addon
function Settings:MigrateFriendGroups(cleanupNotes, force)
	local DB = GetDB()
	local Groups = GetGroups()

	if not DB or not Groups then
		-- BFL:DebugPrint("|cffff0000BetterFriendlist:|r Migration failed - modules not loaded!")
		return
	end

	-- Check if migration has already been completed
	if not force and DB:Get("friendGroupsMigrated") then
		BFL:DebugPrint("|cff00ffffBetterFriendlist:|r " .. L.MSG_MIGRATION_ALREADY_DONE)
		return
	end

	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r " .. L.MSG_MIGRATION_STARTING)
	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r DB module:", DB and "OK" or "MISSING")
	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Groups module:", Groups and "OK" or "MISSING")

	local migratedFriends = 0
	local migratedGroups = {}
	local groupNameMap = {}
	local assignmentCount = 0
	local allGroupNames = {}
	local friendGroupAssignments = {}

	-- PHASE 1: Collect all group names from all friends
	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Phase 1 - Collecting group names...")

	local numBNetFriends = BNGetNumFriends()
	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Scanning", numBNetFriends, "BattleNet friends...")

	for i = 1, numBNetFriends do
		local accountInfo = C_BattleNet.GetFriendAccountInfo(i)
		if accountInfo then
			local battleTag = accountInfo.battleTag
			local noteText = accountInfo.note

			if noteText and noteText ~= "" then
				local actualNote, friendGroups = ParseFriendGroupsNote(noteText)

				if #friendGroups > 0 then
					-- Use battleTag as persistent identifier (bnetAccountID is temporary per session)
					local friendUID = "bnet_" .. (battleTag or tostring(accountInfo.bnetAccountID))
					friendGroupAssignments[friendUID] = {
						groups = friendGroups,
						actualNote = actualNote,
						isBNet = true,
						battleTag = battleTag,
					}

					for _, groupName in ipairs(friendGroups) do
						if groupName ~= "[Favorites]" and groupName ~= "[No Group]" and groupName ~= "" then
							allGroupNames[groupName] = true
						end
					end
				end
			end
		end
	end

	local numWoWFriends = C_FriendList.GetNumFriends() or 0
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Scanning", numWoWFriends, "WoW friends...")

	for i = 1, numWoWFriends do
		local info = C_FriendList.GetFriendInfoByIndex(i)
		if info then
			-- Normalize name to always include realm
			local name = BFL:NormalizeWoWFriendName(info.name)

			-- Debug Logs for WoW Friend Migration
			if name then
				BFL:DebugPrint("  Found WoW friend:", name, "Note:", info.notes)
				local noteText = info.notes

				if noteText and noteText ~= "" then
					local actualNote, friendGroups = ParseFriendGroupsNote(noteText)

					BFL:DebugPrint("    Parsed Note:", actualNote, "Groups Found:", #friendGroups)

					if #friendGroups > 0 then
						local friendUID = "wow_" .. name
						BFL:DebugPrint("    Assigning to Groups. UID:", friendUID)

						friendGroupAssignments[friendUID] = {
							groups = friendGroups,
							actualNote = actualNote,
							isBNet = false,
							characterName = name,
						}

						for _, groupName in ipairs(friendGroups) do
							BFL:DebugPrint("      Group:", groupName)
							if groupName ~= "[Favorites]" and groupName ~= "[No Group]" and groupName ~= "" then
								allGroupNames[groupName] = true
							end
						end
					end
				else
					BFL:DebugPrint("    No note found or empty.")
				end
			else
				BFL:DebugPrint("  Name normalization failed for:", info.name)
			end
		end
	end

	-- PHASE 2: Create groups in alphabetical order
	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Phase 2 - Creating groups in alphabetical order...")

	local sortedGroupNames = {}
	for groupName in pairs(allGroupNames) do
		table.insert(sortedGroupNames, groupName)
	end
	table.sort(sortedGroupNames)

	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Creating", #sortedGroupNames, "groups...")

	local currentOrder = 2
	local groupOrderArray = { "favorites" }

	for _, groupName in ipairs(sortedGroupNames) do
		-- First: Check if a group with this name already exists (handles renamed groups too)
		local existingGroupId = Groups:GetGroupIdByName(groupName)
		if existingGroupId then
			groupNameMap[groupName] = existingGroupId
			migratedGroups[groupName] = true
			table.insert(groupOrderArray, existingGroupId)
			BFL:DebugPrint("|cffffff00BetterFriendlist:|r   [!] Existing:", groupName, "(using existing group)")
			currentOrder = currentOrder + 1
		else
			-- Group doesn't exist by name - create it
			local success, groupId = Groups:CreateWithOrder(groupName, currentOrder)
			if success and groupId then
				groupNameMap[groupName] = groupId
				migratedGroups[groupName] = true
				table.insert(groupOrderArray, groupId)
				BFL:DebugPrint("|cff00ff00BetterFriendlist:|r   Created:", groupName, "(order:", currentOrder, ")")
				currentOrder = currentOrder + 1
			else
				BFL:DebugPrint("|cffff0000BetterFriendlist:|r   FAILED:", groupName, "-", tostring(groupId))
			end
		end
	end

	table.insert(groupOrderArray, "nogroup")

	DB:Set("groupOrder", groupOrderArray)
	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Group order saved:", table.concat(groupOrderArray, ", "))

	-- PHASE 3: Assign friends to groups
	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Phase 3 - Assigning friends to groups...")

	for friendUID, data in pairs(friendGroupAssignments) do
		migratedFriends = migratedFriends + 1

		for _, groupName in ipairs(data.groups) do
			if groupName ~= "[Favorites]" and groupName ~= "[No Group]" and groupName ~= "" then
				local groupId = groupNameMap[groupName]
				if groupId then
					local success = DB:AddFriendToGroup(friendUID, groupId)
					if success then
						assignmentCount = assignmentCount + 1
					end
				end
			end
		end
	end

	-- PHASE 4: Clean up notes if requested
	if cleanupNotes then
		-- BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Phase 4 - Cleaning up notes...")

		for friendUID, data in pairs(friendGroupAssignments) do
			if data.isBNet then
				-- Find bnetAccountID by battleTag (needed for BNSetFriendNote)
				local bnetAccountID = nil
				for i = 1, BNGetNumFriends() do
					local accountInfo = C_BattleNet.GetFriendAccountInfo(i)
					if accountInfo and accountInfo.battleTag == data.battleTag then
						bnetAccountID = accountInfo.bnetAccountID
						break
					end
				end

				if bnetAccountID then
					if data.actualNote ~= "" then
						BNSetFriendNote(bnetAccountID, data.actualNote)
					else
						BNSetFriendNote(bnetAccountID, "")
					end
				end
			else
				if data.actualNote ~= "" then
					C_FriendList.SetFriendNotes(data.characterName, data.actualNote)
				else
					C_FriendList.SetFriendNotes(data.characterName, "")
				end
			end
		end
	end

	local numGroups = 0
	for _ in pairs(migratedGroups) do
		numGroups = numGroups + 1
	end

	local cleanupMsg = cleanupNotes and ("\n|cff00ff00" .. L.SETTINGS_NOTES_CLEANED .. "|r")
		or ("\n|cffffff00" .. L.SETTINGS_NOTES_PRESERVED .. "|r")

	-- Mark migration as completed
	DB:Set("friendGroupsMigrated", true)

	BFL:DebugPrint(
		"|cff00ff00═══════════════════════════════════════"
	)
	BFL:DebugPrint("|cff00ff00BetterFriendlist: " .. L.SETTINGS_MIGRATION_COMPLETE)
	BFL:DebugPrint(
		"|cff00ff00═══════════════════════════════════════"
	)
	BFL:DebugPrint(
		string.format(
			"|cff00ffff%s|r %d\n" .. "|cff00ffff%s|r %d\n" .. "|cff00ffff%s|r %d%s",
			L.SETTINGS_MIGRATION_FRIENDS,
			migratedFriends,
			L.SETTINGS_MIGRATION_GROUPS,
			numGroups,
			L.SETTINGS_MIGRATION_ASSIGNMENTS,
			assignmentCount,
			cleanupMsg
		)
	)

	BFL:DebugPrint("|cff00ffffBetterFriendlist:|r Refreshing friends list...")
	-- Force full refresh - migration creates new groups which affect display list structure
	BFL:ForceRefreshFriendsList()
	BFL:DebugPrint("|cff00ff00BetterFriendlist:|r Friends list refreshed!")
end

-- Show migration dialog
function Settings:ShowMigrationDialog()
	StaticPopupDialogs["BETTERFRIENDLIST_MIGRATE_FRIENDGROUPS"] = {
		text = L.DIALOG_MIGRATE_TEXT,
		button1 = L.DIALOG_MIGRATE_BTN1,
		button2 = L.DIALOG_MIGRATE_BTN2,
		button3 = L.DIALOG_MIGRATE_BTN3,
		OnAccept = function()
			Settings:MigrateFriendGroups(false, true)
			BFL.NoteCleanupWizard:Show()
		end,
		OnCancel = function()
			Settings:MigrateFriendGroups(false, true)
		end,
		timeout = 0,
		whileDead = true,
		hideOnEscape = true,
		preferredIndex = 3,
	}

	StaticPopup_Show("BETTERFRIENDLIST_MIGRATE_FRIENDGROUPS")
end

-- Debug: Print database contents
function Settings:DebugDatabase()
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r =================================")
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r DATABASE STATE")
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r =================================")

	if not BetterFriendlistDB then
		-- BFL:DebugPrint("|cffff0000BetterFriendlist Debug:|r BetterFriendlistDB is NIL!")
		return
	end

	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r")
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r CUSTOM GROUPS:")
	if BetterFriendlistDB.customGroups then
		local groupCount = 0
		for groupId, groupInfo in pairs(BetterFriendlistDB.customGroups) do
			groupCount = groupCount + 1
			-- BFL:DebugPrint(string.format("|cff00ffffBetterFriendlist Debug:|r   [%s] = %s", groupId, groupInfo.name or "UNNAMED"))
		end
		-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r Total groups:", groupCount)
	else
		-- BFL:DebugPrint("|cffff0000BetterFriendlist Debug:|r customGroups table MISSING!")
	end

	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r")
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r FRIEND ASSIGNMENTS:")
	if BetterFriendlistDB.friendGroups then
		local friendCount = 0
		local totalAssignments = 0
		local assignedFriends = {}

		for friendUID, groups in pairs(BetterFriendlistDB.friendGroups) do
			-- Filter out non-string keys before concat
			local stringKeys = {}
			for _, v in ipairs(groups) do
				if type(v) == "string" then
					table.insert(stringKeys, v)
				end
			end

			-- Only show friends that have actual group assignments
			if #stringKeys > 0 then
				friendCount = friendCount + 1
				totalAssignments = totalAssignments + #stringKeys
				table.insert(
					assignedFriends,
					string.format(
						"|cff00ffffBetterFriendlist Debug:|r   %s -> [%s]",
						friendUID,
						table.concat(stringKeys, ", ")
					)
				)
			end
		end

		-- Sort and display
		table.sort(assignedFriends)
		for _, line in ipairs(assignedFriends) do
			-- BFL:DebugPrint(line)
		end

		if friendCount == 0 then
			-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r   (No friends assigned to custom groups)")
		end
		-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r Total friends with assignments:", friendCount)
		-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r Total assignments:", totalAssignments)
	else
		-- BFL:DebugPrint("|cffff0000BetterFriendlist Debug:|r friendGroups table MISSING!")
	end

	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r")
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r GROUP ORDER:")
	if BetterFriendlistDB.groupOrder then
		-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r", table.concat(BetterFriendlistDB.groupOrder, ", "))
	else
		-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r Using default order (nil)")
	end

	-- Show current friends list
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r")
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r CURRENT FRIENDS:")

	-- Battle.net friends
	local numBNetTotal, numBNetOnline = BNGetNumFriends()
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r Battle.net Friends:", numBNetTotal, string.format("(%d online)", numBNetOnline))
	if numBNetTotal > 0 then
		for i = 1, math.min(numBNetTotal, 10) do
			local accountInfo = C_BattleNet.GetFriendAccountInfo(i)
			if accountInfo then
				local status = accountInfo.gameAccountInfo
						and accountInfo.gameAccountInfo.isOnline
						and "|cff00ff00ONLINE|r"
					or "|cffaaaaaaOFFLINE|r"
				local name = BFL:GetSafeAccountName(accountInfo.accountName, accountInfo.battleTag)
				-- BFL:DebugPrint(string.format("|cff00ffffBetterFriendlist Debug:|r   [%d] %s - %s", i, name, status))
			end
		end
		if numBNetTotal > 10 then
			-- BFL:DebugPrint(string.format("|cff00ffffBetterFriendlist Debug:|r   ... and %d more", numBNetTotal - 10))
		end
	end

	-- WoW friends
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r")
	local numWoWFriends = C_FriendList.GetNumFriends() or 0
	local numWoWOnline = C_FriendList.GetNumOnlineFriends()
	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r WoW Friends:", numWoWFriends, string.format("(%d online)", numWoWOnline))
	if numWoWFriends > 0 then
		for i = 1, math.min(numWoWFriends, 10) do
			local friendInfo = C_FriendList.GetFriendInfoByIndex(i)
			if friendInfo then
				local status = friendInfo.connected and "|cff00ff00ONLINE|r" or "|cffaaaaaaOFFLINE|r"
				local level = friendInfo.level and friendInfo.level > 0 and string.format(" (Lvl %d)", friendInfo.level)
					or ""
				-- BFL:DebugPrint(string.format("|cff00ffffBetterFriendlist Debug:|r   [%d] %s%s - %s", i, friendInfo.name, level, status))
			end
		end
		if numWoWFriends > 10 then
			-- BFL:DebugPrint(string.format("|cff00ffffBetterFriendlist Debug:|r   ... and %d more", numWoWFriends - 10))
		end
	end

	-- BFL:DebugPrint("|cff00ffffBetterFriendlist Debug:|r =================================")
end

--------------------------------------------------------------------------
-- EXPORT / IMPORT SYSTEM
--------------------------------------------------------------------------

-- Export all settings to a string
function Settings:ExportSettings()
	if not BetterFriendlistDB then
		return nil, L.ERROR_DB_NOT_AVAILABLE
	end

	-- Deep copy the DB to ensure we capture EVERYTHING
	-- This fulfills the requirement to export future settings automatically
	local DB = GetDB()
	local exportData = DB:InternalDeepCopy(BetterFriendlistDB)

	-- Tag with export version 3 (Base64 + Full DB)
	exportData.exportVersion = 3

	-- Serialize to string using manual encoding
	local serialized = self:SerializeTable(exportData)

	if not serialized then
		return nil, L.ERROR_EXPORT_SERIALIZE
	end

	-- Encode to Base64 (using BFL3: prefix)
	-- Used C_EncodingUtil.EncodeBase64 if available as requested
	local encoded = self:EncodeString(serialized)

	return encoded, nil
end

-- Import settings from string
function Settings:ImportSettings(importString)
	if not importString or importString == "" then
		return false, L.ERROR_IMPORT_EMPTY
	end

	-- Decode
	local decoded, version = self:DecodeString(importString)
	if not decoded then
		return false, L.ERROR_IMPORT_DECODE
	end

	-- Deserialize
	local importData = self:DeserializeTable(decoded)
	if not importData then
		return false, L.ERROR_IMPORT_DESERIALIZE
	end

	-- IMPORT DATA
	local DB = GetDB()
	local Groups = GetGroups()

	if not DB or not Groups then
		return false, L.ERROR_MODULES_NOT_LOADED
	end

	-- Handle V3 (Everything)
	if importData.exportVersion and importData.exportVersion >= 3 then
		BFL:DebugPrint("|cff00ff00BetterFriendlist:|r Importing V3 (Full) backup...")

		-- Wipe the entire DB first, then apply import data.
		-- This ensures keys that existed in the live DB but NOT in the export
		-- (e.g., newly created groups, settings added after the export) are removed.
		wipe(BetterFriendlistDB)

		-- Import ALL keys from the export
		for key, value in pairs(importData) do
			-- Skip metadata
			if key ~= "exportVersion" and key ~= "version" then
				BetterFriendlistDB[key] = value
			end
		end

		-- Explicitly handle version to prevent mismatches
		-- We keep the current addon version in DB, not the one from export,
		-- to trigger migration logic if needed on next reload
		BetterFriendlistDB.version = BFL.VERSION
	else
		-- Legacy V1/V2 Import Logic
		BFL:DebugPrint("|cff00ff00BetterFriendlist:|r Importing V1/V2 (Legacy) backup...")

		-- Validate structure (Relaxed for V1/V2 as they might miss some keys but must have core ones)
		if not importData.customGroups or not importData.friendGroups then
			return false, L.ERROR_EXPORT_STRUCTURE
		end

		-- Clear existing data (Common to v1 and v2)
		BetterFriendlistDB.customGroups = {}
		BetterFriendlistDB.friendGroups = {}
		BetterFriendlistDB.groupOrder = {}
		BetterFriendlistDB.groupStates = {}

		-- Import custom groups
		if importData.customGroups then
			for groupId, groupInfo in pairs(importData.customGroups) do
				BetterFriendlistDB.customGroups[groupId] = {
					name = groupInfo.name,
					collapsed = groupInfo.collapsed or false,
					order = groupInfo.order or 2,
					color = groupInfo.color or { r = 1.0, g = 0.82, b = 0.0 },
				}
			end
		end

		-- Import friend assignments
		if importData.friendGroups then
			for friendUID, groups in pairs(importData.friendGroups) do
				BetterFriendlistDB.friendGroups[friendUID] = {}
				for _, groupId in ipairs(groups) do
					table.insert(BetterFriendlistDB.friendGroups[friendUID], groupId)
				end
			end
		end

		-- Import group order
		if importData.groupOrder then
			for _, groupId in ipairs(importData.groupOrder) do
				table.insert(BetterFriendlistDB.groupOrder, groupId)
			end
		end

		-- Import group states
		if importData.groupStates then
			for groupId, collapsed in pairs(importData.groupStates) do
				BetterFriendlistDB.groupStates[groupId] = collapsed
			end
		end

		-- Import group colors (user-customized colors)
		if importData.groupColors then
			BetterFriendlistDB.groupColors = {}
			for groupId, color in pairs(importData.groupColors) do
				if color and color.r and color.g and color.b then
					BetterFriendlistDB.groupColors[groupId] = {
						r = color.r,
						g = color.g,
						b = color.b,
					}
				end
			end
		end

		-- Version 2: Import all other settings
		if importData.version and importData.version >= 2 then
			local keysToImport = {
				-- General
				"compactMode",
				"theme",
				"darkThemeSettings",
				"customTheme",
				"enableElvUISkin",
				"fontSize",
				"windowScale",
				"hideMaxLevel",
				"accordionGroups",
				"showFavoritesGroup",
				"colorClassNames",
				"hideEmptyGroups",
				"headerCountFormat",
				"groupHeaderAlign",
				"showGroupArrow",
				"groupArrowAlign",
				"showFactionIcons",
				"showRealmName",
				"grayOtherFaction",
				"showMobileAsAFK",
				"treatMobileAsOffline",
				"nameDisplayFormat",
				"enableInGameGroup",
				"inGameGroupMode",
				-- Fonts
				"fontFriendName",
				"fontSizeFriendName",
				"fontOutlineFriendName",
				"fontShadowFriendName",
				"fontColorFriendName",
				"fontFriendInfo",
				"fontSizeFriendInfo",
				"fontOutlineFriendInfo",
				"fontShadowFriendInfo",
				"fontColorFriendInfo",
				"fontTabText",
				"fontSizeTabText",
				"fontOutlineTabText",
				"fontShadowTabText",
				"fontRaidName",
				"fontSizeRaidName",
				"fontOutlineRaidName",
				"fontShadowRaidName",
				"fontGroupHeader",
				"fontSizeGroupHeader",
				"fontOutlineGroupHeader",
				"fontShadowGroupHeader",
				"colorGroupCount",
				"colorGroupArrow",
				"brokerFont",
				"brokerFontSize",
				"brokerFontFlags",
				"brokerUseCustomFontForNonLatin",
				-- Sort & Filter
				"quickFilter",
				"primarySort",
				"secondarySort",
				"customQuickFilters",
				"quickFilterVisibility",
				"quickFilterOrder",
				"nextCustomQuickFilterId",
				"customSorters",
				"sorterVisibility",
				"sorterOrder",
				"nextCustomSorterId",
				-- Guild Tab
				"enableGuildTab",
				"guildTabFilterMode",
				"guildTabSortMode",
				"guildTabShowClassIcons",
				"guildTabUseNicknames",
				-- Broker
				"brokerEnabled",
				"brokerShowIcon",
				"brokerShowLabel",
				"brokerShowTotal",
				"brokerShowGroups",
				"brokerTooltipMode",
				"brokerClickAction",
				"brokerShowHints",
				-- Sync
				"enableGlobalSync",
				"enableGlobalSyncDeletion",
				-- Other
				"nicknames",
				"enableBetaFeatures",
			}

			for _, key in ipairs(keysToImport) do
				if importData[key] ~= nil then
					BetterFriendlistDB[key] = importData[key]
				end
			end
		end
	end

	if importData.theme == nil and importData.enableElvUISkin ~= nil then
		BetterFriendlistDB.theme = importData.enableElvUISkin == true and "elvui" or "blizzard"
	end
	if DB.NormalizeThemeSetting then
		DB:NormalizeThemeSetting()
	end
	if DB.NormalizeThemeSettings then
		DB:NormalizeThemeSettings()
	end
	local Registry = BFL:GetModule("FilterSortRegistry")
	if Registry and Registry.NormalizeDB then
		Registry:NormalizeDB()
		Registry:NormalizeCurrentSelections()
	end

	-- Reload Groups module (this will apply imported colors)
	Groups:Initialize()

	-- Invalidate caches after direct DB writes
	if BFL.SettingsVersion then
		BFL.SettingsVersion = BFL.SettingsVersion + 1
	end
	local FriendsList = BFL:GetModule("FriendsList")
	if FriendsList then
		if FriendsList.InvalidateSettingsCache then
			FriendsList:InvalidateSettingsCache()
		end
		FriendsList.lastBuildInputs = nil
	end

	-- Sync Streamer Mode visual state (button icon, header text) with imported DB
	local StreamerMode = BFL:GetModule("StreamerMode")
	if StreamerMode then
		StreamerMode:UpdateState()
	end

	-- Force full display refresh - import affects groups and display structure
	BFL:ForceRefreshFriendsList()

	return true, nil
end

-- Simple table serialization (Lua table to string)
function Settings:SerializeTable(tbl)
	local function serialize(val, depth)
		depth = depth or 0
		if depth > UI.MAX_RECURSION_DEPTH then
			return "nil"
		end -- Prevent infinite recursion

		local t = type(val)
		if t == "number" then
			return tostring(val)
		elseif t == "boolean" then
			return val and "true" or "false"
		elseif t == "string" then
			return string.format("%q", val)
		elseif t == "table" then
			local parts = {}
			table.insert(parts, "{")
			for k, v in pairs(val) do
				local key = type(k) == "string" and string.format("[%q]", k) or string.format("[%s]", tostring(k))
				table.insert(parts, key .. "=" .. serialize(v, depth + 1) .. ",")
			end
			table.insert(parts, "}")
			return table.concat(parts)
		else
			return "nil"
		end
	end

	return serialize(tbl)
end

-- Deserialize string to table
function Settings:DeserializeTable(str)
	if not str or str == "" then
		return nil
	end

	-- Security: Use setfenv to sandbox the loaded code instead of fragile character whitelisting.
	-- setfenv(func, {}) removes access to ALL globals (os, print, loadstring, etc.),
	-- making code injection harmless. Table literals only need Lua keywords (true/false/nil)
	-- which work without any environment.
	local func, err = loadstring("return " .. str)
	if not func then
		BFL:DebugPrint("DeserializeTable: loadstring failed:", err)
		return nil
	end

	-- Sandbox: empty environment prevents access to any global functions or variables
	setfenv(func, {})

	-- Execute in protected call to catch runtime errors
	local success, result = pcall(func)
	if not success then
		BFL:DebugPrint("DeserializeTable: pcall failed:", tostring(result))
		return nil
	end

	-- Validate result is a table (not a string, number, or other type from crafted input)
	if type(result) ~= "table" then
		BFL:DebugPrint("DeserializeTable: Result is not a table:", type(result))
		return nil
	end

	return result
end

-- Encode string to base64 or hex format
function Settings:EncodeString(str)
	-- V3: Use Base64 (C_EncodingUtil or fallback)
	-- This is much more efficient than hex string
	-- and fulfills the requirement to use C_EncodingUtil
	local encoded = BFL:Base64Encode(str)
	if encoded then
		return "BFL3:" .. encoded
	end

	-- Fallback to Hex (Legacy V1/V2 style) if Base64 fails
	local hexParts = {}
	for i = 1, #str do
		table.insert(hexParts, string.format("%02x", string.byte(str, i)))
	end
	return "BFL1:" .. table.concat(hexParts)
end

-- Decode format back to string
function Settings:DecodeString(encoded)
	if not encoded then
		return nil
	end

	-- V3: Base64
	if string.match(encoded, "^BFL3:") then
		local b64 = string.sub(encoded, 6)
		local decoded = BFL:Base64Decode(b64)
		return decoded, 3
	end

	-- V1/V2: Hex
	if string.match(encoded, "^BFL1:") then
		local hex = string.sub(encoded, 6)
		local chars = {}
		for i = 1, #hex, 2 do
			local byte = tonumber(string.sub(hex, i, i + 1), 16)
			if not byte then
				return nil
			end
			table.insert(chars, string.char(byte))
		end
		return table.concat(chars), 1
	end

	-- Try raw base64 (if user copied without prefix?)
	local tryB64 = BFL:Base64Decode(encoded)
	if tryB64 then
		return tryB64, 3
	end

	return nil
end

-- Show export dialog with scrollable text
function Settings:ShowExportDialog()
	local exportString, err = self:ExportSettings()

	if not exportString then
		BFL:DebugPrint("Export failed:", err or "Unknown error")
		return
	end

	-- Create or reuse export frame
	if not self.exportFrame then
		self:CreateExportFrame()
	end

	-- Set text and show
	self.exportFrame:Show()
	self.exportFrame.scrollFrame.editBox:SetText(exportString)
	self.exportFrame.scrollFrame.editBox:HighlightText()
	self.exportFrame.scrollFrame.editBox:SetFocus()

	BFL:DebugPrint("Export complete! Copy the text from the dialog.")
end

-- Show import dialog
function Settings:ShowImportDialog()
	-- Create or reuse import frame
	if not self.importFrame then
		self:CreateImportFrame()
	end

	-- Clear and show
	self.importFrame:Show()
	self.importFrame.scrollFrame.editBox:SetText("")
	self.importFrame.scrollFrame.editBox:SetFocus()
end

-- Create export frame
function Settings:CreateExportFrame()
	local frame = CreateFrame("Frame", "BetterFriendlistExportFrame", UIParent, "BasicFrameTemplateWithInset")
	frame:SetSize(500, 400)
	frame:SetPoint("CENTER")
	frame:SetFrameStrata("DIALOG")
	frame:EnableMouse(true)
	frame:SetMovable(true)
	frame:RegisterForDrag("LeftButton")
	frame:SetScript("OnDragStart", frame.StartMoving)
	frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
	frame:Hide()

	-- Title
	frame.title = frame:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlight")
	frame.title:SetPoint("TOP", 0, -5)
	frame.title:SetText(L.SETTINGS_EXPORT_TITLE)

	-- Info text
	frame.info = frame:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormal")
	frame.info:SetPoint("TOPLEFT", 15, -30)
	frame.info:SetPoint("TOPRIGHT", -15, -30)
	frame.info:SetJustifyH("LEFT")
	frame.info:SetText(L.SETTINGS_EXPORT_INFO)

	-- Scroll frame with edit box
	local scrollFrame = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
	scrollFrame:SetPoint("TOPLEFT", UI.SPACING_MEDIUM, -60)
	scrollFrame:SetPoint("BOTTOMRIGHT", -30, 50)
	frame.scrollFrame = scrollFrame

	local editBox = CreateFrame("EditBox", nil, scrollFrame)
	editBox:SetMultiLine(true)
	editBox:SetFontObject(BetterFriendlistFontHighlight)
	editBox:SetWidth(460)
	editBox:SetAutoFocus(false)
	editBox:SetScript("OnEscapePressed", function(self)
		frame:Hide()
	end)
	editBox:SetScript("OnKeyUp", function(self, key)
		if IsControlKeyDown() and key == "C" then
			frame:Hide()
		end
	end)
	scrollFrame:SetScrollChild(editBox)
	scrollFrame.editBox = editBox

	-- Copy All button
	local copyButton = CreateFrame("Button", nil, frame, "GameMenuButtonTemplate")
	copyButton:SetPoint("BOTTOM", 0, 15)
	copyButton:SetSize(120, 25)
	copyButton:SetText(L.SETTINGS_EXPORT_BTN)
	copyButton:SetNormalFontObject("BetterFriendlistFontNormal")
	copyButton:SetHighlightFontObject("BetterFriendlistFontHighlight")
	copyButton:SetScript("OnClick", function()
		editBox:HighlightText()
		editBox:SetFocus()
	end)

	self.exportFrame = frame
end

-- Create import frame
function Settings:CreateImportFrame()
	local frame = CreateFrame("Frame", "BetterFriendlistImportFrame", UIParent, "BasicFrameTemplateWithInset")
	frame:SetSize(500, 400)
	frame:SetPoint("CENTER")
	frame:SetFrameStrata("DIALOG")
	frame:EnableMouse(true)
	frame:SetMovable(true)
	frame:RegisterForDrag("LeftButton")
	frame:SetScript("OnDragStart", frame.StartMoving)
	frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
	frame:Hide()

	-- Title
	frame.title = frame:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlight")
	frame.title:SetPoint("TOP", 0, -5)
	frame.title:SetText(L.SETTINGS_IMPORT_TITLE)

	-- Info text
	frame.info = frame:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormal")
	frame.info:SetPoint("TOPLEFT", 15, -30)
	frame.info:SetPoint("TOPRIGHT", -15, -30)
	frame.info:SetJustifyH("LEFT")
	frame.info:SetText(L.SETTINGS_IMPORT_INFO)

	-- Scroll frame with edit box
	local scrollFrame = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
	scrollFrame:SetPoint("TOPLEFT", UI.SPACING_MEDIUM, -80)
	scrollFrame:SetPoint("BOTTOMRIGHT", -30, 50)
	frame.scrollFrame = scrollFrame

	local editBox = CreateFrame("EditBox", nil, scrollFrame)
	editBox:SetMultiLine(true)
	editBox:SetFontObject(BetterFriendlistFontHighlight)
	editBox:SetWidth(460)
	editBox:SetAutoFocus(true)
	editBox:SetScript("OnEscapePressed", function(self)
		frame:Hide()
	end)
	scrollFrame:SetScrollChild(editBox)
	scrollFrame.editBox = editBox

	-- Import button
	local importButton = CreateFrame("Button", nil, frame, "GameMenuButtonTemplate")
	importButton:SetPoint("BOTTOM", -65, 15)
	importButton:SetSize(120, 25)
	importButton:SetText(L.SETTINGS_IMPORT_BTN)
	importButton:SetNormalFontObject("BetterFriendlistFontNormal")
	importButton:SetHighlightFontObject("BetterFriendlistFontHighlight")
	importButton:SetScript("OnClick", function()
		local importString = editBox:GetText()
		local success, err = Settings:ImportSettings(importString)

		if success then
			BFL:DebugPrint(L.SETTINGS_IMPORT_SUCCESS)
			frame:Hide()
		else
			BFL:DebugPrint(L.SETTINGS_IMPORT_FAILED .. (err or "Unknown error"))
			-- Show error in UI
			StaticPopupDialogs["BETTERFRIENDLIST_IMPORT_ERROR"] = {
				text = L.SETTINGS_IMPORT_FAILED .. (err or "Unknown error"),
				button1 = "OK",
				timeout = 0,
				whileDead = true,
				hideOnEscape = true,
				preferredIndex = 3,
			}
			StaticPopup_Show("BETTERFRIENDLIST_IMPORT_ERROR")
		end
	end)

	-- Cancel button
	local cancelButton = CreateFrame("Button", nil, frame, "GameMenuButtonTemplate")
	cancelButton:SetPoint("BOTTOM", 65, 15)
	cancelButton:SetSize(120, 25)
	cancelButton:SetText(L.SETTINGS_IMPORT_CANCEL)
	cancelButton:SetNormalFontObject("BetterFriendlistFontNormal")
	cancelButton:SetHighlightFontObject("BetterFriendlistFontHighlight")
	cancelButton:SetScript("OnClick", function()
		frame:Hide()
	end)

	self.importFrame = frame
end

--------------------------------------------------------------------------
-- CALLBACK FUNCTIONS (Called from Settings.lua wrapper)
--------------------------------------------------------------------------

-- Callback for CompactMode checkbox
function Settings:OnCompactModeChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("compactMode", checked)

	-- CRITICAL: Force full display refresh to recalculate button heights
	BFL:ForceRefreshFriendsList()
end

-- Callback for ShowBlizzardOption checkbox
function Settings:OnShowBlizzardOptionChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("showBlizzardOption", checked)

	-- Force full display refresh for immediate update
	BFL:ForceRefreshFriendsList()
end

-- Callback for ColorClassNames checkbox
function Settings:OnColorClassNamesChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("colorClassNames", checked)

	-- Force full display refresh for immediate update
	BFL:ForceRefreshFriendsList()
end

-- Callback for HideEmptyGroups checkbox
function Settings:OnHideEmptyGroupsChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("hideEmptyGroups", checked)

	-- Affects which groups are shown - needs display list rebuild
	BFL:ForceRefreshFriendsList()
end

-- Show Faction Icons toggle
function Settings:OnShowFactionIconsChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("showFactionIcons", checked)

	-- Display-only change - re-render existing data
	BFL:ForceRefreshFriendsList()
end

-- Show Realm Name toggle
function Settings:OnShowRealmNameChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("showRealmName", checked)

	-- Display-only change - re-render existing data
	BFL:ForceRefreshFriendsList()
end

-- Show Favorites Group toggle
function Settings:OnShowFavoritesGroupChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("showFavoritesGroup", checked)

	-- Affects which groups are shown - needs display list rebuild
	BFL:ForceRefreshFriendsList()
end

-- Gray Other Faction toggle
function Settings:OnGrayOtherFactionChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("grayOtherFaction", checked)

	-- Display-only change - re-render existing data
	BFL:ForceRefreshFriendsList()
end

-- Show Mobile as AFK toggle
function Settings:OnShowMobileAsAFKChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("showMobileAsAFK", checked)

	-- Display-only change - re-render existing data
	BFL:ForceRefreshFriendsList()
end

-- NEW: Treat Mobile as Offline toggle (Feature Request)
function Settings:OnTreatMobileAsOfflineChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("treatMobileAsOffline", checked)

	-- IMPORTANT: This option affects data LOADING (not just display)
	-- Must force full data reload from API, not just display update
	-- UpdateFriendsList() re-reads all friends from C_BattleNet API
	-- where treatMobileAsOffline logic is applied during data processing
	local FriendsList = BFL and BFL:GetModule("FriendsList")
	if FriendsList and FriendsList.UpdateFriendsList then
		FriendsList:UpdateFriendsList()
	end
end

-- Hide Max Level toggle
function Settings:OnHideMaxLevelChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("hideMaxLevel", checked)

	-- Display-only change - re-render existing data
	BFL:ForceRefreshFriendsList()
end

-- Accordion Groups toggle
function Settings:OnAccordionGroupsChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("accordionGroups", checked)

	-- Collapse all groups to ensure clean state (Requested by User)
	local Groups = BFL:GetModule("Groups")
	if Groups then
		local allGroups = Groups:GetAll()
		for groupId, _ in pairs(allGroups) do
			Groups:SetCollapsed(groupId, true, true)
		end
	end

	-- Display-only change - re-render existing data
	BFL:ForceRefreshFriendsList()
end

-- Use UI Panel System toggle
function Settings:OnUseUIPanelSystemChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	-- Save the pending state temporarily
	DB.pendingUIPanelSystemChange = checked

	-- Show confirmation dialog
	StaticPopup_Show("BFL_CONFIRM_UI_PANEL_RELOAD")
end

-- Friend list whisper click toggle
function Settings:OnFriendListClickWhisperEnabledChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("friendListClickWhisperEnabled", checked and true or false)
	self:RefreshGeneralTab()
end

-- Friend list whisper click mode
function Settings:OnFriendListClickWhisperModeChanged(mode)
	local DB = GetDB()
	if not DB then
		return
	end

	if mode ~= "single" and mode ~= "double" then
		mode = "double"
	end

	DB:Set("friendListClickWhisperMode", mode)
end

-- Simple Mode Toggle
function Settings:OnSimpleModeChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("simpleMode", checked)

	-- Reset top tab to Friends when deactivating Simple Mode
	-- Without this, a stale activeTopTab (e.g. Recent Allies) causes ShowBottomTab
	-- to skip showing ScrollFrame, which breaks hook-driven layout updates
	if not checked and not BFL.IsClassic and BetterFriendsFrame_ShowTab then
		BetterFriendsFrame_ShowTab(1)
	end

	-- Update Main Frame Layout (Show/Hide Tabs) FIRST
	-- This ensures the Frame is on Tab 1 (Friends) and contexts like ScrollFrame visibility are established
	-- BEFORE we try to calculate SearchBox visibility/parenting.
	if BetterFriendsFrame_ShowBottomTab then
		BetterFriendsFrame_ShowBottomTab(1)
	end

	-- Refresh Friends List Layout immediately
	-- Force UpdateScrollBoxExtent explicitly for instant UI feedback (SearchBox position)
	local FriendsList = BFL:GetModule("FriendsList")
	if FriendsList and FriendsList.UpdateScrollBoxExtent then
		FriendsList:UpdateScrollBoxExtent()
		-- Double-tap: Update again on next frame to catch any layout resolution issues (SearchBox)
		C_Timer.After(0.01, function()
			if FriendsList and FriendsList.UpdateScrollBoxExtent then
				FriendsList:UpdateScrollBoxExtent()
			end
			if BFL.UpdatePortraitVisibility then
				BFL:UpdatePortraitVisibility("SettingsToggleDeferred")
			end
		end)
	end

	if BFL.ForceRefreshFriendsList then
		BFL:ForceRefreshFriendsList()
	end

	-- Update Portrait Visibility immediately
	if BFL.UpdatePortraitVisibility then
		BFL:UpdatePortraitVisibility("SettingsToggle")
	end
	self:RefreshThemeTab()
end

-- Legacy code (no longer used for Simple Mode)
--[[ function Settings:OnUseUIPanelSystemChanged_OLD(checked)
	local DB = GetDB()
	if not DB then return end
	
	DB:Set("useUIPanelSystem", checked)
	
	-- Apply immediately if frame is shown (only out of combat)
	if BetterFriendsFrame and BetterFriendsFrame:IsShown() and not InCombatLockdown() then
		if checked then
			-- Configure attributes and switch to ShowUIPanel
			if _G.ConfigureUIPanelAttributes then
				_G.ConfigureUIPanelAttributes(true)
			end
			BetterFriendsFrame:Hide()
			ShowUIPanel(BetterFriendsFrame)
			BFL:DebugPrint("Switched to UI Panel System")
		else
			-- Disable attributes and switch to direct Show/Hide
			-- CRITICAL: Use HideUIPanel first to properly unregister from UI Panel system
			if UIPanelWindows["BetterFriendsFrame"] then
				HideUIPanel(BetterFriendsFrame)
			else
				BetterFriendsFrame:Hide()
			end
			
			-- Now disable attributes (this removes from UIPanelWindows)
			if _G.ConfigureUIPanelAttributes then
				_G.ConfigureUIPanelAttributes(false)
			end
			
			-- Reopen with normal Show() (no longer a UIPanel)
			BetterFriendsFrame:Show()
			BFL:DebugPrint("Switched to direct Show/Hide")
		end
	elseif InCombatLockdown() then
		BFL:DebugPrint("Setting saved - will apply after combat ends")
	end
end
--]]

-- Enable Guild Tab toggle (all versions)
function Settings:OnEnableGuildTabChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("enableGuildTab", checked and true or false)
	self:RefreshGuildTabVisibility()
	self:RefreshGuildTab()
end

function Settings:RefreshGuildTabVisibility()
	local showGuildTab = BFL.IsGuildTabEnabled and BFL:IsGuildTabEnabled()

	if not BetterFriendsFrame then return end

	if BFL.IsClassic then
		-- Classic: Tab 3 = Guild, Tab 4 = Raid
		if BetterFriendsFrame.BottomTab3 then
			if showGuildTab then
				BetterFriendsFrame.BottomTab3:Show()
				local L = BFL.L
				BetterFriendsFrame.BottomTab3:SetText(L and L.GUILD_TAB_TITLE or GUILD or "Guild")
				BetterFriendsFrame.BottomTab3:SetScript("OnClick", function(self)
					PanelTemplates_Tab_OnClick(self, BetterFriendsFrame)
					BetterFriendsFrame_ShowBottomTab(3)
					PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON)
				end)
			else
				BetterFriendsFrame.BottomTab3:Hide()
				-- If currently on guild tab, switch to Friends
				if (BetterFriendsFrame.selectedTab or 1) == 3 then
					PanelTemplates_SetTab(BetterFriendsFrame, 1)
					BetterFriendsFrame_ShowBottomTab(1)
				end
			end
		end
		-- Reposition Raid tab
		if BetterFriendsFrame.BottomTab4 then
			BetterFriendsFrame.BottomTab4:ClearAllPoints()
			if showGuildTab then
				BetterFriendsFrame.BottomTab4:SetPoint("LEFT", BetterFriendsFrame.BottomTab3, "RIGHT", -15, 0)
			else
				BetterFriendsFrame.BottomTab4:SetPoint("LEFT", BetterFriendsFrame.BottomTab2, "RIGHT", -15, 0)
			end
		end
	else
		-- Retail: Guild is Top Tab 4 in FriendsTabHeader
		local header = BetterFriendsFrame.FriendsTabHeader
		local tab4 = header and header.Tab4
		if tab4 then
			if showGuildTab then
				tab4:Show()
				PanelTemplates_SetNumTabs(header, 4)
				PanelTemplates_UpdateTabs(header)
			else
				tab4:Hide()
				PanelTemplates_SetNumTabs(header, 3)
				PanelTemplates_UpdateTabs(header)
				-- If currently on guild top tab, switch to Friends top tab
				local activeTopTab = PanelTemplates_GetSelectedTab(header) or 1
				if activeTopTab == 4 then
					PanelTemplates_SetTab(header, 1)
					BetterFriendsFrame_ShowTab(1)
				end
			end
		end
	end

	-- Recalculate tab layout after visibility change
	if BFL.ApplyTabFonts then
		BFL:ApplyTabFonts()
	end

	local GuildFrame = BFL:GetModule("GuildFrame")
	if GuildFrame and GuildFrame.OnGuildTabSettingChanged then
		GuildFrame:OnGuildTabSettingChanged()
	end
end

-- Classic: Hide Guild Tab toggle
function Settings:OnHideGuildTabChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("hideGuildTab", checked)

	-- Toggle Guild Tab visibility and reposition Raid tab immediately
	if BetterFriendsFrame and BetterFriendsFrame.BottomTab3 then
		if checked then
			BetterFriendsFrame.BottomTab3:Hide()
			-- Reposition Raid tab (Tab 4) next to WHO tab (Tab 2)
			if BetterFriendsFrame.BottomTab4 then
				BetterFriendsFrame.BottomTab4:ClearAllPoints()
				BetterFriendsFrame.BottomTab4:SetPoint("LEFT", BetterFriendsFrame.BottomTab2, "RIGHT", -15, 0)
			end
		else
			BetterFriendsFrame.BottomTab3:Show()
			-- Restore Guild tab text and click handler
			local L = BFL.L
			BetterFriendsFrame.BottomTab3:SetText(L and L.GUILD_TAB_TITLE or GUILD or "Guild")
			BetterFriendsFrame.BottomTab3:SetScript("OnClick", function(self)
				PanelTemplates_Tab_OnClick(self, BetterFriendsFrame)
				BetterFriendsFrame_ShowBottomTab(3)
				PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON)
			end)
			-- Reposition Raid tab (Tab 4) next to Guild tab (Tab 3)
			if BetterFriendsFrame.BottomTab4 then
				BetterFriendsFrame.BottomTab4:ClearAllPoints()
				BetterFriendsFrame.BottomTab4:SetPoint("LEFT", BetterFriendsFrame.BottomTab3, "RIGHT", -15, 0)
			end
		end
	end
end

-- NEW: Enable In-Game Group toggle (Feature Request)
function Settings:OnEnableInGameGroupChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("enableInGameGroup", checked)

	-- Affects group structure - needs full refresh
	BFL:ForceRefreshFriendsList()

	-- Refresh settings to show/hide sub-option
	self:RefreshGeneralTab()
end

-- NEW: In-Game Group Mode (Feature Request)
function Settings:OnInGameGroupModeChanged(value)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("inGameGroupMode", value)

	-- Affects group structure - needs full refresh
	BFL:ForceRefreshFriendsList()
end

-- Enable Recently Added Group toggle
function Settings:OnEnableRecentlyAddedGroupChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("enableRecentlyAddedGroup", checked)

	-- Notify the module about the state change
	local RecentlyAddedModule = BFL:GetModule("RecentlyAdded")
	if RecentlyAddedModule and checked then
		RecentlyAddedModule:OnFeatureEnabled()
	end

	-- Affects group structure - needs full refresh
	BFL:ForceRefreshFriendsList()

	-- Refresh settings to show/hide sub-options
	self:RefreshGeneralTab()
end

-- Recently Added Duration Unit changed
function Settings:OnRecentlyAddedDurationUnitChanged(value)
	local DB = GetDB()
	if not DB then
		return
	end

	DB:Set("recentlyAddedDurationUnit", value)

	-- Duration change affects membership - needs full refresh
	BFL:ForceRefreshFriendsList()
end

-- Recently Added Duration Value changed
function Settings:OnRecentlyAddedDurationValueChanged(value)
	local DB = GetDB()
	if not DB then
		return
	end

	local num = tonumber(value)
	if not num or num < 1 then
		num = 1
	end
	num = math.floor(num)

	DB:Set("recentlyAddedDurationValue", num)

	-- Duration change affects membership - needs full refresh
	BFL:ForceRefreshFriendsList()
end

--------------------------------------------------------------------------
-- NEW: TAB REFRESH FUNCTIONS
--------------------------------------------------------------------------

-- Theme setting callbacks
local function IsDarkSkinThemeValue(theme)
	return theme == "dark" or theme == "custom"
end

local function ApplyThemeSettingsChanged(reason)
	local ThemeManager = BFL:GetModule("ThemeManager")
	if ThemeManager and ThemeManager.ApplyCurrentTheme then
		ThemeManager:ApplyCurrentTheme(reason or "theme-settings")
	end
end

local function FormatThemePercent(value)
	value = tonumber(value) or 0
	return string.format("%d%%", math.floor(value * 100 + 0.5))
end

function Settings:OnThemeChanged(theme)
	local DB = GetDB()
	if not DB then
		return
	end

	if theme ~= "blizzard" and theme ~= "dark" and theme ~= "custom" and theme ~= "elvui" then
		theme = "blizzard"
	end
	-- Dark and Custom are standard theme values.
	-- They remain selectable even when Beta Features are disabled.
	-- Only unavailable addon-backed themes fall back to Blizzard.
	if theme == "elvui" and (not BFL.IsElvUIAvailable or not BFL:IsElvUIAvailable()) then
		theme = "blizzard"
	end

	local oldStoredTheme = DB:Get("theme", "blizzard")
	local oldEffectiveTheme = BFL.GetEffectiveTheme and BFL:GetEffectiveTheme() or oldStoredTheme
	DB:Set("theme", theme)
	DB:Set("enableElvUISkin", theme == "elvui")

	local ThemeManager = BFL:GetModule("ThemeManager")
	local touchesElvUI = oldEffectiveTheme == "elvui" or theme == "elvui"
	if touchesElvUI then
		if IsDarkSkinThemeValue(oldEffectiveTheme) then
			local DarkTheme = BFL:GetModule("DarkTheme")
			if DarkTheme and DarkTheme.Remove then
				DarkTheme:Remove("settings-elvui")
			end
		end
		if ThemeManager and ThemeManager.ShowReloadDialog then
			ThemeManager:ShowReloadDialog()
		end
		return
	end

	if ThemeManager and ThemeManager.ApplyCurrentTheme then
		ThemeManager:ApplyCurrentTheme("settings")
	end
	self:RefreshThemeTab()
end

function Settings:OnLegacyElvUISkinChanged(checked)
	local DB = GetDB()
	if not DB then
		return
	end

	local oldStoredTheme = DB:Get("theme", "blizzard")
	local oldEffectiveTheme = BFL.GetEffectiveTheme and BFL:GetEffectiveTheme() or oldStoredTheme
	local enabled = checked == true
	DB:Set("enableElvUISkin", enabled)
	DB:Set("theme", enabled and "elvui" or "blizzard")

	local ThemeManager = BFL:GetModule("ThemeManager")
	if oldEffectiveTheme == "elvui" or enabled then
		if ThemeManager and ThemeManager.ShowReloadDialog then
			ThemeManager:ShowReloadDialog()
		end
	elseif ThemeManager and ThemeManager.ApplyCurrentTheme then
		ThemeManager:ApplyCurrentTheme("legacy-elvui-setting")
	end
end

function Settings:RefreshThemeTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.ThemeTab then
		return
	end

	local tab = content.ThemeTab
	local DB = GetDB()
	if not DB then
		return
	end
	local ThemePalette = BFL:GetModule("ThemePalette")
	local SkinEngine = BFL:GetModule("SkinEngine")
	local currentTheme = BFL.GetEffectiveTheme and BFL:GetEffectiveTheme() or DB:Get("theme", "blizzard")

	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}
	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_THEME_HEADER or "Theme"))

	local labels = {
		L.SETTINGS_THEME_BLIZZARD or "Blizzard",
		L.SETTINGS_THEME_DARK or "Dark",
		L.SETTINGS_THEME_CUSTOM or "Custom",
	}
	local values = { "blizzard", "dark", "custom" }
	if BFL.IsElvUIAvailable and BFL:IsElvUIAvailable() then
		table.insert(labels, L.SETTINGS_THEME_ELVUI or "ElvUI")
		table.insert(values, "elvui")
	end

	local themeDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_THEME_DROPDOWN or "Theme",
		{
			labels = labels,
			values = values,
		},
		function(value)
			return currentTheme == value
		end,
		function(value)
			self:OnThemeChanged(value)
		end
	)
	themeDropdown:SetTooltip(
		L.SETTINGS_THEME_DROPDOWN or "Theme",
		L.SETTINGS_THEME_DROPDOWN_DESC
			or "Choose the visual style for BetterFriendlist. ElvUI requires a UI reload."
	)
	table.insert(allFrames, themeDropdown)

	local simpleMode = DB:Get("simpleMode", false)

	local function SetThemeSetting(theme, key, value)
		if ThemePalette and ThemePalette.SetThemeSetting then
			ThemePalette:SetThemeSetting(theme, key, value)
			ApplyThemeSettingsChanged(theme .. "-theme-setting")
		end
	end

	local function SetCustomColor(key, r, g, b, a)
		if ThemePalette and ThemePalette.SetCustomColor then
			ThemePalette:SetCustomColor(key, { r = r, g = g, b = b, a = a })
			ApplyThemeSettingsChanged("custom-theme-setting")
		end
	end

	local function ResetCustomColor(key)
		if ThemePalette and ThemePalette.SetCustomColor then
			ThemePalette:SetCustomColor(key, nil)
			ApplyThemeSettingsChanged("custom-theme-reset-color")
			self:RefreshThemeTab()
		end
	end

	local function RefreshBrokerTooltips()
		local Broker = BFL:GetModule("Broker")
		if Broker and Broker.RefreshTooltip then
			Broker:RefreshTooltip()
		end

		local GBroker = BFL:GetModule("GuildBroker")
		if GBroker and GBroker.RefreshTooltip then
			GBroker:RefreshTooltip()
		end
	end

	local function SetBrokerTooltipThemeSetting(theme, key, value)
		if ThemePalette and ThemePalette.SetBrokerTooltipThemeSetting then
			ThemePalette:SetBrokerTooltipThemeSetting(theme, key, value)
			RefreshBrokerTooltips()
		end
	end

	local function ResetBrokerTooltipThemeSettings(theme)
		if ThemePalette and ThemePalette.ResetBrokerTooltipThemeSettings then
			ThemePalette:ResetBrokerTooltipThemeSettings(theme)
			RefreshBrokerTooltips()
			self:RefreshThemeTab()
		end
	end

	local function GetThemeSettings(theme)
		if ThemePalette and ThemePalette.GetThemeSettings then
			return ThemePalette:GetThemeSettings(theme)
		end
		if theme == "custom" then
			return DB:Get("customThemeSettings", {})
		end
		return DB:Get("darkThemeSettings", {})
	end

	local function AddThemeSlider(theme, label, key, tooltip, minValue)
		local settings = GetThemeSettings(theme)
		local initialValue = settings[key]
		if initialValue == nil then
			initialValue = 0
		end
		local slider = Components:CreateSlider(tab, label, minValue or 0, 1, initialValue, FormatThemePercent, function(value)
			SetThemeSetting(theme, key, value)
		end)
		if slider.SetStep then
			slider:SetStep(0.01)
		end
		if slider.SetTooltip then
			slider:SetTooltip(label, tooltip)
		end
		table.insert(allFrames, slider)
	end

	local function AddAvatarSlider(theme)
		if simpleMode then
			return
		end
		AddThemeSlider(
			theme,
			L.SETTINGS_THEME_ARTWORK_VISIBILITY or "BFL Avatar",
			"avatarVisibility",
			L.SETTINGS_THEME_ARTWORK_VISIBILITY_DESC,
			0
		)
	end

	local function AddCommonSkinSliders(theme)
		AddThemeSlider(theme, L.SETTINGS_THEME_WINDOW_OPACITY or "Window Opacity", "windowOpacity", L.SETTINGS_THEME_WINDOW_OPACITY_DESC, 0.15)
		AddThemeSlider(
			theme,
			L.SETTINGS_THEME_POPUP_OPACITY or "Popup & Tooltip Opacity",
			"popupOpacity",
			L.SETTINGS_THEME_POPUP_OPACITY_DESC,
			0.15
		)
		AddThemeSlider(
			theme,
			L.SETTINGS_THEME_LIST_OPACITY or "List & Inset Opacity",
			"listOpacity",
			L.SETTINGS_THEME_LIST_OPACITY_DESC,
			0.05
		)
		AddThemeSlider(
			theme,
			L.SETTINGS_THEME_CONTROL_OPACITY or "Control Opacity",
			"controlOpacity",
			L.SETTINGS_THEME_CONTROL_OPACITY_DESC,
			0.05
		)
		AddThemeSlider(theme, L.SETTINGS_THEME_HOVER_STRENGTH or "Hover Strength", "hoverStrength", L.SETTINGS_THEME_HOVER_STRENGTH_DESC)
		AddThemeSlider(
			theme,
			L.SETTINGS_THEME_SELECTION_STRENGTH or "Selection Strength",
			"selectionStrength",
			L.SETTINGS_THEME_SELECTION_STRENGTH_DESC
		)
		AddThemeSlider(theme, L.SETTINGS_THEME_BORDER_STRENGTH or "Border Strength", "borderStrength", L.SETTINGS_THEME_BORDER_STRENGTH_DESC)
		AddAvatarSlider(theme)
	end

	local function AddThemeColor(label, initialColor, callback, resetCallback, tooltip)
		local picker = Components:CreateColorPicker(tab, label, initialColor, callback, {
			tooltipTitle = label,
			tooltipDesc = tooltip,
			resetCallback = resetCallback,
			resetTooltip = L.SETTINGS_THEME_RESET_COLOR_TOOLTIP or "Right-click to inherit this color from the Dark theme.",
		})
		table.insert(allFrames, picker)
	end

	local function ToColor(array, fallback)
		local color = array or fallback
		if type(color) ~= "table" then
			return { r = 1, g = 1, b = 1, a = 1 }
		end
		return {
			r = color.r or color[1] or 1,
			g = color.g or color[2] or 1,
			b = color.b or color[3] or 1,
			a = color.a or color[4] or 1,
		}
	end

	local function AddBrokerTooltipSection(theme)
		if not ThemePalette or not ThemePalette.GetBrokerTooltipThemeSettings then
			return
		end

		local settings = ThemePalette:GetBrokerTooltipThemeSettings(theme)
		local BrokerUtils = BFL.BrokerUtils
		local fallback = BrokerUtils and BrokerUtils.GetBrokerTooltipFallbackBackground
			and BrokerUtils.GetBrokerTooltipFallbackBackground(theme)
			or { r = 0, g = 0, b = 0, a = 0.85 }
		local initialColor = ToColor(settings.backgroundColor, fallback)
		local initialOpacity = settings.opacity
		if initialOpacity == nil then
			initialOpacity = initialColor.a or fallback.a or 1
		end
		initialColor.a = initialOpacity

		table.insert(allFrames, Components:CreateSpacer(tab))
		table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_THEME_BROKER_TOOLTIP_HEADER or "Broker Tooltips"))

		local brokerBackgroundPicker = Components:CreateColorPicker(
			tab,
			L.SETTINGS_THEME_BROKER_TOOLTIP_BACKGROUND or "Background Color",
			initialColor,
			function(r, g, b, a)
				SetBrokerTooltipThemeSetting(theme, "backgroundColor", { r = r, g = g, b = b, a = a })
			end,
			{
				tooltipTitle = L.SETTINGS_THEME_BROKER_TOOLTIP_BACKGROUND or "Background Color",
				tooltipDesc = L.SETTINGS_THEME_BROKER_TOOLTIP_BACKGROUND_DESC
					or "Overrides the background color for Friends and Guild Data Broker tooltips in the selected theme.",
			}
		)
		table.insert(allFrames, brokerBackgroundPicker)

		local brokerOpacitySlider = Components:CreateSlider(
			tab,
			L.SETTINGS_THEME_BROKER_TOOLTIP_OPACITY or "Broker Tooltip Opacity",
			0,
			1,
			initialOpacity,
			FormatThemePercent,
			function(value)
				SetBrokerTooltipThemeSetting(theme, "opacity", value)
			end
		)
		if brokerOpacitySlider.SetStep then
			brokerOpacitySlider:SetStep(0.01)
		end
		if brokerOpacitySlider.SetTooltip then
			brokerOpacitySlider:SetTooltip(
				L.SETTINGS_THEME_BROKER_TOOLTIP_OPACITY or "Broker Tooltip Opacity",
				L.SETTINGS_THEME_BROKER_TOOLTIP_OPACITY_DESC
					or "Overrides the background opacity for Friends and Guild Data Broker tooltips in the selected theme."
			)
		end
		table.insert(allFrames, brokerOpacitySlider)

		table.insert(
			allFrames,
			Components:CreateButtonRow(tab, L.SETTINGS_THEME_BROKER_TOOLTIP_RESET or "Reset Broker Tooltips", nil, function()
				ResetBrokerTooltipThemeSettings(theme)
			end)
		)
	end

	if currentTheme == "dark" then
		local darkSettings = GetThemeSettings("dark")
		table.insert(allFrames, Components:CreateSpacer(tab))
		table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_THEME_DARK_HEADER or "Dark Theme Settings"))
		AddThemeColor(
			L.SETTINGS_THEME_ACCENT_COLOR or "Accent Color",
			darkSettings.accentColor,
			function(r, g, b, a)
				SetThemeSetting("dark", "accentColor", { r = r, g = g, b = b, a = a })
			end,
			nil,
			L.SETTINGS_THEME_ACCENT_COLOR_DESC or "Controls selected, hover, slider, and icon accents."
		)
		AddCommonSkinSliders("dark")
		table.insert(
			allFrames,
			Components:CreateButtonRow(tab, L.SETTINGS_THEME_RESET_DARK or "Reset Dark Settings", nil, function()
				if ThemePalette and ThemePalette.ResetDarkSettings then
					ThemePalette:ResetDarkSettings()
					ApplyThemeSettingsChanged("reset-dark-theme")
					self:RefreshThemeTab()
				end
			end)
		)
	elseif currentTheme == "custom" then
		local resolvedColors = SkinEngine and SkinEngine.defaultColors or {}
		if ThemePalette and SkinEngine and SkinEngine.defaultColors then
			resolvedColors = {}
			ThemePalette:ApplyToColors(resolvedColors, SkinEngine.defaultColors)
		end

		local customTheme = ThemePalette and ThemePalette:GetCustomTheme() or DB:Get("customTheme", {})
		local customMap = {
			{
				key = "backgroundColor",
				label = L.SETTINGS_THEME_CUSTOM_BACKGROUND or "Background",
				token = "panel",
				desc = L.SETTINGS_THEME_CUSTOM_BACKGROUND_DESC,
			},
			{
				key = "surfaceColor",
				label = L.SETTINGS_THEME_CUSTOM_SURFACE or "Surface",
				token = "panelSoft",
				desc = L.SETTINGS_THEME_CUSTOM_SURFACE_DESC,
			},
			{
				key = "insetColor",
				label = L.SETTINGS_THEME_CUSTOM_INSET or "Inset & List",
				token = "inset",
				desc = L.SETTINGS_THEME_CUSTOM_INSET_DESC,
			},
			{
				key = "controlColor",
				label = L.SETTINGS_THEME_CUSTOM_CONTROL or "Control",
				token = "control",
				desc = L.SETTINGS_THEME_CUSTOM_CONTROL_DESC,
			},
			{
				key = "borderColor",
				label = L.SETTINGS_THEME_CUSTOM_BORDER or "Border",
				token = "border",
				desc = L.SETTINGS_THEME_CUSTOM_BORDER_DESC,
			},
			{
				key = "accentColor",
				label = L.SETTINGS_THEME_CUSTOM_ACCENT or "Accent",
				token = "accent",
				desc = L.SETTINGS_THEME_CUSTOM_ACCENT_DESC,
			},
			{
				key = "textColor",
				label = L.SETTINGS_THEME_CUSTOM_TEXT or "Control Text",
				token = "text",
				desc = L.SETTINGS_THEME_CUSTOM_TEXT_DESC,
			},
			{
				key = "disabledTextColor",
				label = L.SETTINGS_THEME_CUSTOM_DISABLED_TEXT or "Disabled Text",
				token = "disabledText",
				desc = L.SETTINGS_THEME_CUSTOM_DISABLED_TEXT_DESC,
			},
			{
				key = "hoverColor",
				label = L.SETTINGS_THEME_CUSTOM_HOVER or "Hover",
				token = "rowHover",
				desc = L.SETTINGS_THEME_CUSTOM_HOVER_DESC,
			},
			{
				key = "selectedColor",
				label = L.SETTINGS_THEME_CUSTOM_SELECTED or "Selected & Pressed",
				token = "rowDown",
				desc = L.SETTINGS_THEME_CUSTOM_SELECTED_DESC,
			},
			{
				key = "scrollThumbColor",
				label = L.SETTINGS_THEME_CUSTOM_SCROLL_THUMB or "Scrollbar Thumb",
				token = "scrollThumb",
				desc = L.SETTINGS_THEME_CUSTOM_SCROLL_THUMB_DESC,
			},
			{
				key = "iconColor",
				label = L.SETTINGS_THEME_CUSTOM_ICON or "Icon Tint",
				token = "icon",
				desc = L.SETTINGS_THEME_CUSTOM_ICON_DESC,
			},
		}

		table.insert(allFrames, Components:CreateSpacer(tab))
		table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_THEME_CUSTOM_HEADER or "Custom Theme"))
		AddCommonSkinSliders("custom")
		for _, definition in ipairs(customMap) do
			local fallback = resolvedColors[definition.token] or (SkinEngine and SkinEngine.defaultColors and SkinEngine.defaultColors[definition.token])
			local initialColor = customTheme[definition.key] or ToColor(fallback)
			AddThemeColor(definition.label, initialColor, function(r, g, b, a)
				SetCustomColor(definition.key, r, g, b, a)
			end, function()
				ResetCustomColor(definition.key)
			end, definition.desc)
		end
		table.insert(
			allFrames,
			Components:CreateButtonRow(tab, L.SETTINGS_THEME_RESET_CUSTOM or "Reset Custom Theme", nil, function()
				if ThemePalette and ThemePalette.ResetCustomTheme then
					if ThemePalette.ResetCustomSettings then
						ThemePalette:ResetCustomSettings()
					end
					ThemePalette:ResetCustomTheme()
					ApplyThemeSettingsChanged("reset-custom-theme")
					self:RefreshThemeTab()
				end
			end)
		)
	end

	AddBrokerTooltipSection(currentTheme)

	Components:AnchorChain(allFrames, -5)
	tab.components = allFrames
end

-- Refresh General Tab with new component library
function Settings:RefreshGeneralTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.GeneralTab then
		return
	end

	local tab = content.GeneralTab
	local DB = GetDB()
	if not DB then
		return
	end

	-- Clear existing content (but keep the tab frame itself)
	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}

	-- Header: Display Options
	local displayHeader = Components:CreateHeader(tab, L.SETTINGS_DISPLAY_OPTIONS or "Display Options")
	table.insert(allFrames, displayHeader)

	-- Row 1: Class Colors & Faction Icons
	local row1 = Components:CreateDoubleCheckbox(tab, { -- Left
		label = L.SETTINGS_COLOR_CLASS_NAMES,
		initialValue = DB:Get("colorClassNames", true),
		callback = function(val)
			self:OnColorClassNamesChanged(val)
		end,
		tooltipTitle = L.SETTINGS_COLOR_CLASS_NAMES,
		tooltipDesc = L.SETTINGS_COLOR_CLASS_NAMES_DESC
			or "Colors character names using their class color for easier identification",
	}, { -- Right
		label = L.SETTINGS_SHOW_FACTION_ICONS,
		initialValue = DB:Get("showFactionIcons", false),
		callback = function(val)
			self:OnShowFactionIconsChanged(val)
		end,
		tooltipTitle = L.SETTINGS_SHOW_FACTION_ICONS,
		tooltipDesc = L.SETTINGS_SHOW_FACTION_ICONS_DESC or "Display Alliance/Horde icons next to character names",
	})
	table.insert(allFrames, row1)

	-- Row 2: Faction BG & Gray Other Faction
	local row2 = Components:CreateDoubleCheckbox(tab, { -- Left
		label = L.SETTINGS_SHOW_FACTION_BG,
		initialValue = DB:Get("showFactionBg", false),
		callback = function(val)
			DB:Set("showFactionBg", val)
			BFL:ForceRefreshFriendsList()
		end,
		tooltipTitle = L.SETTINGS_SHOW_FACTION_BG,
		tooltipDesc = L.SETTINGS_SHOW_FACTION_BG_DESC or "Show faction color as background for friend buttons.",
	}, { -- Right
		label = L.SETTINGS_GRAY_OTHER_FACTION,
		initialValue = DB:Get("grayOtherFaction", false),
		callback = function(val)
			self:OnGrayOtherFactionChanged(val)
		end,
		tooltipTitle = L.SETTINGS_GRAY_OTHER_FACTION,
		tooltipDesc = L.SETTINGS_GRAY_OTHER_FACTION_DESC or "Make friends from the opposite faction appear grayed out",
	})
	table.insert(allFrames, row2)

	-- Row 2b: Multi-Account Badge
	local row2b = Components:CreateCheckbox(tab, {
		label = L.SETTINGS_SHOW_MULTI_ACCOUNT_BADGE,
		initialValue = DB:Get("showMultiAccountBadge", true),
		callback = function(val)
			DB:Set("showMultiAccountBadge", val)
			BFL:ForceRefreshFriendsList()
		end,
		tooltipTitle = L.SETTINGS_SHOW_MULTI_ACCOUNT_BADGE,
		tooltipDesc = L.SETTINGS_SHOW_MULTI_ACCOUNT_BADGE_DESC
			or "Display a badge on friends with multiple game accounts online.",
	})
	table.insert(allFrames, row2b)

	-- Row 2c: Show Multi-Account Info
	local row2c = Components:CreateCheckbox(tab, {
		label = L.SETTINGS_SHOW_MULTI_ACCOUNT_INFO or "Show Multi-Account Info",
		initialValue = DB:Get("showMultiAccountInfo", true),
		callback = function(val)
			DB:Set("showMultiAccountInfo", val)
			BFL:ForceRefreshFriendsList()
		end,
		tooltipTitle = L.SETTINGS_SHOW_MULTI_ACCOUNT_INFO or "Show Multi-Account Info",
		tooltipDesc = L.SETTINGS_SHOW_MULTI_ACCOUNT_INFO_DESC
			or "Add a dedicated line listing other online characters when a friend is logged into multiple accounts.",
	})
	table.insert(allFrames, row2c)

	-- Row 3: Realm Name & Hide Max Level
	local row3 = Components:CreateDoubleCheckbox(tab, { -- Left
		label = L.SETTINGS_SHOW_REALM_NAME,
		initialValue = DB:Get("showRealmName", true),
		callback = function(val)
			self:OnShowRealmNameChanged(val)
		end,
		tooltipTitle = L.SETTINGS_SHOW_REALM_NAME,
		tooltipDesc = L.SETTINGS_SHOW_REALM_NAME_DESC or "Display the realm name for friends on different servers",
	}, { -- Right
		label = L.SETTINGS_HIDE_MAX_LEVEL,
		initialValue = DB:Get("hideMaxLevel", false),
		callback = function(val)
			self:OnHideMaxLevelChanged(val)
		end,
		tooltipTitle = L.SETTINGS_HIDE_MAX_LEVEL,
		tooltipDesc = L.SETTINGS_HIDE_MAX_LEVEL_DESC or "Don't display level number for characters at max level",
	})
	table.insert(allFrames, row3)

	-- Row 4: Mobile Settings
	local row4 = Components:CreateDoubleCheckbox(tab, { -- Left
		label = L.SETTINGS_SHOW_MOBILE_AS_AFK,
		initialValue = DB:Get("showMobileAsAFK", false),
		callback = function(val)
			self:OnShowMobileAsAFKChanged(val)
		end,
		tooltipTitle = L.SETTINGS_SHOW_MOBILE_AS_AFK,
		tooltipDesc = L.SETTINGS_SHOW_MOBILE_AS_AFK_DESC or "Display AFK status icon for friends on mobile (BSAp only)",
	}, { -- Right
		label = L.SETTINGS_TREAT_MOBILE_OFFLINE or "Treat Mobile users as Offline",
		initialValue = DB:Get("treatMobileAsOffline", false),
		callback = function(val)
			self:OnTreatMobileAsOfflineChanged(val)
		end,
		tooltipTitle = L.SETTINGS_TREAT_MOBILE_OFFLINE or "Treat Mobile as Offline",
		tooltipDesc = L.SETTINGS_TREAT_MOBILE_OFFLINE_DESC
			or "Display friends using the Mobile App in the Offline group",
	})
	table.insert(allFrames, row4)

	-- Row 5: Welcome Message & Hide Empty
	local row5 = Components:CreateDoubleCheckbox(tab, { -- Left
		label = L.SETTINGS_SHOW_WELCOME_MESSAGE,
		initialValue = DB:Get("showWelcomeMessage", true),
		callback = function(val)
			DB:Set("showWelcomeMessage", val)
		end,
		tooltipTitle = L.SETTINGS_SHOW_WELCOME_MESSAGE,
		tooltipDesc = L.SETTINGS_SHOW_WELCOME_MESSAGE_DESC
			or "Shows the 'BetterFriendlist loaded...' message in chat when you log in or reload.",
	}, { -- Right
		label = L.SETTINGS_HIDE_EMPTY_GROUPS,
		initialValue = DB:Get("hideEmptyGroups", false),
		callback = function(val)
			self:OnHideEmptyGroupsChanged(val)
		end,
		tooltipTitle = L.SETTINGS_HIDE_EMPTY_GROUPS,
		tooltipDesc = L.SETTINGS_HIDE_EMPTY_GROUPS_DESC or "Automatically hides groups that have no online members",
	})
	table.insert(allFrames, row5)

	-- Row 6: Favorite Icon + Favorite Icon Dropdown (conditional)
	local favoriteIconEnabled = DB:Get("enableFavoriteIcon", true)
	local hasBlizzardFavoriteIcon = BFL.HasAtlas and BFL.HasAtlas("friendslist-favorite")
	local blizzardIconTag = BFL.GetAtlasOrTextureMarkup
		and BFL.GetAtlasOrTextureMarkup("friendslist-favorite", "Interface\\AddOns\\BetterFriendlist\\Icons\\star", 20, 20)
		or "|TInterface\\AddOns\\BetterFriendlist\\Icons\\star:20:20|t"

	local favoriteEntries = {
		labels = {
			"|TInterface\\AddOns\\BetterFriendlist\\Icons\\star:17:17|t "
				.. (L.SETTINGS_FAVORITE_ICON_OPTION_BFL or "BFL Icon"),
			blizzardIconTag .. " " .. (L.SETTINGS_FAVORITE_ICON_OPTION_BLIZZARD or "Blizzard Icon"),
		},
		values = { "bfl", "blizzard" },
	}

	local favoriteDropdownData = nil
	if favoriteIconEnabled and hasBlizzardFavoriteIcon then
		favoriteDropdownData = {
			label = L.SETTINGS_FAVORITE_ICON_STYLE or "Favorite Icon",
			entries = favoriteEntries,
			isSelectedCallback = function(value)
				return DB:Get("favoriteIconStyle", "bfl") == value
			end,
			onSelectionCallback = function(value)
				DB:Set("favoriteIconStyle", value)
				BFL:ForceRefreshFriendsList()
			end,
			tooltipTitle = L.SETTINGS_FAVORITE_ICON_STYLE or "Favorite Icon",
			tooltipDesc = L.SETTINGS_FAVORITE_ICON_STYLE_DESC or "Choose which icon is used for favorites.",
		}
	end

	local row6 = Components:CreateCheckboxDropdown(tab, {
		label = L.SETTINGS_ENABLE_FAVORITE_ICON,
		initialValue = favoriteIconEnabled,
		callback = function(val)
			DB:Set("enableFavoriteIcon", val)
			BFL:ForceRefreshFriendsList()
			self:RefreshGeneralTab()
		end,
		tooltipTitle = L.SETTINGS_ENABLE_FAVORITE_ICON,
		tooltipDesc = L.SETTINGS_ENABLE_FAVORITE_ICON_DESC or "Display a star icon on the friend button for favorites.",
	}, favoriteDropdownData)
	table.insert(allFrames, row6)

	-- Row 7: Blizzard Option
	local row7 = Components:CreateCheckbox(tab, {
		label = L.SETTINGS_SHOW_BLIZZARD,
		initialValue = DB:Get("showBlizzardOption", false),
		callback = function(val)
			self:OnShowBlizzardOptionChanged(val)
		end,
		tooltipTitle = L.SETTINGS_SHOW_BLIZZARD,
		tooltipDesc = L.SETTINGS_SHOW_BLIZZARD_DESC or "Shows the original Blizzard Friends button in the social menu",
	})
	table.insert(allFrames, row7)

	if ShouldShowLegacyElvUISkinSetting() then
		local elvUISkinToggle = Components:CreateCheckbox(tab, {
			label = L.SETTINGS_ENABLE_ELVUI_SKIN,
			initialValue = IsLegacyElvUISkinSelected(DB),
			callback = function(val)
				self:OnLegacyElvUISkinChanged(val)
			end,
			tooltipTitle = L.SETTINGS_ENABLE_ELVUI_SKIN,
			tooltipDesc = L.SETTINGS_ENABLE_ELVUI_SKIN_DESC
				or "Enables the ElvUI skin for BetterFriendlist. Requires ElvUI to be installed and enabled.",
		})
		table.insert(allFrames, elvUISkinToggle)
	end

	-- Spacer before next section
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ========================================
	-- Header: Name Formatting (Phase 22 - Preset System)
	-- ========================================
	local nameFormatHeader = Components:CreateHeader(tab, L.SETTINGS_NAME_FORMAT_HEADER or "Name Formatting")
	table.insert(allFrames, nameFormatHeader)

	-- FriendListColors warning (if active)
	local friendListColorsActive = _G.FriendListColorsAPI ~= nil
	if friendListColorsActive then
		local flcWarning = Components:CreateLabel(
			tab,
			"|cffFF3333"
				.. (L.SETTINGS_NAME_FORMAT_DISABLED_FRIENDLISTCOLORS or "Name formatting is disabled because 'FriendListColors' is managing name colors/formats.")
				.. "|r",
			true
		)
		table.insert(allFrames, flcWarning)
	end

	-- Name Format Preset Dropdown
	local namePresetEntries = {
		labels = {
			L.NAME_PRESET_DEFAULT or "Name (Character)",
			L.NAME_PRESET_BATTLETAG or "BattleTag (Character)",
			L.NAME_PRESET_BATTLETAG_ONLY or "BattleTag Only",
			L.NAME_PRESET_NICKNAME or "Nickname (Character)",
			L.NAME_PRESET_CHARACTER or "Character Only",
			L.NAME_PRESET_NAME_ONLY or "Name Only",
			L.NAME_PRESET_CUSTOM or "Custom...",
		},
		values = {
			"default",
			"battletag",
			"battletag_only",
			"nickname",
			"character",
			"name_only",
			"custom",
		},
	}

	local nameCustomInput -- forward declare for visibility toggling

	local namePresetDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_NAME_FORMAT_LABEL or "Preset:",
		namePresetEntries,
		function(val)
			return DB:Get("nameFormatPreset", "default") == val
		end,
		function(val)
			DB:Set("nameFormatPreset", val)
			BFL:ForceRefreshFriendsList()
			-- Show/hide custom input
			if nameCustomInput then
				if val == "custom" then
					nameCustomInput:Show()
				else
					nameCustomInput:Hide()
				end
			end
		end
	)
	if friendListColorsActive and namePresetDropdown then
		-- Disable dropdown when FriendListColors is active
		if namePresetDropdown.Label then
			namePresetDropdown.Label:SetTextColor(0.5, 0.5, 0.5)
		end
	end
	table.insert(allFrames, namePresetDropdown)

	-- Custom Name Format Input (shown only when preset == "custom")
	nameCustomInput = Components:CreateInput(
		tab,
		L.SETTINGS_NAME_FORMAT_CUSTOM_LABEL or "Custom Format:",
		DB:Get("nameFormatCustom", "%name%"),
		function(text)
			if text and text ~= "" then
				DB:Set("nameFormatCustom", text)
				BFL:ForceRefreshFriendsList()
			end
		end,
		350
	)
	if nameCustomInput.SetTooltip then
		nameCustomInput:SetTooltip(
			L.SETTINGS_NAME_FORMAT_TOOLTIP or "Custom Name Format",
			L.SETTINGS_NAME_FORMAT_DESC
				or "Use tokens to customize the display:\n|cffFFD100%name%|r - Account Name\n|cffFFD100%battletag%|r - BattleTag\n|cffFFD100%nickname%|r - Nickname\n|cffFFD100%note%|r - Note\n|cffFFD100%character%|r - Character Name\n|cffFFD100%realm%|r - Realm Name\n|cffFFD100%level%|r - Level\n|cffFFD100%zone%|r - Zone\n|cffFFD100%class%|r - Class\n|cffFFD100%game%|r - Game"
		)
	end
	-- Initially show/hide based on current preset
	if DB:Get("nameFormatPreset", "default") ~= "custom" then
		nameCustomInput:Hide()
	end
	if friendListColorsActive then
		if nameCustomInput.Label then
			nameCustomInput.Label:SetTextColor(0.5, 0.5, 0.5)
		end
		if nameCustomInput.Input then
			nameCustomInput.Input:Disable()
			nameCustomInput.Input:SetTextColor(0.5, 0.5, 0.5)
		end
	end
	table.insert(allFrames, nameCustomInput)

	-- Spacer before Info Formatting
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ========================================
	-- Header: Friend Info Formatting (Phase 22)
	-- ========================================
	local infoFormatHeader = Components:CreateHeader(tab, L.SETTINGS_INFO_FORMAT_HEADER or "Friend Info Formatting")
	table.insert(allFrames, infoFormatHeader)

	-- Info Format Preset Dropdown
	local infoPresetEntries = {
		labels = {
			L.INFO_PRESET_DEFAULT or "Default (Level, Zone)",
			L.INFO_PRESET_ZONE or "Zone Only",
			L.INFO_PRESET_LEVEL or "Level Only",
			L.INFO_PRESET_CLASS_ZONE or "Class, Zone",
			L.INFO_PRESET_LEVEL_CLASS_ZONE or "Level Class, Zone",
			L.INFO_PRESET_GAME or "Game Name",
			L.INFO_PRESET_DISABLED or "Disabled (Hide Info)",
			L.INFO_PRESET_CUSTOM or "Custom...",
		},
		values = { "default", "zone", "level", "class_zone", "level_class_zone", "game", "disabled", "custom" },
	}

	local infoCustomInput -- forward declare

	local infoPresetDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_INFO_FORMAT_LABEL or "Preset:",
		infoPresetEntries,
		function(val)
			return DB:Get("infoFormatPreset", "default") == val
		end,
		function(val)
			DB:Set("infoFormatPreset", val)
			BFL:ForceRefreshFriendsList()
			-- Show/hide custom input
			if infoCustomInput then
				if val == "custom" then
					infoCustomInput:Show()
				else
					infoCustomInput:Hide()
				end
			end
		end
	)
	table.insert(allFrames, infoPresetDropdown)

	-- Custom Info Format Input (shown only when preset == "custom")
	infoCustomInput = Components:CreateInput(
		tab,
		L.SETTINGS_INFO_FORMAT_CUSTOM_LABEL or "Custom Format:",
		DB:Get("infoFormatCustom", "%level%, %zone%"),
		function(text)
			if text and text ~= "" then
				DB:Set("infoFormatCustom", text)
				BFL:ForceRefreshFriendsList()
			end
		end,
		350
	)
	if infoCustomInput.SetTooltip then
		infoCustomInput:SetTooltip(
			L.SETTINGS_INFO_FORMAT_TOOLTIP or "Custom Info Format",
			L.SETTINGS_INFO_FORMAT_DESC
				or "Use tokens to customize the info line:\n|cffFFD100%level%|r - Character Level\n|cffFFD100%zone%|r - Current Zone\n|cffFFD100%class%|r - Class Name\n|cffFFD100%game%|r - Game Name\n|cffFFD100%realm%|r - Realm Name\n|cffFFD100%status%|r - AFK/DND/Online\n|cffFFD100%lastonline%|r - Last Online\n|cffFFD100%name%|r - Account Name\n|cffFFD100%battletag%|r - BattleTag\n|cffFFD100%nickname%|r - Nickname\n|cffFFD100%note%|r - Note\n|cffFFD100%character%|r - Character Name"
		)
	end
	-- Initially show/hide based on current preset
	if DB:Get("infoFormatPreset", "default") ~= "custom" then
		infoCustomInput:Hide()
	end
	table.insert(allFrames, infoCustomInput)

	-- Spacer before next section
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- Header: Behavior
	local behaviorHeader = Components:CreateHeader(tab, L.SETTINGS_BEHAVIOR_HEADER or "Behavior")
	table.insert(allFrames, behaviorHeader)

	-- Behavior Settings Row 1 (Accordion / Compact)
	local behaviorRow1 = Components:CreateDoubleCheckbox(tab, {
		label = L.SETTINGS_ACCORDION_GROUPS,
		initialValue = DB:Get("accordionGroups", true),
		callback = function(val)
			self:OnAccordionGroupsChanged(val)
		end,
		tooltipTitle = L.SETTINGS_ACCORDION_GROUPS,
		tooltipDesc = L.SETTINGS_ACCORDION_GROUPS_DESC
			or "Only allow one group to be expanded at a time, automatically collapsing others",
	}, {
		label = L.SETTINGS_COMPACT_MODE,
		initialValue = DB:Get("compactMode", false),
		callback = function(val)
			self:OnCompactModeChanged(val)
		end,
		tooltipTitle = L.SETTINGS_COMPACT_MODE,
		tooltipDesc = L.SETTINGS_COMPACT_MODE_DESC or "Reduces button height to fit more friends on screen",
	})
	table.insert(allFrames, behaviorRow1)

	-- Behavior Settings Row 2 (UI Panel / Simple Mode)
	local behaviorRow2 = Components:CreateDoubleCheckbox(tab, {
		label = L.SETTINGS_USE_UI_PANEL_SYSTEM or "Use UI Panel System",
		initialValue = DB:Get("useUIPanelSystem", false),
		callback = function(val)
			self:OnUseUIPanelSystemChanged(val)
		end,
		tooltipTitle = L.SETTINGS_USE_UI_PANEL_SYSTEM or "Use UI Panel System",
		tooltipDesc = L.SETTINGS_USE_UI_PANEL_SYSTEM_DESC
			or "Use Blizzard's UI Panel system for automatic repositioning when other windows are open (Character, Spellbook, etc.)",
	}, {
		label = L.SETTINGS_SIMPLE_MODE or "Simple Mode",
		initialValue = DB:Get("simpleMode", false),
		callback = function(val)
			self:OnSimpleModeChanged(val)
		end,
		tooltipTitle = L.SETTINGS_SIMPLE_MODE or "Simple Mode",
		tooltipDesc = L.SETTINGS_SIMPLE_MODE_DESC
			or "Hides the player portrait and adds a changelog option to the contacts menu.",
	})
	table.insert(allFrames, behaviorRow2)

	local whisperClickEnabled = DB:Get("friendListClickWhisperEnabled", false)
	local whisperClickDropdown = nil
	if whisperClickEnabled then
		whisperClickDropdown = {
			label = L.SETTINGS_FRIENDLIST_CLICK_WHISPER_MODE or "Whisper Click",
			entries = {
				labels = {
					L.SETTINGS_FRIENDLIST_CLICK_WHISPER_MODE_DOUBLE or "Double-click",
					L.SETTINGS_FRIENDLIST_CLICK_WHISPER_MODE_SINGLE or "Single click",
				},
				values = { "double", "single" },
			},
			isSelectedCallback = function(val)
				return DB:Get("friendListClickWhisperMode", "double") == val
			end,
			onSelectionCallback = function(val)
				self:OnFriendListClickWhisperModeChanged(val)
			end,
			tooltipTitle = L.SETTINGS_FRIENDLIST_CLICK_WHISPER_MODE or "Whisper Click",
			tooltipDesc = L.SETTINGS_FRIENDLIST_CLICK_WHISPER_MODE_DESC
				or "Choose whether a single left-click or double left-click starts the whisper.",
		}
	end

	local whisperClickRow = Components:CreateCheckboxDropdown(tab, {
		label = L.SETTINGS_FRIENDLIST_CLICK_WHISPER or "Whisper from Friend List",
		initialValue = whisperClickEnabled,
		callback = function(val)
			self:OnFriendListClickWhisperEnabledChanged(val)
		end,
		tooltipTitle = L.SETTINGS_FRIENDLIST_CLICK_WHISPER or "Whisper from Friend List",
		tooltipDesc = L.SETTINGS_FRIENDLIST_CLICK_WHISPER_DESC
			or "Start a whisper from the friend list with a left-click action.",
	}, whisperClickDropdown)
	table.insert(allFrames, whisperClickRow)

	-- Spacer before next section
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- Header: Group Management
	local groupHeader = Components:CreateHeader(tab, L.SETTINGS_GROUP_MANAGEMENT or "Group Management")
	table.insert(allFrames, groupHeader)

	-- Group Management Row (Favorites / In-Game)
	local Groups = GetGroups()
	local showGroupFmt = L.SETTINGS_SHOW_GROUP_FMT or "Show %s Group"
	local showGroupDescFmt = L.SETTINGS_SHOW_GROUP_DESC_FMT or "Toggle visibility of the %s group in your friends list"
	local function BuildBuiltinDisplayName(groupId, fallbackName)
		local groupName = (Groups and Groups:Get(groupId) and Groups:Get(groupId).name) or fallbackName
		local defaultName = fallbackName
		if groupName and defaultName and groupName ~= defaultName then
			return string.format("%s (%s)", groupName, defaultName)
		end
		return groupName or defaultName
	end
	local favoritesDisplayName = BuildBuiltinDisplayName("favorites", L.GROUP_FAVORITES or "Favorites")
	local inGameDisplayName = BuildBuiltinDisplayName("ingame", L.GROUP_INGAME or "In-Game")
	local favoritesLabel = string.format(showGroupFmt, favoritesDisplayName)
	local inGameLabel = string.format(showGroupFmt, inGameDisplayName)

	local groupRow = Components:CreateDoubleCheckbox(tab, {
		label = favoritesLabel,
		initialValue = DB:Get("showFavoritesGroup", true),
		callback = function(val)
			self:OnShowFavoritesGroupChanged(val)
		end,
		tooltipTitle = favoritesLabel,
		tooltipDesc = string.format(showGroupDescFmt, favoritesDisplayName),
	}, {
		label = inGameLabel,
		initialValue = DB:Get("enableInGameGroup", false),
		callback = function(val)
			self:OnEnableInGameGroupChanged(val)
		end,
		tooltipTitle = inGameLabel,
		tooltipDesc = string.format(showGroupDescFmt, inGameDisplayName),
	})
	table.insert(allFrames, groupRow)

	-- NEW: In-Game Group Mode (Sub-option) - Keep as dedicated row since it's a dropdown
	if DB:Get("enableInGameGroup", false) then
		local modeOptions = {
			labels = { L.SETTINGS_INGAME_MODE_WOW or "WoW Only (Same Era)", L.SETTINGS_INGAME_MODE_ANY or "Any Game" },
			values = { "same_game", "any_game" },
		}
		local currentMode = DB:Get("inGameGroupMode", "same_game")

		local modeDropdown = Components:CreateDropdown(
			tab,
			L.SETTINGS_INGAME_MODE_LABEL or "   Mode:",
			modeOptions,
			function(val)
				return val == DB:Get("inGameGroupMode", "same_game")
			end,
			function(val)
				self:OnInGameGroupModeChanged(val)
			end
		)
		modeDropdown:SetTooltip(
			L.SETTINGS_INGAME_MODE_TOOLTIP or "In-Game Group Mode",
			L.SETTINGS_INGAME_MODE_TOOLTIP_DESC
				or "Choose which friends to include in the In-Game group:\n\n|cffffffffWoW Only:|r Friends playing the same WoW version (Retail/Classic)\n|cffffffffAny Game:|r Friends playing any Battle.net game"
		)
		table.insert(allFrames, modeDropdown)
	end

	-- Recently Added Group checkbox
	local recentlyAddedDisplayName =
		BuildBuiltinDisplayName("recentlyadded", L.GROUP_RECENTLY_ADDED or "Recently Added")
	local recentlyAddedLabel = string.format(showGroupFmt, recentlyAddedDisplayName)
	local recentlyAddedCheckbox = Components:CreateCheckbox(tab, {
		label = recentlyAddedLabel,
		initialValue = DB:Get("enableRecentlyAddedGroup", false),
		callback = function(val)
			self:OnEnableRecentlyAddedGroupChanged(val)
		end,
		tooltipTitle = recentlyAddedLabel,
		tooltipDesc = string.format(showGroupDescFmt, recentlyAddedDisplayName),
	})
	table.insert(allFrames, recentlyAddedCheckbox)

	-- Recently Added sub-options (only shown when enabled)
	if DB:Get("enableRecentlyAddedGroup", false) then
		-- Duration Unit dropdown
		local unitOptions = {
			labels = {
				L.SETTINGS_DURATION_DAYS or "Days",
				L.SETTINGS_DURATION_HOURS or "Hours",
				L.SETTINGS_DURATION_MINUTES or "Minutes",
			},
			values = { "days", "hours", "minutes" },
		}
		local durationDropdown = Components:CreateDropdown(
			tab,
			L.SETTINGS_RECENTLY_ADDED_DURATION_UNIT or "   Duration Unit:",
			unitOptions,
			function(val)
				return val == DB:Get("recentlyAddedDurationUnit", "days")
			end,
			function(val)
				self:OnRecentlyAddedDurationUnitChanged(val)
			end
		)
		durationDropdown:SetTooltip(
			L.SETTINGS_RECENTLY_ADDED_DURATION_UNIT_TOOLTIP or "Duration Unit",
			L.SETTINGS_RECENTLY_ADDED_DURATION_UNIT_DESC
				or "Choose the time unit for how long friends stay in the Recently Added group."
		)
		table.insert(allFrames, durationDropdown)

		-- Duration Value input
		local durationInput = Components:CreateInput(
			tab,
			L.SETTINGS_RECENTLY_ADDED_DURATION_VALUE or "   Duration Value:",
			tostring(DB:Get("recentlyAddedDurationValue", 7)),
			function(val, editBox)
				local num = tonumber(val)
				if not num or num < 1 then
					num = 1
				end
				num = math.floor(num)
				if editBox then
					editBox:SetText(tostring(num))
				end
				self:OnRecentlyAddedDurationValueChanged(num)
			end
		)
		durationInput:SetTooltip(
			L.SETTINGS_RECENTLY_ADDED_DURATION_VALUE_TOOLTIP or "Duration Value",
			L.SETTINGS_RECENTLY_ADDED_DURATION_VALUE_DESC
				or "How many days/hours/minutes friends stay in the Recently Added group before being removed."
		)
		table.insert(allFrames, durationInput)
	end

	-- Spacer before next section
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- Header: Frame Dimensions
	local frameHeader = Components:CreateHeader(tab, L.SETTINGS_FRAME_DIMENSIONS_HEADER or "Frame Dimensions")
	table.insert(allFrames, frameHeader)

	-- Helper for input validation
	local function ValidateAndApply(val, min, max, applyFunc, editBox, isFloat)
		local num = tonumber(val)
		if num then
			-- Clamp
			if num < min then
				num = min
			end
			if num > max then
				num = max
			end

			if not isFloat then
				num = math.floor(num + 0.5)
			end

			applyFunc(num)

			if editBox then
				editBox:SetText(tostring(num))
			end
		end
	end

	-- Frame Width Input
	local width = 415
	local dbSize = DB:Get("mainFrameSize")
	if dbSize and dbSize["Default"] and dbSize["Default"].width then
		width = dbSize["Default"].width
	end

	local widthInput = Components:CreateInput(
		tab,
		(L.SETTINGS_FRAME_WIDTH or "Width:") .. " (380-800)",
		tostring(width), -- Current Value
		function(val, editBox)
			ValidateAndApply(val, 380, 800, function(v)
				local FrameSettings = BFL:GetModule("FrameSettings")
				if FrameSettings then
					FrameSettings:ApplySize(v, nil)
					BFL:ForceRefreshFriendsList()
				end
			end, editBox, false)
		end
	)
	table.insert(allFrames, widthInput)

	-- Frame Height Input
	local height = 570
	if dbSize and dbSize["Default"] and dbSize["Default"].height then
		height = dbSize["Default"].height
	end

	local heightInput = Components:CreateInput(
		tab,
		(L.SETTINGS_FRAME_HEIGHT or "Height:") .. " (400-1200)",
		tostring(height), -- Current Value
		function(val, editBox)
			ValidateAndApply(val, 400, 1200, function(v)
				local FrameSettings = BFL:GetModule("FrameSettings")
				if FrameSettings then
					FrameSettings:ApplySize(nil, v)
					BFL:ForceRefreshFriendsList()
				end
			end, editBox, false)
		end
	)
	table.insert(allFrames, heightInput)

	-- Frame Scale Input
	local scaleInput = Components:CreateInput(
		tab,
		(L.SETTINGS_FRAME_SCALE or "Scale:") .. " (0.5-2.0)",
		tostring(DB:Get("windowScale", 1.0)), -- Current Value
		function(val, editBox)
			ValidateAndApply(val, 0.5, 2.0, function(v)
				local FrameSettings = BFL:GetModule("FrameSettings")
				if FrameSettings then
					FrameSettings:ApplyScale(v)
					-- Scale touches everything, might need layout update
					BFL:ForceRefreshFriendsList()
				end
			end, editBox, true)
		end
	)
	table.insert(allFrames, scaleInput)

	-- Lock Window Checkbox
	local lockCheckbox = Components:CreateCheckbox(
		tab,
		L.SETTINGS_LOCK_WINDOW or "Lock Window",
		DB:Get("lockWindow", false),
		function(checked)
			local FrameSettings = BFL:GetModule("FrameSettings")
			if FrameSettings then
				FrameSettings:ApplyLock(checked)
			end
		end
	)
	lockCheckbox:SetTooltip(
		L.SETTINGS_LOCK_WINDOW or "Lock Window",
		L.SETTINGS_LOCK_WINDOW_DESC or "Prevent the main window from being moved."
	)
	table.insert(allFrames, lockCheckbox)

	-- Anchor all frames vertically
	Components:AnchorChain(allFrames, -5)

	-- Store components for cleanup
	tab.components = allFrames
end

local function OpenLegacyFriendTagEditor(tagId)
	local Editor = BFL:GetModule("FriendTagEditor")
	if Editor and Editor.Show then
		Editor:Show(tagId)
		return true
	end
	return false
end

local function CreateLegacyFriendTagRow(parent, FriendTags, tag, refreshCallback)
	local row = CreateFrame("Frame", nil, parent, "BackdropTemplate")
	row:SetHeight(36)
	row:SetPoint("LEFT", UI.SPACING_MEDIUM, 0)
	row:SetPoint("RIGHT", -UI.SPACING_MEDIUM, 0)
	row:SetBackdrop({
		bgFile = "Interface\\Buttons\\WHITE8X8",
		edgeFile = "Interface\\Buttons\\WHITE8X8",
		edgeSize = 1,
	})
	row:SetBackdropColor(0.05, 0.05, 0.06, 0.45)
	row:SetBackdropBorderColor(0.24, 0.24, 0.28, 0.55)

	local profile = FriendTags and FriendTags:GetChipProfile(tag)
	local icon = row:CreateTexture(nil, "ARTWORK")
	icon:SetSize(18, 18)
	icon:SetPoint("LEFT", row, "LEFT", 8, 0)
	if profile and profile.icon then
		icon:SetTexture(profile.icon)
	else
		icon:SetTexture("Interface\\AddOns\\BetterFriendlist\\Icons\\tag")
	end

	local name = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlight")
	name:SetPoint("LEFT", icon, "RIGHT", 8, 0)
	name:SetPoint("RIGHT", row, "RIGHT", tag.source == "custom" and -210 or -142, 0)
	name:SetJustifyH("LEFT")
	name:SetWordWrap(false)
	local label = FriendTags and FriendTags.GetChipLabel and FriendTags:GetChipLabel(tag) or nil
	if label and label ~= "" and label ~= tag.name then
		name:SetText((tag.name or tag.id) .. "  |cffaaaaaa(" .. label .. ")|r")
	else
		name:SetText(tag.name or tag.id)
	end

	local source = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlightSmall")
	source:SetPoint("LEFT", name, "RIGHT", 6, 0)
	source:SetWidth(66)
	source:SetJustifyH("LEFT")
	if tag.source == "custom" then
		source:SetText(L.FRIEND_TAGS_SOURCE_CUSTOM or "Custom")
		source:SetTextColor(0.64, 0.86, 0.56, 1)
	else
		source:SetText(L.FRIEND_TAGS_SOURCE_BLIZZARD or "Blizzard")
		source:SetTextColor(0.50, 0.78, 1.00, 1)
	end

	local function CreateRowButton(text, width, rightOffset, onClick)
		local button = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
		button:SetSize(width or 58, 22)
		button:SetPoint("RIGHT", row, "RIGHT", rightOffset, 0)
		button:SetText(text)
		button:SetScript("OnClick", onClick)
		return button
	end

	if tag.source == "custom" then
		CreateRowButton(L.FRIEND_TAGS_EDITOR_DELETE or DELETE or "Delete", 58, -8, function()
			if StaticPopup_Show and StaticPopupDialogs and StaticPopupDialogs["BFL_FRIEND_TAG_EDITOR_DELETE_CUSTOM"] then
				StaticPopup_Show("BFL_FRIEND_TAG_EDITOR_DELETE_CUSTOM", tag.name or tag.id, nil, { tagId = tag.id })
			elseif FriendTags and FriendTags.DeleteCustomTag then
				FriendTags:DeleteCustomTag(tag.id, refreshCallback)
			end
		end)
		CreateRowButton(L.FRIEND_TAGS_EDITOR_RESET or RESET or "Reset", 58, -70, function()
			if FriendTags and FriendTags.ResetChipProfile then
				FriendTags:ResetChipProfile(tag.id, refreshCallback)
			end
		end)
		CreateRowButton(L.FRIEND_TAGS_EDITOR_EDIT or EDIT or "Edit", 58, -132, function()
			OpenLegacyFriendTagEditor(tag.id)
		end)
	else
		CreateRowButton(L.FRIEND_TAGS_EDITOR_RESET or RESET or "Reset", 58, -8, function()
			if FriendTags and FriendTags.ResetChipProfile then
				FriendTags:ResetChipProfile(tag.id, refreshCallback)
			end
		end)
		CreateRowButton(L.FRIEND_TAGS_EDITOR_EDIT or EDIT or "Edit", 58, -70, function()
			OpenLegacyFriendTagEditor(tag.id)
		end)
	end

	return row
end

function Settings:RefreshContactMemoryTab()
	if not settingsFrame or not Components then
		return
	end

	local tab = self:GetContactMemoryTabHost()
	if not tab then
		return
	end

	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}
	local contactMemory = BFL:GetModule("ContactMemory")
	local contactMemoryDB = BetterFriendlistDB and BetterFriendlistDB.contactMemory
	local betaEnabled = BetterFriendlistDB and BetterFriendlistDB.enableBetaFeatures == true
	local contactMemoryEnabled = false
	if contactMemory and contactMemory.GetEnabledSetting then
		contactMemoryEnabled = contactMemory:GetEnabledSetting() == true
	elseif type(contactMemoryDB) == "table" then
		contactMemoryEnabled = contactMemoryDB.enabled == true
	end

	local function RefreshContactMemorySettings()
		if BFL.ForceRefreshFriendsList then
			BFL:ForceRefreshFriendsList()
		end
		self:RefreshContactMemoryTab()
	end

	table.insert(allFrames, Components:CreateHeader(tab, L.CONTACT_MEMORY_TITLE or "Private Notes"))
	table.insert(
		allFrames,
		Components:CreateLabel(
			tab,
			L.CONTACT_MEMORY_SETTINGS_ENABLE_DESC or "Adds local private notes for friends and ignored players.",
			true,
			{ r = 0.75, g = 0.75, b = 0.75, a = 1 }
		)
	)

	local contactMemoryTooltipDesc = L.CONTACT_MEMORY_SETTINGS_ENABLE_DESC
		or "Adds local private notes for friends and ignored players."
	if not betaEnabled then
		contactMemoryTooltipDesc = contactMemoryTooltipDesc
			.. "\n\n"
			.. (L.CONTACT_MEMORY_DISABLED_BETA or "Enable Beta Features to use Private Notes.")
	end

	local contactMemoryToggle = Components:CreateCheckbox(tab, {
		label = L.CONTACT_MEMORY_SETTINGS_ENABLE or "Private Notes (Beta)",
		initialValue = contactMemoryEnabled,
		disabled = not betaEnabled,
		callback = function(checked)
			if contactMemory and contactMemory.SetEnabled then
				contactMemory:SetEnabled(checked == true)
			else
				if type(BetterFriendlistDB.contactMemory) ~= "table" then
					BetterFriendlistDB.contactMemory = {}
				end
				BetterFriendlistDB.contactMemory.enabled = checked == true
				BFL.SettingsVersion = (BFL.SettingsVersion or 0) + 1
			end
			RefreshContactMemorySettings()
		end,
		tooltipTitle = L.CONTACT_MEMORY_SETTINGS_ENABLE or "Private Notes (Beta)",
		tooltipDesc = contactMemoryTooltipDesc,
	})
	table.insert(allFrames, contactMemoryToggle)

	local detailsDisabled = not betaEnabled or contactMemoryEnabled ~= true
	local function GetContactMemorySetting(key, defaultValue)
		if contactMemory and contactMemory.GetSetting then
			return contactMemory:GetSetting(key, defaultValue)
		end
		if type(contactMemoryDB) == "table" and type(contactMemoryDB.settings) == "table" then
			local value = contactMemoryDB.settings[key]
			if value ~= nil then
				return value
			end
		end
		return defaultValue
	end
	local function SetContactMemorySetting(key, value)
		if contactMemory and contactMemory.SetSetting then
			contactMemory:SetSetting(key, value == true)
			return
		end
		if type(BetterFriendlistDB.contactMemory) ~= "table" then
			BetterFriendlistDB.contactMemory = {}
		end
		if type(BetterFriendlistDB.contactMemory.settings) ~= "table" then
			BetterFriendlistDB.contactMemory.settings = {}
		end
		BetterFriendlistDB.contactMemory.settings[key] = value == true
		BFL.SettingsVersion = (BFL.SettingsVersion or 0) + 1
		if BFL.ForceRefreshFriendsList then
			BFL:ForceRefreshFriendsList()
		end
	end

	table.insert(allFrames, Components:CreateSpacer(tab))

	local contactMemoryTooltipToggle = Components:CreateCheckbox(tab, {
		label = L.CONTACT_MEMORY_SETTINGS_TOOLTIPS or "Show in Tooltips",
		initialValue = GetContactMemorySetting("showTooltipSection", true) == true,
		disabled = detailsDisabled,
		callback = function(checked)
			SetContactMemorySetting("showTooltipSection", checked == true)
		end,
		tooltipTitle = L.CONTACT_MEMORY_SETTINGS_TOOLTIPS or "Show in Tooltips",
		tooltipDesc = L.CONTACT_MEMORY_SETTINGS_TOOLTIPS_DESC
			or "Show private notes in supported friend and ignore tooltips.",
	})
	table.insert(allFrames, contactMemoryTooltipToggle)

	local contactMemoryStreamerToggle = Components:CreateCheckbox(tab, {
		label = L.CONTACT_MEMORY_SETTINGS_HIDE_STREAMER or "Hide in Streamer Mode",
		initialValue = GetContactMemorySetting("hideInStreamerMode", true) == true,
		disabled = detailsDisabled,
		callback = function(checked)
			SetContactMemorySetting("hideInStreamerMode", checked == true)
		end,
		tooltipTitle = L.CONTACT_MEMORY_SETTINGS_HIDE_STREAMER or "Hide in Streamer Mode",
		tooltipDesc = L.CONTACT_MEMORY_SETTINGS_HIDE_STREAMER_DESC
			or "Hide private notes while Streamer Mode is active.",
	})
	table.insert(allFrames, contactMemoryStreamerToggle)

	Components:AnchorChain(allFrames, -5)
	tab.components = allFrames
end

function Settings:RefreshFriendTagsTab()
	if not settingsFrame or not Components then
		return
	end

	local tab = self:GetFriendTagsTabHost()
	if not tab then
		return
	end

	local DB = GetDB()
	local FriendTags = BFL:GetModule("FriendTags")
	if not DB or not FriendTags then
		return
	end

	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}
	local function RefreshFriendTagsSettings()
		if BFL.ForceRefreshFriendsList then
			BFL:ForceRefreshFriendsList()
		end
		self:RefreshFriendTagsTab()
	end

	local ContactMemory = BFL:GetModule("ContactMemory")
	local contactMemoryEnabled = ContactMemory
		and ContactMemory.GetEnabledSetting
		and ContactMemory:GetEnabledSetting() == true

	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_TAB_FRIEND_TAGS or "Friend Tags"))
	table.insert(
		allFrames,
		Components:CreateLabel(
			tab,
			L.FRIEND_TAGS_SETTINGS_DESC
				or "Configure Blizzard-compatible friend tags, custom tags, and compact contact chips.",
			true,
			{ r = 0.75, g = 0.75, b = 0.75, a = 1 }
		)
	)

	local enableTags = Components:CreateCheckbox(tab, {
		label = L.FRIEND_TAGS_ENABLE or "Enable Friend Tags",
		initialValue = FriendTags:GetSetting("enabled", true) ~= false,
		callback = function(val)
			FriendTags:SetSetting("enabled", val == true, RefreshFriendTagsSettings)
		end,
		disabled = not contactMemoryEnabled,
		tooltipTitle = L.FRIEND_TAGS_ENABLE or "Enable Friend Tags",
		tooltipDesc = contactMemoryEnabled
				and (L.FRIEND_TAGS_ENABLE_DESC or "Adds Blizzard-compatible and custom tags for friends.")
			or (L.FRIEND_TAGS_REQUIRES_CONTACT_MEMORY or "Enable Private Notes first to use Friend Tags."),
	})
	table.insert(allFrames, enableTags)

	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateHeader(tab, L.FRIEND_TAGS_SETTINGS_VISIBILITY or "Visibility"))

	table.insert(allFrames, Components:CreateDoubleCheckbox(tab, {
		label = L.FRIEND_TAGS_SHOW_ROW_CHIPS or "Show Row Chips",
		initialValue = FriendTags:GetSetting("showRowChips", true) ~= false,
		callback = function(val)
			FriendTags:SetSetting("showRowChips", val == true, RefreshFriendTagsSettings)
		end,
		tooltipTitle = L.FRIEND_TAGS_SHOW_ROW_CHIPS or "Show Row Chips",
		tooltipDesc = L.FRIEND_TAGS_SHOW_ROW_CHIPS_DESC or "Show compact tag chips below friend rows.",
	}, {
		label = L.FRIEND_TAGS_SHOW_TOOLTIP_CHIPS or "Show Tooltip Chips",
		initialValue = FriendTags:GetSetting("showTooltipChips", true) ~= false,
		callback = function(val)
			FriendTags:SetSetting("showTooltipChips", val == true, RefreshFriendTagsSettings)
		end,
		tooltipTitle = L.FRIEND_TAGS_SHOW_TOOLTIP_CHIPS or "Show Tooltip Chips",
		tooltipDesc = L.FRIEND_TAGS_SHOW_TOOLTIP_CHIPS_DESC or "Show tags in BetterFriendlist tooltips.",
	}))

	table.insert(allFrames, Components:CreateDoubleCheckbox(tab, {
		label = L.FRIEND_TAGS_SHOW_BROKER_CHIPS or "Show Broker Chips",
		initialValue = FriendTags:GetSetting("showBrokerChips", true) ~= false,
		callback = function(val)
			FriendTags:SetSetting("showBrokerChips", val == true, RefreshFriendTagsSettings)
		end,
		tooltipTitle = L.FRIEND_TAGS_SHOW_BROKER_CHIPS or "Show Broker Chips",
		tooltipDesc = L.FRIEND_TAGS_SHOW_BROKER_CHIPS_DESC or "Show tags in the Data Broker tooltip.",
	}, {
		label = L.FRIEND_TAGS_STREAMER_MODE or "Show in Streamer Mode",
		initialValue = FriendTags:GetSetting("showTagsInStreamerMode", false) == true,
		callback = function(val)
			FriendTags:SetSetting("showTagsInStreamerMode", val == true, RefreshFriendTagsSettings)
		end,
		tooltipTitle = L.FRIEND_TAGS_STREAMER_MODE or "Show in Streamer Mode",
		tooltipDesc = L.FRIEND_TAGS_STREAMER_MODE_DESC or "Allow friend tags while Streamer Mode is active.",
	}))

	local compactOptions = {
		labels = {
			L.FRIEND_TAGS_COMPACT_HIDDEN or "Hidden",
			L.FRIEND_TAGS_COMPACT_ICON_ONLY or "Icons Only",
			L.FRIEND_TAGS_COMPACT_CHIP_LINE or "Chip Line",
		},
		values = { "hidden", "icon_only", "chip_line" },
	}
	local compactDropdown = Components:CreateDropdown(
		tab,
		L.FRIEND_TAGS_SETTINGS_COMPACT_MODE or "Compact Row Mode",
		compactOptions,
		function(value)
			return FriendTags:GetSetting("compactRowMode", "icon_only") == value
		end,
		function(value)
			FriendTags:SetSetting("compactRowMode", value, RefreshFriendTagsSettings)
		end
	)
	if compactDropdown.SetTooltip then
		compactDropdown:SetTooltip(
			L.FRIEND_TAGS_SETTINGS_COMPACT_MODE or "Compact Row Mode",
			L.FRIEND_TAGS_SETTINGS_COMPACT_MODE_DESC or "Choose how tags are shown when BetterFriendlist uses compact rows."
		)
	end
	table.insert(allFrames, compactDropdown)

	table.insert(allFrames, Components:CreateSlider(tab, L.FRIEND_TAGS_MAX_ROW_CHIPS or "Max Row Chips", 1, 8,
		FriendTags:GetSetting("maxRowChips", 3), function(value)
			return tostring(math.floor((tonumber(value) or 0) + 0.5))
		end, function(value)
			FriendTags:SetSetting("maxRowChips", math.floor((tonumber(value) or 3) + 0.5), RefreshFriendTagsSettings)
		end))

	table.insert(allFrames, Components:CreateSlider(tab, L.FRIEND_TAGS_MAX_TOOLTIP_CHIPS or "Max Tooltip Chips", 1, 20,
		FriendTags:GetSetting("maxTooltipChips", 8), function(value)
			return tostring(math.floor((tonumber(value) or 0) + 0.5))
		end, function(value)
			FriendTags:SetSetting("maxTooltipChips", math.floor((tonumber(value) or 8) + 0.5), RefreshFriendTagsSettings)
		end))

	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateHeader(tab, L.FRIEND_TAGS_SETTINGS_GROUPS or "Dynamic Tag Groups"))
	table.insert(allFrames, Components:CreateDoubleCheckbox(tab, {
		label = L.FRIEND_TAGS_SETTINGS_DYNAMIC_GROUPS or "Show Dynamic Tag Groups",
		initialValue = FriendTags:GetSetting("enableDynamicTagGroups", false) == true,
		callback = function(val)
			FriendTags:SetSetting("enableDynamicTagGroups", val == true, RefreshFriendTagsSettings)
		end,
		tooltipTitle = L.FRIEND_TAGS_SETTINGS_DYNAMIC_GROUPS or "Show Dynamic Tag Groups",
		tooltipDesc = L.FRIEND_TAGS_SETTINGS_DYNAMIC_GROUPS_DESC
			or "Add virtual groups for assigned tags without writing those groups to Blizzard notes.",
	}, {
		label = L.FRIEND_TAGS_SHOW_MENU_COUNTS or "Show Menu Counts",
		initialValue = FriendTags:GetSetting("showMenuTagCounts", true) ~= false,
		callback = function(val)
			FriendTags:SetSetting("showMenuTagCounts", val == true, RefreshFriendTagsSettings)
		end,
		tooltipTitle = L.FRIEND_TAGS_SHOW_MENU_COUNTS or "Show Menu Counts",
		tooltipDesc = L.FRIEND_TAGS_SHOW_MENU_COUNTS_DESC or "Show assigned tag counts in supported context menus.",
	}))

	table.insert(allFrames, Components:CreateDoubleCheckbox(tab, {
		label = L.FRIEND_TAGS_SEARCH_BLIZZARD or "Search Blizzard Tags",
		initialValue = FriendTags:GetSetting("includeBlizzardTagsInSearch", true) ~= false,
		callback = function(val)
			FriendTags:SetSetting("includeBlizzardTagsInSearch", val == true, RefreshFriendTagsSettings)
		end,
		tooltipTitle = L.FRIEND_TAGS_SEARCH_BLIZZARD or "Search Blizzard Tags",
		tooltipDesc = L.FRIEND_TAGS_SEARCH_BLIZZARD_DESC or "Include Blizzard-compatible tags in friend search.",
	}, {
		label = L.FRIEND_TAGS_SEARCH_CUSTOM or "Search Custom Tags",
		initialValue = FriendTags:GetSetting("includeCustomTagsInSearch", true) ~= false,
		callback = function(val)
			FriendTags:SetSetting("includeCustomTagsInSearch", val == true, RefreshFriendTagsSettings)
		end,
		tooltipTitle = L.FRIEND_TAGS_SEARCH_CUSTOM or "Search Custom Tags",
		tooltipDesc = L.FRIEND_TAGS_SEARCH_CUSTOM_DESC or "Include custom tags in friend search.",
	}))

	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateHeader(tab, L.FRIEND_TAGS_SETTINGS_EDITOR or "Tag Editor"))
	table.insert(
		allFrames,
		Components:CreateButton(tab, L.FRIEND_TAGS_EDITOR_OPEN or "Open Tag Editor", function()
			OpenLegacyFriendTagEditor()
		end, L.FRIEND_TAGS_EDITOR_DESC or "Customize labels, icons, colors, visibility, and custom tags.")
	)

	if FriendTags:IsBlizzardTagAPIAvailable() then
		local count = FriendTags:GetLocalBlizzardTagAssignmentCount()
		local handoffButton = Components:CreateButton(
			tab,
			string.format("%s (%d)", L.FRIEND_TAGS_SYNC_LOCAL_BUTTON or "Sync Local Blizzard Tags", count or 0),
			function()
				FriendTags:HandoffKnownLocalBlizzardTags(RefreshFriendTagsSettings)
			end,
			L.FRIEND_TAGS_SYNC_LOCAL_DESC
				or "Moves locally stored Blizzard-compatible tag selections to Blizzard's native 12.1 tag storage."
		)
		if count <= 0 or not FriendTags:AreBlizzardTagsEnabled() then
			handoffButton:Disable()
		end
		table.insert(allFrames, handoffButton)
	end

	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateHeader(tab, L.FRIEND_TAGS_SETTINGS_TAG_LIST or "Tags"))

	local definitions = FriendTags:GetAllTagDefinitions()
	if #definitions == 0 then
		table.insert(allFrames, Components:CreateLabel(tab, L.FRIEND_TAGS_SETTINGS_TAG_LIST_EMPTY or "No tags available.", true))
	else
		for _, tag in ipairs(definitions) do
			table.insert(allFrames, CreateLegacyFriendTagRow(tab, FriendTags, tag, RefreshFriendTagsSettings))
		end
	end

	Components:AnchorChain(allFrames, -5)
	tab.components = allFrames
end

function Settings:RefreshGuildTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.GuildTab then
		return
	end

	local tab = content.GuildTab
	local DB = GetDB()
	if not DB then
		return
	end

	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}
	local capability = BFL.GetGuildTabCapability and BFL:GetGuildTabCapability() or {}
	local guildControlsDisabled = capability.canShowSetting == false

	local function RefreshGuildFrame()
		local GuildFrame = BFL:GetModule("GuildFrame")
		if GuildFrame then
			if GuildFrame.EnsureEnabled then
				GuildFrame:EnsureEnabled()
			end
			if GuildFrame.RefreshHeaderControls then
				GuildFrame:RefreshHeaderControls()
			end
			if GuildFrame.Refresh then
				GuildFrame:Refresh()
			end
		end
	end

	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_TAB_GUILD or GUILD or "Guild"))
	table.insert(
		allFrames,
		Components:CreateLabel(
			tab,
			L.SETTINGS_GUILD_DESC
				or "Configure the beta Guild roster tab. The tab is only available while Beta Features are enabled.",
			true,
			{ r = 0.75, g = 0.75, b = 0.75, a = 1 }
		)
	)

	local enableGuildTab = Components:CreateCheckbox(tab, {
		label = L.SETTINGS_ENABLE_GUILD_TAB or "Enable Guild Tab",
		initialValue = DB:Get("enableGuildTab", false),
		callback = function(val)
			self:OnEnableGuildTabChanged(val)
		end,
		disabled = guildControlsDisabled,
		tooltipTitle = L.SETTINGS_ENABLE_GUILD_TAB or "Enable Guild Tab",
		tooltipDesc = L.SETTINGS_ENABLE_GUILD_TAB_DESC
			or "Shows the beta Guild roster tab with roster actions backed by the supported guild APIs.",
	})
	table.insert(allFrames, enableGuildTab)

	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_GUILD_ROSTER_HEADER or "Roster Defaults"))

	local filterOptions = {
		labels = {
			L.GUILD_FILTER_ONLINE or "Online",
			L.GUILD_FILTER_ALL or "All",
			L.GUILD_FILTER_OFFLINE or "Offline",
		},
		values = { "online", "all", "offline" },
	}
	local filterDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_GUILD_DEFAULT_FILTER or "Default Filter",
		filterOptions,
		function(val)
			return DB:Get("guildTabFilterMode", "online") == val
		end,
		function(val)
			DB:Set("guildTabFilterMode", val)
			local GuildFrame = BFL:GetModule("GuildFrame")
			if GuildFrame and GuildFrame.SetFilter then
				GuildFrame:SetFilter(val)
			else
				RefreshGuildFrame()
			end
		end
	)
	if filterDropdown.SetTooltip then
		filterDropdown:SetTooltip(
			L.SETTINGS_GUILD_DEFAULT_FILTER or "Default Filter",
			L.SETTINGS_GUILD_DEFAULT_FILTER_DESC or "Initial status filter used by the Guild tab."
		)
	end
	table.insert(allFrames, filterDropdown)

	local sortOptions = {
		labels = {
			RANK or "Rank",
			NAME or "Name",
			LEVEL or "Level",
			CLASS or "Class",
			ZONE or "Zone",
			STATUS or "Status",
			LASTONLINE or "Last Online",
		},
		values = { "rank", "name", "level", "class", "zone", "status", "lastonline" },
	}
	local sortDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_GUILD_DEFAULT_SORT or "Default Sort",
		sortOptions,
		function(val)
			return DB:Get("guildTabSortMode", "rank") == val
		end,
		function(val)
			DB:Set("guildTabSortMode", val)
			local GuildFrame = BFL:GetModule("GuildFrame")
			if GuildFrame and GuildFrame.SetSort then
				GuildFrame:SetSort(val, true)
			else
				RefreshGuildFrame()
			end
		end
	)
	if sortDropdown.SetTooltip then
		sortDropdown:SetTooltip(
			L.SETTINGS_GUILD_DEFAULT_SORT or "Default Sort",
			L.SETTINGS_GUILD_DEFAULT_SORT_DESC or "Initial sort mode used by the Guild tab."
		)
	end
	table.insert(allFrames, sortDropdown)

	local visualRow = Components:CreateDoubleCheckbox(tab, {
		label = L.SETTINGS_GUILD_SHOW_CLASS_ICONS or "Show Class Icons",
		initialValue = DB:Get("guildTabShowClassIcons", true),
		callback = function(val)
			DB:Set("guildTabShowClassIcons", val and true or false)
			RefreshGuildFrame()
		end,
		tooltipTitle = L.SETTINGS_GUILD_SHOW_CLASS_ICONS or "Show Class Icons",
		tooltipDesc = L.SETTINGS_GUILD_SHOW_CLASS_ICONS_DESC
			or "Display class icons in Guild roster rows.",
	}, {
		label = L.SETTINGS_GUILD_USE_NICKNAMES or "Use BFL Guild Nicknames",
		initialValue = DB:Get("guildTabUseNicknames", true),
		callback = function(val)
			DB:Set("guildTabUseNicknames", val and true or false)
			RefreshGuildFrame()
		end,
		tooltipTitle = L.SETTINGS_GUILD_USE_NICKNAMES or "Use BFL Guild Nicknames",
		tooltipDesc = L.SETTINGS_GUILD_USE_NICKNAMES_DESC
			or "Use local BetterFriendlist guild nicknames in the Guild tab.",
	})
	table.insert(allFrames, visualRow)

	Components:AnchorChain(allFrames, -5)
	tab.components = allFrames
end

function Settings:RefreshFontsTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.FontsTab then
		return
	end

	local tab = content.FontsTab
	local DB = GetDB()

	-- Clear existing content (but keep the tab frame itself)
	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}

	-- Font Library
	local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
	local fontList = LSM and LSM:List("font") or { "Friz Quadrata TT" }
	local fontPaths = {}
	for i, fontName in ipairs(fontList) do
		fontPaths[i] = BFL.FontManager:ResolveFontPath(fontName)
	end
	local fontOptions = { labels = fontList, values = fontList, fontPaths = fontPaths, useCheckboxes = true }

	local function RefreshAllConfiguredFonts()
		local FriendsList = BFL:GetModule("FriendsList")
		if FriendsList and FriendsList.InvalidateSettingsCache then
			FriendsList:InvalidateSettingsCache()
		end
		C_Timer.After(0.01, function()
			if BFL.ApplyTabFonts then
				BFL:ApplyTabFonts()
			end
			local RaidFrame = BFL:GetModule("RaidFrame")
			if RaidFrame and RaidFrame.UpdateAllMemberButtons then
				RaidFrame:UpdateAllMemberButtons()
			end
			local Broker = BFL:GetModule("Broker")
			if Broker and Broker.RefreshTooltip then
				Broker:RefreshTooltip()
			end
			local GBroker = BFL:GetModule("GuildBroker")
			if GBroker and GBroker.RefreshTooltip then
				GBroker:RefreshTooltip()
			end
			BFL:ForceRefreshFriendsList()
		end)
	end

	-- -------------------------------------------------------------------------
	-- Friend Name Settings
	local nameFontHeader = Components:CreateHeader(tab, L.SETTINGS_FRIEND_NAME_SETTINGS or "Friend Name Settings")
	table.insert(allFrames, nameFontHeader)

	-- Name Font Face
	local currentNameFont = DB:Get("fontFriendName", "Friz Quadrata TT")
	local currentNameSize = DB:Get("fontSizeFriendName", 12)
	local currentNameColor = DB:Get("fontColorFriendName", { r = 1, g = 1, b = 1, a = 1 })
	local nameFontDropdown
	nameFontDropdown = Components:CreateDropdown(
		tab,
		"Font:", -- Use generic label as per request to move code
		fontOptions,
		function(val)
			return val == currentNameFont
		end,
		function(val)
			DB:Set("fontFriendName", val)
			currentNameFont = val
			if nameFontDropdown.DropDown then
				BFL.SetDropdownText(nameFontDropdown.DropDown, val)
			end
			-- Defer update to next frame to ensure resource availability
			C_Timer.After(0.01, function()
				BFL:ForceRefreshFriendsList()
			end)
		end,
		FONT_SETTINGS_CONTROL_WIDTH
	)
	table.insert(allFrames, nameFontDropdown)

	-- Name Font Size
	local nameSizeSlider = Components:CreateSlider(
		tab,
		L.SETTINGS_FONT_SIZE_NUM or "Font Size:",
		8,
		24, -- Min/Max
		currentNameSize, -- Current Value
		function(val)
			return tostring(val)
		end, -- Label formatter
		function(val)
			DB:Set("fontSizeFriendName", val)
			-- Fix #35/#40: Invalidate settings cache to update dynamic row heights
			local FriendsList = BFL:GetModule("FriendsList")
			if FriendsList then
				FriendsList:InvalidateSettingsCache()
			end
			C_Timer.After(0.01, function()
				BFL:ForceRefreshFriendsList()
			end)
		end,
		FONT_SETTINGS_CONTROL_WIDTH
	)
	table.insert(allFrames, nameSizeSlider)

	local nameFlagsDropdown = CreateFontFlagsDropdown(
		tab,
		DB,
		"fontOutlineFriendName",
		RefreshAllConfiguredFonts,
		FONT_SETTINGS_CONTROL_WIDTH
	)
	table.insert(allFrames, nameFlagsDropdown)

	-- Name Font Color
	local nameColorPicker = Components:CreateColorPicker(
		tab,
		L.SETTINGS_FONT_COLOR,
		currentNameColor,
		function(r, g, b, a)
			DB:Set("fontColorFriendName", { r = r, g = g, b = b, a = a })
			C_Timer.After(0.01, function()
				BFL:ForceRefreshFriendsList()
			end)
		end
	)
	table.insert(allFrames, nameColorPicker)

	-- -------------------------------------------------------------------------
	-- Friend Info Settings
	-- -------------------------------------------------------------------------
	table.insert(allFrames, Components:CreateSpacer(tab))
	local infoFontHeader = Components:CreateHeader(tab, L.SETTINGS_FRIEND_INFO_SETTINGS or "Friend Info Settings")
	table.insert(allFrames, infoFontHeader)

	-- Info Font Face
	local currentInfoFont = DB:Get("fontFriendInfo", "Friz Quadrata TT")
	local currentInfoSize = DB:Get("fontSizeFriendInfo", 12)
	local currentInfoColor = DB:Get("fontColorFriendInfo", { r = 0.82, g = 0.82, b = 0.82, a = 1 })
	local infoFontDropdown
	infoFontDropdown = Components:CreateDropdown(
		tab,
		"Font:", -- Changed from "Font Face:" (User Request)
		fontOptions,
		function(val)
			return val == currentInfoFont
		end,
		function(val)
			DB:Set("fontFriendInfo", val)
			currentInfoFont = val
			if infoFontDropdown.DropDown then
				BFL.SetDropdownText(infoFontDropdown.DropDown, val)
			end
			-- Defer update to next frame
			C_Timer.After(0.01, function()
				BFL:ForceRefreshFriendsList()
			end)
		end,
		FONT_SETTINGS_CONTROL_WIDTH
	)
	table.insert(allFrames, infoFontDropdown)

	-- Info Font Size
	local infoSizeSlider = Components:CreateSlider(
		tab,
		L.SETTINGS_FONT_SIZE_NUM or "Font Size:",
		8,
		24, -- Min/Max
		currentInfoSize, -- Current Value
		function(val)
			return tostring(val)
		end, -- Label formatter
		function(val)
			DB:Set("fontSizeFriendInfo", val)
			-- Fix #35/#40: Invalidate settings cache to update dynamic row heights
			local FriendsList = BFL:GetModule("FriendsList")
			if FriendsList then
				FriendsList:InvalidateSettingsCache()
			end
			-- Defer update to next frame
			C_Timer.After(0.01, function()
				BFL:ForceRefreshFriendsList()
			end)
		end,
		FONT_SETTINGS_CONTROL_WIDTH
	)
	table.insert(allFrames, infoSizeSlider)

	local infoFlagsDropdown = CreateFontFlagsDropdown(
		tab,
		DB,
		"fontOutlineFriendInfo",
		RefreshAllConfiguredFonts,
		FONT_SETTINGS_CONTROL_WIDTH
	)
	table.insert(allFrames, infoFlagsDropdown)

	-- Info Font Color
	local infoColorPicker = Components:CreateColorPicker(
		tab,
		L.SETTINGS_FONT_COLOR,
		currentInfoColor,
		function(r, g, b, a)
			DB:Set("fontColorFriendInfo", { r = r, g = g, b = b, a = a })
			-- Defer update to next frame
			C_Timer.After(0.01, function()
				BFL:ForceRefreshFriendsList()
			end)
		end
	)
	table.insert(allFrames, infoColorPicker)

	-- -------------------------------------------------------------------------
	-- Tabs Text Settings
	-- -------------------------------------------------------------------------
	table.insert(allFrames, Components:CreateSpacer(tab))
	local tabsFontHeader = Components:CreateHeader(tab, L.SETTINGS_FONT_TABS_TITLE or "Tabs Text")
	table.insert(allFrames, tabsFontHeader)

	-- Determine defaults
	local _, defaultTabSize = _G.GameFontNormalSmall:GetFont()

	-- Tabs Font Face
	local currentTabFont = DB:Get("fontTabText", "Friz Quadrata TT")
	local currentTabSize = DB:Get("fontSizeTabText", defaultTabSize)
	local tabFontDropdown
	tabFontDropdown = Components:CreateDropdown(tab, "Font:", fontOptions, function(val)
		return val == currentTabFont
	end, function(val)
		DB:Set("fontTabText", val)
		currentTabFont = val
		if tabFontDropdown.DropDown then
			BFL.SetDropdownText(tabFontDropdown.DropDown, val)
		end
		C_Timer.After(0.01, function()
			BFL:ApplyTabFonts() -- Immediate Apply
			BFL:ForceRefreshFriendsList()
		end)
	end, FONT_SETTINGS_CONTROL_WIDTH)
	table.insert(allFrames, tabFontDropdown)

	-- Tabs Font Size
	local tabSizeSlider = Components:CreateSlider(
		tab,
		L.SETTINGS_FONT_SIZE_NUM or "Font Size:",
		8,
		24, -- Fix #40: Proportional tab scaling handles overflow
		currentTabSize,
		function(val)
			return tostring(val)
		end,
		function(val)
			DB:Set("fontSizeTabText", val)
			C_Timer.After(0.01, function()
				BFL:ApplyTabFonts() -- Immediate Apply
				BFL:ForceRefreshFriendsList()
			end)
		end,
		FONT_SETTINGS_CONTROL_WIDTH
	)
	table.insert(allFrames, tabSizeSlider)

	local tabFlagsDropdown = CreateFontFlagsDropdown(tab, DB, "fontOutlineTabText", function()
		C_Timer.After(0.01, function()
			BFL:ApplyTabFonts()
			BFL:ForceRefreshFriendsList()
		end)
	end, FONT_SETTINGS_CONTROL_WIDTH)
	table.insert(allFrames, tabFlagsDropdown)

	-- -------------------------------------------------------------------------
	-- Raid Name Settings
	-- -------------------------------------------------------------------------
	table.insert(allFrames, Components:CreateSpacer(tab))
	local raidFontHeader = Components:CreateHeader(tab, L.SETTINGS_FONT_RAID_TITLE or "Raid Name Text")
	table.insert(allFrames, raidFontHeader)

	-- Determine defaults for Raid (consistent with Tabs)
	local _, raidSizeDefault = _G.GameFontNormalSmall:GetFont()
	local defaultRaidSize = raidSizeDefault or 10

	-- Raid Font Face
	local currentRaidFont = DB:Get("fontRaidName", "Friz Quadrata TT")
	local currentRaidSize = DB:Get("fontSizeRaidName", defaultRaidSize)
	local raidFontDropdown
	raidFontDropdown = Components:CreateDropdown(tab, "Font:", fontOptions, function(val)
		return val == currentRaidFont
	end, function(val)
		DB:Set("fontRaidName", val)
		currentRaidFont = val
		if raidFontDropdown.DropDown then
			BFL.SetDropdownText(raidFontDropdown.DropDown, val)
		end
		C_Timer.After(0.01, function()
			local RaidFrame = BFL:GetModule("RaidFrame")
			if RaidFrame and RaidFrame.UpdateAllMemberButtons then
				RaidFrame:UpdateAllMemberButtons()
			end
			BFL:ForceRefreshFriendsList()
		end)
	end, FONT_SETTINGS_CONTROL_WIDTH)
	table.insert(allFrames, raidFontDropdown)

	-- Raid Font Size
	local raidSizeSlider = Components:CreateSlider(
		tab,
		L.SETTINGS_FONT_SIZE_NUM or "Font Size:",
		8,
		24,
		currentRaidSize,
		function(val)
			return tostring(val)
		end,
		function(val)
			DB:Set("fontSizeRaidName", val)
			C_Timer.After(0.01, function()
				local RaidFrame = BFL:GetModule("RaidFrame")
				if RaidFrame and RaidFrame.UpdateAllMemberButtons then
					RaidFrame:UpdateAllMemberButtons()
				end
				BFL:ForceRefreshFriendsList()
			end)
		end,
		FONT_SETTINGS_CONTROL_WIDTH
	)
	table.insert(allFrames, raidSizeSlider)

	local raidFlagsDropdown = CreateFontFlagsDropdown(tab, DB, "fontOutlineRaidName", function()
		C_Timer.After(0.01, function()
			local RaidFrame = BFL:GetModule("RaidFrame")
			if RaidFrame and RaidFrame.UpdateAllMemberButtons then
				RaidFrame:UpdateAllMemberButtons()
			end
			BFL:ForceRefreshFriendsList()
		end)
	end, FONT_SETTINGS_CONTROL_WIDTH)
	table.insert(allFrames, raidFlagsDropdown)

	-- Anchor all frames vertically
	Components:AnchorChain(allFrames, -5)

	-- Store components for cleanup
	tab.components = allFrames
end

-- Refresh Groups Tab with new component library
function Settings:RefreshGroupsTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.GroupsTab then
		return
	end

	local tab = content.GroupsTab
	local DB = GetDB()
	if not DB then
		return
	end

	-- Clear existing content using GetChildren for robustness against reference leaks
	local children = { tab:GetChildren() }
	for _, child in ipairs(children) do
		if child.Hide then
			child:Hide()
		end
	end
	-- Legacy compatibility (keep table clear)
	tab.components = {}

	local allFrames = {}

	-- Common Font Options
	local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
	local fontList = LSM and LSM:List("font") or { "Friz Quadrata TT" }
	local fontPaths = {}
	for i, fontName in ipairs(fontList) do
		fontPaths[i] = BFL.FontManager:ResolveFontPath(fontName)
	end
	local fontOptions = { labels = fontList, values = fontList, fontPaths = fontPaths, useCheckboxes = true }

	-- ===========================================
	-- MOVED SETTINGS (From General)
	-- ===========================================
	local headerSettingsHeader =
		Components:CreateHeader(tab, L.SETTINGS_GROUP_HEADER_SETTINGS or "Group Header Settings")
	table.insert(allFrames, headerSettingsHeader)

	-- Row 1: Group Header Count Format & Group Arrow
	local headerCountFormatOptions = {
		labels = { L.SETTINGS_HEADER_COUNT_VISIBLE, L.SETTINGS_HEADER_COUNT_ONLINE, L.SETTINGS_HEADER_COUNT_BOTH },
		values = { "visible", "online", "both" },
	}

	local headerCountDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_HEADER_COUNT_FORMAT,
		headerCountFormatOptions,
		function(val)
			return val == DB:Get("headerCountFormat", "visible")
		end,
		function(val)
			DB:Set("headerCountFormat", val)
			BFL:ForceRefreshFriendsList()
			self:RefreshGroupsTab() -- Self refresh to update visible state if needed
		end
	)
	-- Shift dropdown to prevent clipping
	if headerCountDropdown.DropDown then
		local point, relativeTo, relativePoint, xOfs, yOfs = headerCountDropdown.DropDown:GetPoint(1)
		if point then
			headerCountDropdown.DropDown:SetPoint(point, relativeTo, relativePoint, (xOfs or 0) + 10, yOfs or 0)
		end
	end
	table.insert(allFrames, headerCountDropdown)

	-- Group Header Alignment
	local groupHeaderAlignOptions = {
		labels = { L.SETTINGS_ALIGN_LEFT, L.SETTINGS_ALIGN_CENTER, L.SETTINGS_ALIGN_RIGHT },
		values = { "LEFT", "CENTER", "RIGHT" },
	}
	local groupHeaderAlignDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_GROUP_HEADER_ALIGN,
		groupHeaderAlignOptions,
		function(val)
			return val == DB:Get("groupHeaderAlign", "LEFT")
		end,
		function(val)
			DB:Set("groupHeaderAlign", val)
			BFL:ForceRefreshFriendsList()
			self:RefreshGroupsTab()
		end
	)
	-- Shift dropdown to prevent clipping
	if groupHeaderAlignDropdown.DropDown then
		local point, relativeTo, relativePoint, xOfs, yOfs = groupHeaderAlignDropdown.DropDown:GetPoint(1)
		if point then
			groupHeaderAlignDropdown.DropDown:SetPoint(point, relativeTo, relativePoint, (xOfs or 0) + 10, yOfs or 0)
		end
	end
	table.insert(allFrames, groupHeaderAlignDropdown)

	-- Group Arrow Alignment
	local groupArrowAlignOptions = {
		labels = { L.SETTINGS_ALIGN_LEFT, L.SETTINGS_ALIGN_CENTER, L.SETTINGS_ALIGN_RIGHT },
		values = { "LEFT", "CENTER", "RIGHT" },
	}
	local groupArrowAlignDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_GROUP_ARROW_ALIGN,
		groupArrowAlignOptions,
		function(val)
			return val == DB:Get("groupArrowAlign", "LEFT")
		end,
		function(val)
			DB:Set("groupArrowAlign", val)
			BFL:ForceRefreshFriendsList()
			self:RefreshGroupsTab()
		end
	)
	-- Shift dropdown to prevent clipping
	if groupArrowAlignDropdown.DropDown then
		local point, relativeTo, relativePoint, xOfs, yOfs = groupArrowAlignDropdown.DropDown:GetPoint(1)
		if point then
			groupArrowAlignDropdown.DropDown:SetPoint(point, relativeTo, relativePoint, (xOfs or 0) + 10, yOfs or 0)
		end
	end
	table.insert(allFrames, groupArrowAlignDropdown)

	-- Show Arrow Checkbox
	local showArrowCheckbox = Components:CreateCheckbox(
		tab,
		L.SETTINGS_SHOW_GROUP_ARROW,
		DB:Get("showGroupArrow", true),
		function(val)
			DB:Set("showGroupArrow", val)
			-- Force full rebuild of display list to ensure arrows update
			local FriendsList = BFL:GetModule("FriendsList")
			if FriendsList then
				FriendsList:InvalidateSettingsCache()
			end
			BFL:ForceRefreshFriendsList()
			self:RefreshGroupsTab()
		end
	)
	table.insert(allFrames, showArrowCheckbox)

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ===========================================
	-- NEW FONT SETTINGS
	-- ===========================================
	local fontHeader = Components:CreateHeader(tab, L.SETTINGS_GROUP_FONT_HEADER or "Group Header Font")
	table.insert(allFrames, fontHeader)

	-- Group Font Dropdown
	local currentGroupFont = DB:Get("fontGroupHeader", "Friz Quadrata TT")
	local groupFontDropdown
	groupFontDropdown = Components:CreateDropdown(tab, "Font:", fontOptions, function(val)
		return val == currentGroupFont
	end, function(val)
		DB:Set("fontGroupHeader", val)
		currentGroupFont = val
		if groupFontDropdown.DropDown then
			BFL.SetDropdownText(groupFontDropdown.DropDown, val)
		end
		-- Defer update
		C_Timer.After(0.01, function()
			BFL:ForceRefreshFriendsList()
		end)
	end)
	if groupFontDropdown.DropDown then
		local point, relativeTo, relativePoint, xOfs, yOfs = groupFontDropdown.DropDown:GetPoint(1)
		if point then
			groupFontDropdown.DropDown:SetPoint(point, relativeTo, relativePoint, (xOfs or 0) + 10, yOfs or 0)
		end
	end
	table.insert(allFrames, groupFontDropdown)

	-- Group Font Size
	local currentGroupSize = DB:Get("fontSizeGroupHeader", 12)
	local GroupSizeSlider = Components:CreateSlider(
		tab,
		L.SETTINGS_FONT_SIZE_NUM or "Font Size:",
		8,
		24, -- Min/Max
		currentGroupSize, -- Current Value
		function(val)
			return tostring(val)
		end, -- Label formatter
		function(val)
			DB:Set("fontSizeGroupHeader", val)
			C_Timer.After(0.01, function()
				BFL:ForceRefreshFriendsList()
			end)
		end
	)
	table.insert(allFrames, GroupSizeSlider)

	local groupFlagsDropdown = CreateFontFlagsDropdown(tab, DB, "fontOutlineGroupHeader", function()
		C_Timer.After(0.01, function()
			BFL:ForceRefreshFriendsList()
		end)
	end)
	table.insert(allFrames, groupFlagsDropdown)

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- Header: Group Order
	local orderHeader = Components:CreateHeader(tab, L.SETTINGS_GROUP_ORDER or "Group Order")
	table.insert(allFrames, orderHeader)

	-- Get ordered groups
	local Groups = BFL:GetModule("Groups")
	if not Groups then
		return
	end

	local function GetBuiltinDefaultName(groupId)
		if groupId == "favorites" then
			return L.GROUP_FAVORITES or "Favorites"
		end
		if groupId == "ingame" then
			return L.GROUP_INGAME or "In-Game"
		end
		if groupId == "recentlyadded" then
			return L.GROUP_RECENTLY_ADDED or "Recently Added"
		end
		if groupId == "nogroup" then
			return L.GROUP_NO_GROUP or "No Group"
		end
		return nil
	end

	local allGroups = Groups:GetAll()
	local groupOrder = DB:Get("groupOrder") or {}
	local orderedGroups = {}

	-- Build ordered list
	if #groupOrder > 0 then
		for i, groupId in ipairs(groupOrder) do
			local group = allGroups[groupId]
			if group then
				table.insert(orderedGroups, { id = groupId, name = group.name, order = i, builtin = group.builtin })
			end
		end
		-- Add any groups not in order
		for groupId, group in pairs(allGroups) do
			local found = false
			for _, ordered in ipairs(orderedGroups) do
				if ordered.id == groupId then
					found = true
					break
				end
			end
			if not found then
				table.insert(
					orderedGroups,
					{ id = groupId, name = group.name, order = #orderedGroups + 1, builtin = group.builtin }
				)
			end
		end
	else
		-- Default order: favorites, custom groups (sorted), nogroup
		if allGroups["favorites"] then
			table.insert(
				orderedGroups,
				{ id = "favorites", name = allGroups["favorites"].name, order = 1, builtin = true }
			)
		end

		local customGroups = {}
		for groupId, group in pairs(allGroups) do
			if groupId ~= "favorites" and groupId ~= "nogroup" then
				table.insert(customGroups, { id = groupId, name = group.name, builtin = group.builtin == true })
			end
		end
		table.sort(customGroups, function(a, b)
			return a.name < b.name
		end)

		for _, group in ipairs(customGroups) do
			table.insert(
				orderedGroups,
				{ id = group.id, name = group.name, order = #orderedGroups + 1, builtin = false }
			)
		end

		if allGroups["nogroup"] then
			table.insert(
				orderedGroups,
				{ id = "nogroup", name = allGroups["nogroup"].name, order = #orderedGroups + 1, builtin = true }
			)
		end
	end

	-- Create list items for each group
	local listItems = {}

	for i, groupData in ipairs(orderedGroups) do
		local isBuiltin = groupData.builtin
		local displayName = groupData.name
		if isBuiltin then
			local defaultName = GetBuiltinDefaultName(groupData.id)
			if defaultName and groupData.name and groupData.name ~= defaultName then
				displayName = string.format("%s (%s)", groupData.name, defaultName)
			end
		end

		local onDragStart = function(btn)
			-- Only update background highlights in OnUpdate to be efficient
			btn:SetScript("OnUpdate", function(self)
				for _, otherItem in ipairs(listItems) do
					if otherItem ~= self and otherItem:IsVisible() then
						if MouseIsOver(otherItem) then
							otherItem.bg:SetColorTexture(0.3, 0.3, 0.3, 0.7)
						else
							otherItem.bg:SetColorTexture(0.1, 0.1, 0.1, 0.5)
						end
					end
				end
			end)
		end

		local onDragStop = function(btn)
			btn:SetScript("OnUpdate", nil)

			local targetIndex = nil

			for _, otherItem in ipairs(listItems) do
				otherItem.bg:SetColorTexture(0.1, 0.1, 0.1, 0.5) -- Reset
				if otherItem ~= btn and otherItem:IsVisible() then
					if MouseIsOver(otherItem) then
						targetIndex = otherItem.orderIndex
					end
				end
			end

			if targetIndex and targetIndex ~= btn.orderIndex then
				-- Perform Move
				local itemToMove = table.remove(orderedGroups, btn.orderIndex)
				table.insert(orderedGroups, targetIndex, itemToMove)

				-- Save
				local newOrder = {}
				for _, g in ipairs(orderedGroups) do
					table.insert(newOrder, g.id)
				end
				DB:Set("groupOrder", newOrder)

				-- Refresh
				Groups:Initialize()
				self:RefreshGroupsTab()
				BFL:ForceRefreshFriendsList()
			end
		end

		local listItem = Components:CreateListItem(
			tab,
			displayName,
			i,
			onDragStart,
			onDragStop,
			-- Rename callback (allow for all groups)
			function()
				self:RenameGroup(groupData.id, groupData.name)
			end,
			-- Color callback (allow for all groups)
			function(colorSwatch)
				self:ShowColorPicker(groupData.id, groupData.name, colorSwatch)
			end,
				-- Delete callback (only for non-builtin groups)
			not isBuiltin and function()
					self:DeleteGroup(groupData.id, groupData.name)
				end or nil,
			-- Count Color Callback (New)
			function(colorSwatch, isReset)
				self:ShowGroupCountColorPicker(groupData.id, groupData.name, colorSwatch, isReset)
			end,
			-- Arrow Color Callback (New)
			function(colorSwatch, isReset)
				self:ShowGroupArrowColorPicker(groupData.id, groupData.name, colorSwatch, isReset)
			end,
			-- Initial Colors (New)
			{
				count = Groups:Get(groupData.id) and Groups:Get(groupData.id).countColor,
				arrow = Groups:Get(groupData.id) and Groups:Get(groupData.id).arrowColor,
				countSet = Groups:Get(groupData.id) and Groups:Get(groupData.id).countColor,
				arrowSet = Groups:Get(groupData.id) and Groups:Get(groupData.id).arrowColor,
				fallback = { r = 1, g = 1, b = 1 },
			}
		)

		-- Set initial color for all groups
		if listItem.colorSwatch then
			local group = Groups:Get(groupData.id)
			if group and group.color then
				local alpha = group.color.a
				if alpha == nil then
					alpha = 1
				end
				listItem.colorSwatch:SetColorTexture(group.color.r, group.color.g, group.color.b, alpha)
			else
				listItem.colorSwatch:SetColorTexture(1, 0.82, 0, 1)
			end
		end

		-- Update tooltips properties for new component structure
		if listItem.renameButton then
			listItem.renameButton:SetScript("OnEnter", function(self)
				BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
				BFL_Tooltip:SetText(L.SETTINGS_RENAME_GROUP, 1, 1, 1)
				BFL_Tooltip:AddLine(L.TOOLTIP_RENAME_DESC, 1, 1, 1, true)
				BFL_Tooltip:Show()
			end)
		end

		if listItem.colorButton then
			listItem.colorButton:SetScript("OnEnter", function(self)
				BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
				BFL_Tooltip:SetText(L.SETTINGS_GROUP_COLOR, GetAccentColor(1, 0.82, 0, 1))
				BFL_Tooltip:AddLine(L.TOOLTIP_GROUP_COLOR_DESC, 1, 1, 1, true)
				BFL_Tooltip:Show()
			end)
		end

		if listItem.deleteButton then
			listItem.deleteButton:SetScript("OnEnter", function(self)
				BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
				BFL_Tooltip:SetText(L.SETTINGS_DELETE_GROUP, 1, 1, 1)
				BFL_Tooltip:AddLine(L.TOOLTIP_DELETE_DESC, 1, 1, 1, true)
				BFL_Tooltip:Show()
			end)
		end

		table.insert(listItems, listItem)
		table.insert(allFrames, listItem)
	end

	-- Anchor all frames vertically
	Components:AnchorChain(allFrames, -5)

	-- Store components for cleanup
	tab.components = allFrames
end

-- Refresh Advanced Tab
-- Refresh Advanced Tab
function Settings:RefreshAdvancedTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.AdvancedTab then
		return
	end

	local tab = content.AdvancedTab

	-- Clear existing content (but keep the tab frame itself)
	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}

	-- Title
	local title = Components:CreateHeader(tab, L.SETTINGS_TAB_ADVANCED or "Advanced Settings")
	table.insert(allFrames, title)

	-- Description
	table.insert(allFrames, Components:CreateLabel(tab, L.SETTINGS_ADVANCED_DESC or "Advanced options and tools"))

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ===========================================
	-- FriendGroups Migration Section
	-- ===========================================
	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_MIGRATION_HEADER or "FriendGroups Migration"))

	-- Migration description
	table.insert(
		allFrames,
		Components:CreateLabel(
			tab,
			L.SETTINGS_MIGRATION_DESC
				or "Migrate groups and friend assignments from FriendGroups addon. This will parse group information from BattleNet notes and create corresponding groups in BetterFriendlist.",
			true
		)
	)

	-- Migration button
	local migrateButton = Components:CreateButton(tab, L.SETTINGS_MIGRATE_BTN or "Migrate from FriendGroups", function()
		self:ShowMigrationDialog()
	end, L.SETTINGS_MIGRATE_TOOLTIP or "Import groups from the FriendGroups addon")
	migrateButton:SetSize(200, 24)
	-- Center the migrate button
	migrateButton:ClearAllPoints()
	migrateButton:SetPoint("CENTER", tab, "CENTER", 0, 0) -- This point will be overridden by AnchorChain? No, AnchorChain sets TOP. We need a container if we want horizontal centering?
	-- Wait, Components:CreateButton returns a button. AnchorChain sets its TOP point. It usually lacks LEFT/RIGHT.
	-- If we want it centered, we need to wrap it or set a point that conflicts less?
	-- AnchorChain sets "TOP". If we set "CENTER" x-offset it might work if we set relative to parent center?
	-- But AnchorChain uses "TOP" relative to previous element.
	-- Ideally we put it in a container like btnRow.

	local migrateBtnRow = CreateFrame("Frame", nil, tab)
	migrateBtnRow:SetHeight(30)
	migrateBtnRow:SetPoint("LEFT", 20, 0)
	migrateBtnRow:SetPoint("RIGHT", -20, 0)

	migrateButton:SetParent(migrateBtnRow)
	migrateButton:ClearAllPoints()
	migrateButton:SetPoint("CENTER", migrateBtnRow, "CENTER", 0, 0)

	table.insert(allFrames, migrateBtnRow)

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ===========================================
	-- Export / Import Section
	-- ===========================================
	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_EXPORT_HEADER or "Export / Import Settings"))

	-- Export/Import description
	table.insert(
		allFrames,
		Components:CreateLabel(
			tab,
			L.SETTINGS_EXPORT_DESC
				or "Export your groups and friend assignments to share between characters or accounts. Perfect for players with multiple accounts who share Battle.net friends.",
			true
		)
	)

	-- Export Warning
	local exportWarning = Components:CreateLabel(
		tab,
		L.SETTINGS_EXPORT_WARNING or "|cffff0000Warning: Importing will replace ALL your groups and assignments!|r",
		true
	)
	table.insert(allFrames, exportWarning)

	-- Export/Import Buttons Row
	local btnRow = CreateFrame("Frame", nil, tab)
	btnRow:SetHeight(30)
	btnRow:SetPoint("LEFT", 20, 0)
	btnRow:SetPoint("RIGHT", -20, 0)

	-- Export button
	local exportButton = Components:CreateButton(btnRow, L.BUTTON_EXPORT, function()
		self:ShowExportDialog()
	end, L.SETTINGS_EXPORT_TOOLTIP or "Export your groups and friend assignments")
	exportButton:SetSize(140, 24)
	exportButton:SetPoint("RIGHT", btnRow, "CENTER", -5, 0)

	-- Import button
	local importButton = Components:CreateButton(btnRow, L.SETTINGS_IMPORT_BTN, function()
		self:ShowImportDialog()
	end, L.SETTINGS_IMPORT_TOOLTIP or "Import groups and friend assignments")
	importButton:SetSize(140, 24)
	importButton:SetPoint("LEFT", btnRow, "CENTER", 5, 0)

	table.insert(allFrames, btnRow)

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ===========================================
	-- Group Note Sync Section
	-- ===========================================
	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_SYNC_GROUPS_NOTE_HEADER or "Group Note Sync"))

	-- Description
	table.insert(
		allFrames,
		Components:CreateLabel(
			tab,
			L.SETTINGS_SYNC_GROUPS_NOTE_DESC
				or "Write group assignments into friend notes using the FriendGroups format (Note#Group1#Group2).",
			true
		)
	)

	-- Sync Groups to Note Toggle
	local syncNoteToggle = Components:CreateCheckbox(
		tab,
		L.SETTINGS_SYNC_GROUPS_NOTE or "Sync Groups to Friend Note",
		BetterFriendlistDB.syncGroupsToNote or false,
		function(checked)
			if checked then
				-- Show confirmation dialog before enabling
				StaticPopupDialogs["BETTERFRIENDLIST_SYNC_GROUPS_CONFIRM"] = {
					text = L.DIALOG_SYNC_GROUPS_CONFIRM_TEXT,
					button1 = L.DIALOG_SYNC_GROUPS_CONFIRM_BTN1 or "Enable",
					button2 = L.DIALOG_SYNC_GROUPS_CONFIRM_BTN2 or "Cancel",
					OnAccept = function()
						BetterFriendlistDB.syncGroupsToNote = true
						-- Increment settings version to invalidate caches
						if BFL.SettingsVersion then
							BFL.SettingsVersion = BFL.SettingsVersion + 1
						end
						-- Import existing group tags from notes before writing
						-- (prevents data loss for users coming from FriendGroups)
						local NoteSync = BFL:GetModule("NoteSync")
						if NoteSync then
							local importedGroups, importedAssignments = NoteSync:ImportGroupsFromNotes()
							if importedGroups > 0 then
								local importMsg = string.format(
									L.MSG_SYNC_GROUPS_IMPORTED
										or "Imported %d group(s) and %d assignment(s) from existing notes.",
									importedGroups,
									importedAssignments
								)
								BFL:DebugPrint("|cff00ff00BetterFriendlist:|r " .. importMsg)
								-- Reload groups from DB and force re-render
								local Groups = BFL:GetModule("Groups")
								if Groups and Groups.Initialize then
									Groups:Initialize()
								end
								BFL:ForceRefreshFriendsList()
								-- Refresh group list in Settings UI if open
								if BetterFriendlistSettingsFrame and BetterFriendlistSettingsFrame:IsShown() then
									Settings:RefreshGroupList()
								end
							end
						end
						-- Trigger full sync of all friend notes
						local NoteSync = BFL:GetModule("NoteSync")
						if NoteSync then
							NoteSync:SyncAllFriends()
						end
					end,
					OnCancel = function()
						-- Revert checkbox state
						BetterFriendlistDB.syncGroupsToNote = false
						-- Refresh the Advanced tab to reset the checkbox visual
						Settings:RefreshAdvancedTab()
					end,
					timeout = 0,
					whileDead = true,
					hideOnEscape = true,
					preferredIndex = 3,
				}
				StaticPopup_Show("BETTERFRIENDLIST_SYNC_GROUPS_CONFIRM")
			else
				BetterFriendlistDB.syncGroupsToNote = false
				-- Increment settings version to invalidate caches
				if BFL.SettingsVersion then
					BFL.SettingsVersion = BFL.SettingsVersion + 1
				end
				-- Ask user if they want to clean up notes
				StaticPopupDialogs["BETTERFRIENDLIST_SYNC_GROUPS_DISABLE"] = {
					text = L.DIALOG_SYNC_GROUPS_DISABLE_TEXT
						or "Group Note Sync has been disabled.\n\nWould you like to open the Note Cleanup Wizard to remove the group suffixes from your friend notes?",
					button1 = L.DIALOG_SYNC_GROUPS_DISABLE_BTN1 or "Open Cleanup Wizard",
					button2 = L.DIALOG_SYNC_GROUPS_DISABLE_BTN2 or "Keep Notes",
					OnAccept = function()
						local NoteCleanupWizard = BFL.NoteCleanupWizard
						if NoteCleanupWizard then
							NoteCleanupWizard:Show()
						end
					end,
					timeout = 0,
					whileDead = true,
					hideOnEscape = true,
					preferredIndex = 3,
				}
				StaticPopup_Show("BETTERFRIENDLIST_SYNC_GROUPS_DISABLE")
			end
		end
	)
	syncNoteToggle:SetTooltip(
		L.SETTINGS_SYNC_GROUPS_NOTE or "Sync Groups to Friend Note",
		L.SETTINGS_SYNC_GROUPS_NOTE_DESC or "Write group assignments into friend notes using the FriendGroups format."
	)
	table.insert(allFrames, syncNoteToggle)

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ===========================================
	-- Note Cleanup Section
	-- ===========================================
	table.insert(allFrames, Components:CreateHeader(tab, L.WIZARD_HEADER or "Note Cleanup"))

	-- Note Cleanup description
	table.insert(
		allFrames,
		Components:CreateLabel(
			tab,
			L.WIZARD_HEADER_DESC
				or "Remove FriendGroups suffixes from friend notes. Back up your notes first, then review and apply changes.",
			true
		)
	)

	-- Note Cleanup Wizard button
	local cleanupButton = Components:CreateButton(tab, L.WIZARD_BTN or "Note Cleanup Wizard", function()
		local NoteCleanupWizard = BFL.NoteCleanupWizard
		if NoteCleanupWizard then
			NoteCleanupWizard:Show()
		end
	end, L.WIZARD_BTN_TOOLTIP or "Open the wizard to clean up FriendGroups data from friend notes")
	cleanupButton:SetSize(200, 24)

	-- View Backup button
	local viewBackupButton = Components:CreateButton(tab, L.WIZARD_VIEW_BACKUP_BTN or "View Backup", function()
		local NoteCleanupWizard = BFL.NoteCleanupWizard
		if NoteCleanupWizard then
			NoteCleanupWizard:ShowBackupViewer()
		end
	end, L.WIZARD_VIEW_BACKUP_TOOLTIP or "Open the backup viewer to see all backed-up notes")
	viewBackupButton:SetSize(200, 24)

	local cleanupBtnRow = CreateFrame("Frame", nil, tab)
	cleanupBtnRow:SetHeight(30)
	cleanupBtnRow:SetPoint("LEFT", 20, 0)
	cleanupBtnRow:SetPoint("RIGHT", -20, 0)

	cleanupButton:SetParent(cleanupBtnRow)
	cleanupButton:ClearAllPoints()
	cleanupButton:SetPoint("CENTER", cleanupBtnRow, "CENTER", -110, 0)

	viewBackupButton:SetParent(cleanupBtnRow)
	viewBackupButton:ClearAllPoints()
	viewBackupButton:SetPoint("CENTER", cleanupBtnRow, "CENTER", 110, 0)

	table.insert(allFrames, cleanupBtnRow)

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ===========================================
	-- Taint-Free Whisper Section
	-- ===========================================
	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_TAINT_FREE_WHISPER or "Taint-Free Whisper"))

	-- Description
	table.insert(
		allFrames,
		Components:CreateLabel(
			tab,
			L.SETTINGS_TAINT_FREE_WHISPER_DESC
				or "Uses a built-in message box for whispering friends instead of opening the default chat.",
			true
		)
	)

	-- Taint-Free Whisper Toggle
	local taintFreeToggle = Components:CreateCheckbox(
		tab,
		L.SETTINGS_TAINT_FREE_WHISPER or "Taint-Free Whisper",
		BetterFriendlistDB.taintFreeWhisper or false,
		function(checked)
			BetterFriendlistDB.taintFreeWhisper = checked
			if not checked and BFL.CloseTaintFreeWhisper then
				BFL:CloseTaintFreeWhisper(true)
			end
			if BFL.SettingsVersion then
				BFL.SettingsVersion = BFL.SettingsVersion + 1
			end
		end
	)
	taintFreeToggle:SetTooltip(
		L.SETTINGS_TAINT_FREE_WHISPER or "Taint-Free Whisper",
		L.SETTINGS_TAINT_FREE_WHISPER_TOOLTIP
			or "Prevents chat taint errors caused by BetterFriendlist opening the Blizzard whisper box."
	)
	table.insert(allFrames, taintFreeToggle)

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	-- ===========================================
	-- Beta Features Section
	-- ===========================================
	local betaFeaturesHeader = Components:CreateHeader(tab, "|cffff8800" .. L.SETTINGS_BETA_FEATURES_TITLE .. "|r")
	self.legacyBetaFeaturesAnchor = betaFeaturesHeader
	table.insert(allFrames, betaFeaturesHeader)

	-- Beta features description
	table.insert(allFrames, Components:CreateLabel(tab, L.SETTINGS_BETA_FEATURES_DESC, true))

	-- Warning icon + text container
	local warningFrame = CreateFrame("Frame", nil, tab)
	warningFrame:SetHeight(45)
	warningFrame:SetPoint("LEFT", 20, 0)
	warningFrame:SetPoint("RIGHT", -20, 0)

	local warningIcon = warningFrame:CreateTexture(nil, "ARTWORK")
	warningIcon:SetSize(16, 16)
	warningIcon:SetPoint("TOPLEFT", 0, -2)
	warningIcon:SetTexture("Interface\\AddOns\\BetterFriendlist\\Icons\\alert-triangle")
	warningIcon:SetVertexColor(1, 0.65, 0)

	local warningText = warningFrame:CreateFontString(nil, "ARTWORK", "BetterFriendlistFontNormalSmall")
	warningText:SetPoint("LEFT", warningIcon, "RIGHT", 6, 0)
	warningText:SetPoint("RIGHT", warningFrame, "RIGHT", 0, 0)
	warningText:SetJustifyH("LEFT")
	warningText:SetWordWrap(true)
	warningText:SetText(L.SETTINGS_BETA_FEATURES_WARNING)
	warningText:SetTextColor(1, 0.53, 0) -- Orange

	table.insert(allFrames, warningFrame)

	-- Enable Beta Features Toggle
	local betaToggle = Components:CreateCheckbox(
		tab,
		L.SETTINGS_BETA_FEATURES_ENABLE,
		BetterFriendlistDB.enableBetaFeatures or false,
		function(checked)
			local DB = GetDB()
			if not DB then
				return
			end

			local ThemeManager = BFL:GetModule("ThemeManager")
			local oldStoredTheme = DB:Get("theme", "blizzard")
			local oldEffectiveTheme = BFL.GetEffectiveTheme and BFL:GetEffectiveTheme() or oldStoredTheme
			DB:Set("enableBetaFeatures", checked)
			local MenuBridge = BFL:GetModule("MenuBridge")
			if MenuBridge and MenuBridge.RefreshEnabledState then
				MenuBridge:RefreshEnabledState()
			end

			-- If disabling Beta and currently on ANY Beta tab, switch to General
			if not checked then
				-- Theme is stable, so disabling Beta must not force Dark/Custom to Blizzard.
				-- Other beta tabs still switch back to General below.
				-- ElvUI compatibility remains synced through the theme setting.
				-- Keep oldStoredTheme/oldEffectiveTheme so legacy ElvUI survives the toggle.
				-- Dark and Custom are refreshed through ApplyCurrentTheme below.
				-- ThemeManager:SetTheme("blizzard") is intentionally not called here.
				-- Beta Features now only gates the remaining experimental tabs and toggles.
				if oldStoredTheme == "elvui" or oldEffectiveTheme == "elvui" or DB:Get("enableElvUISkin", false) == true then
					DB:Set("enableElvUISkin", true)
					DB:Set("theme", "elvui")
					if ThemeManager and ThemeManager.ApplyCurrentTheme then
						ThemeManager:ApplyCurrentTheme("beta-disabled-elvui-legacy")
					end
				elseif ThemeManager and ThemeManager.ApplyCurrentTheme then
					ThemeManager:ApplyCurrentTheme("beta-disabled")
				end

				local betaTabIds = GetBetaTabIds()
				for _, betaTabId in ipairs(betaTabIds) do
					if currentTab == betaTabId then
						self:SelectCategory(1)
						break
					end
				end
			else
				if DB:Get("enableElvUISkin", false) == true and DB:Get("theme", "blizzard") == "blizzard" then
					DB:Set("theme", "elvui")
				end
				if ThemeManager and ThemeManager.ApplyCurrentTheme then
					ThemeManager:ApplyCurrentTheme("beta-enabled")
				end
			end

			local Registry = BFL:GetModule("FilterSortRegistry")
			if Registry and Registry.NormalizeCurrentSelections then
				Registry:NormalizeCurrentSelections()
			end
			if BFL.ForceRefreshFriendsList then
				BFL:ForceRefreshFriendsList()
			end

			-- Refresh Settings UI (show/hide Beta tabs)
			if BetterFriendlistSettings_RefreshTabs then
				BetterFriendlistSettings_RefreshTabs()
			end
			if currentTab == 1 then
				self:RefreshGeneralTab()
			elseif currentTab == 12 then
				self:RefreshGuildTab()
			end

			self:RefreshGuildTabVisibility()

			-- User feedback
			if checked then
				BFL:DebugPrint(L.SETTINGS_BETA_FEATURES_ENABLED)
				BFL:DebugPrint(L.SETTINGS_BETA_TABS_VISIBLE)
			else
				BFL:DebugPrint(L.SETTINGS_BETA_FEATURES_DISABLED)
				BFL:DebugPrint(L.SETTINGS_BETA_TABS_HIDDEN)
			end

			-- Data Broker is stable now; only ElvUI theme fallback may request a reload above.
			if currentTab == 4 then
				self:RefreshAdvancedTab()
			end
		end
	)
	betaToggle:SetTooltip(L.SETTINGS_BETA_FEATURES_TITLE, L.SETTINGS_BETA_FEATURES_TOOLTIP)
	table.insert(allFrames, betaToggle)

	local settingsCenterToggle = Components:CreateCheckbox(tab, {
		label = L.SETTINGS_ENABLE_SETTINGS_CENTER or "New Settings Center (Beta)",
		initialValue = BetterFriendlistDB.enableSettingsCenterBeta == true,
		callback = function(checked)
			self:OnSettingsCenterBetaChanged(checked)
		end,
		tooltipTitle = L.SETTINGS_ENABLE_SETTINGS_CENTER or "New Settings Center (Beta)",
		tooltipDesc = L.SETTINGS_ENABLE_SETTINGS_CENTER_DESC
			or "Use the new LibSettingsDesigner Settings Center instead of the classic settings window.",
	})
	table.insert(allFrames, settingsCenterToggle)

	if BFL.HasSecretValues then
		local bridgeToggle = Components:CreateCheckbox(tab, {
			label = L.SETTINGS_EXTERNAL_MENU_BRIDGE or "External AddOn Menu Bridge (Beta)",
			initialValue = BetterFriendlistDB.externalMenuBridgeEnabled or false,
			disabled = BetterFriendlistDB.enableBetaFeatures ~= true,
			callback = function(checked)
				local DB = GetDB()
				if not DB then
					return
				end
				DB:Set("externalMenuBridgeEnabled", checked == true)
				local MenuBridge = BFL:GetModule("MenuBridge")
				if MenuBridge and MenuBridge.RefreshEnabledState then
					MenuBridge:RefreshEnabledState()
				end
			end,
			tooltipTitle = L.SETTINGS_EXTERNAL_MENU_BRIDGE or "External AddOn Menu Bridge (Beta)",
			tooltipDesc = L.SETTINGS_EXTERNAL_MENU_BRIDGE_DESC
				or "Adds compatible AddOn actions to supported BetterFriendlist context menus.",
		})
		table.insert(allFrames, bridgeToggle)
	end

	-- Beta feature list (informational)
	-- Only show if there are actual beta features
	local hasBetaFeatures = true
	for _, tabDef in ipairs(TAB_DEFINITIONS) do
		if tabDef.beta and IsTabAvailableForClient(tabDef) then
			hasBetaFeatures = true
			break
		end
	end

	if hasBetaFeatures then
		local listContainer = CreateFrame("Frame", nil, tab)
		listContainer:SetPoint("LEFT", 20, 0)
		listContainer:SetPoint("RIGHT", -20, 0)

		local featureListTitle = listContainer:CreateFontString(nil, "ARTWORK", "BetterFriendlistFontNormal")
		featureListTitle:SetPoint("TOPLEFT", 0, 0)
		featureListTitle:SetText(L.SETTINGS_BETA_FEATURES_LIST)
		featureListTitle:SetTextColor(1, 0.53, 0) -- Orange

		-- Dynamic list from TAB_DEFINITIONS
		local listY = -20
		local settingsCenterIcon = listContainer:CreateTexture(nil, "ARTWORK")
		settingsCenterIcon:SetSize(14, 14)
		settingsCenterIcon:SetPoint("TOPLEFT", 5, listY)
		settingsCenterIcon:SetTexture("Interface\\AddOns\\BetterFriendlist\\Textures\\SettingsCenter\\page-advanced-beta.tga")
		settingsCenterIcon:SetVertexColor(1, 0.53, 0)

		local settingsCenterText = listContainer:CreateFontString(nil, "ARTWORK", "BetterFriendlistFontHighlightSmall")
		settingsCenterText:SetPoint("LEFT", settingsCenterIcon, "RIGHT", 6, 0)
		settingsCenterText:SetPoint("TOP", settingsCenterIcon, "TOP", 0, -1)
		settingsCenterText:SetText(L.SETTINGS_ENABLE_SETTINGS_CENTER or "New Settings Center (Beta)")
		listY = listY - 18

		for _, tabDef in ipairs(TAB_DEFINITIONS) do
			if tabDef.beta and IsTabAvailableForClient(tabDef) then
				local icon = listContainer:CreateTexture(nil, "ARTWORK")
				icon:SetSize(14, 14)
				icon:SetPoint("TOPLEFT", 5, listY)
				icon:SetTexture(tabDef.icon)
				icon:SetVertexColor(1, 0.53, 0) -- Orange

				local text = listContainer:CreateFontString(nil, "ARTWORK", "BetterFriendlistFontHighlightSmall")
				text:SetPoint("LEFT", icon, "RIGHT", 6, 0)
				text:SetPoint("TOP", icon, "TOP", 0, -1)
				text:SetText(tabDef.name)

				listY = listY - 18
			end
		end
		listContainer:SetHeight(math.abs(listY) + 10)
		table.insert(allFrames, listContainer)
	end

	-- Anchor all frames vertically
	Components:AnchorChain(allFrames, -15)

	-- Store components for cleanup
	tab.components = allFrames
end

-- ===========================================
-- QUICKFILTER & SORTER BUILDER TAB (Tab ID 11, Beta)
-- ===========================================
local BFL_FILTER_SORT_EDITOR_WIDTH = 470
local BFL_FILTER_SORT_EDITOR_CONTENT_WIDTH = 420
local BFL_FILTER_SORT_EDITOR_SIDE_PADDING = 18
local BFL_FILTER_SORT_EDITOR_MAX_DEPTH = 2
local BFL_FILTER_SORT_EDITOR_FORM_LABEL_WIDTH = 86

local function BFL_Settings_ApplyBuilderButtonStyle(button, state)
	if not (button and button.SetBackdrop) then
		return
	end
	local bg = { 0.030, 0.026, 0.018, 0.90 }
	local border = { 0.48, 0.34, 0.10, 0.72 }
	local textColor = { 1, 0.82, 0, 1 }
	if state == "hover" then
		bg = { 0.085, 0.070, 0.035, 0.95 }
		border = { 0.92, 0.72, 0.20, 0.95 }
		textColor = { 1, 0.92, 0.45, 1 }
	elseif state == "selected" then
		bg = { 0.070, 0.054, 0.020, 0.94 }
		border = { 0.90, 0.68, 0.18, 0.95 }
		textColor = { 1, 0.86, 0.12, 1 }
	elseif state == "disabled" then
		bg = { 0.018, 0.016, 0.012, 0.72 }
		border = { 0.20, 0.16, 0.08, 0.46 }
		textColor = { 0.45, 0.43, 0.36, 1 }
	end
	button:SetBackdrop({
		bgFile = "Interface\\Buttons\\WHITE8x8",
		edgeFile = "Interface\\Buttons\\WHITE8x8",
		tile = false,
		edgeSize = 1,
		insets = { left = 1, right = 1, top = 1, bottom = 1 },
	})
	button:SetBackdropColor(bg[1], bg[2], bg[3], bg[4])
	button:SetBackdropBorderColor(border[1], border[2], border[3], border[4])
	if button.Text then
		button.Text:SetTextColor(textColor[1], textColor[2], textColor[3], textColor[4])
	end
end

local function BFL_Settings_ApplyBuilderPanelStyle(frame, bg, border)
	if not (frame and frame.SetBackdrop) then
		return
	end
	frame:SetBackdrop({
		bgFile = "Interface\\Buttons\\WHITE8x8",
		edgeFile = "Interface\\Buttons\\WHITE8x8",
		tile = false,
		edgeSize = 1,
		insets = { left = 1, right = 1, top = 1, bottom = 1 },
	})
	bg = bg or { 0.012, 0.011, 0.009, 0.62 }
	border = border or { 0.42, 0.31, 0.10, 0.56 }
	frame:SetBackdropColor(bg[1], bg[2], bg[3], bg[4])
	frame:SetBackdropBorderColor(border[1], border[2], border[3], border[4])
end

local function BFL_Settings_ApplyBuilderRowStyle(row, state)
	if not (row and row.SetBackdrop) then
		return false
	end
	local bg = { 0.020, 0.018, 0.014, 0.64 }
	local border = { 0.18, 0.14, 0.08, 0.52 }
	if state == "selected" then
		bg = { 0.070, 0.054, 0.020, 0.88 }
		border = { 0.80, 0.60, 0.16, 0.78 }
	elseif state == "hover" then
		bg = { 0.055, 0.045, 0.022, 0.78 }
		border = { 0.62, 0.46, 0.14, 0.72 }
	elseif state == "target" then
		bg = { 0.095, 0.074, 0.025, 0.92 }
		border = { 0.96, 0.74, 0.16, 0.96 }
	end
	BFL_Settings_ApplyBuilderPanelStyle(row, bg, border)
	if row.bg then
		row.bg:Hide()
	end
	return true
end

local function BFL_Settings_GetStackHeight(frames, startY)
	local height = math.abs(startY or 0) + 24
	for _, frame in ipairs(frames or {}) do
		if frame and frame.GetHeight then
			height = height + (frame:GetHeight() or 0) + 10
		end
	end
	return height
end

local function BFL_Settings_ShouldUseBuilderSkin(parent)
	local frame = parent
	while frame do
		if frame.BFL_SettingsCenterBuilderSkin then
			return true
		end
		frame = frame.GetParent and frame:GetParent() or nil
	end
	return false
end

local function BFL_Settings_CreateSmallButton(parent, text, width, onClick)
	local useBuilderSkin = BFL_Settings_ShouldUseBuilderSkin(parent)
	local button = CreateFrame("Button", nil, parent, useBuilderSkin and "BackdropTemplate" or "UIPanelButtonTemplate")
	button:SetSize(width or 64, 22)
	button.BFL_SettingsCenterBuilderSkin = useBuilderSkin
	if useBuilderSkin then
		button.Text = button:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormalSmall")
		button.Text:SetPoint("LEFT", button, "LEFT", 7, 0)
		button.Text:SetPoint("RIGHT", button, "RIGHT", -7, 0)
		button.Text:SetJustifyH("CENTER")
		button.Text:SetWordWrap(false)
		button.SetText = function(self, value)
			self.Text:SetText(value or "")
		end
		button.GetText = function(self)
			return self.Text:GetText()
		end
	elseif button.SetNormalFontObject then
		button:SetNormalFontObject("BetterFriendlistFontNormalSmall")
		button:SetHighlightFontObject("BetterFriendlistFontHighlightSmall")
		button:SetDisabledFontObject("BetterFriendlistFontDisableSmall")
	end
	button:SetText(text)
	if useBuilderSkin then
		BFL_Settings_ApplyBuilderButtonStyle(button)
	end
	if onClick then
		button:SetScript("OnClick", onClick)
	end
	if useBuilderSkin then
		button:SetScript("OnEnter", function(self)
			if self:IsEnabled() then
				BFL_Settings_ApplyBuilderButtonStyle(self, "hover")
			end
		end)
		button:SetScript("OnLeave", function(self)
			BFL_Settings_ApplyBuilderButtonStyle(self, self:IsEnabled() and (self.bflSelected and "selected" or nil) or "disabled")
		end)
		button:SetScript("OnEnable", function(self)
			BFL_Settings_ApplyBuilderButtonStyle(self, self.bflSelected and "selected" or nil)
		end)
		button:SetScript("OnDisable", function(self)
			BFL_Settings_ApplyBuilderButtonStyle(self, "disabled")
		end)
	end
	return button
end

local function BFL_Settings_GetEditorIndent(depth)
	return BFL_FILTER_SORT_EDITOR_SIDE_PADDING + math.min(depth or 0, BFL_FILTER_SORT_EDITOR_MAX_DEPTH) * 12
end

local function BFL_Settings_CreateEditorRow(parent, height, leftOffset, rightOffset)
	local template = BackdropTemplateMixin and "BackdropTemplate" or nil
	local row = CreateFrame("Frame", nil, parent, template)
	row:SetHeight(height or 30)
	row:SetPoint("LEFT", leftOffset or BFL_FILTER_SORT_EDITOR_SIDE_PADDING, 0)
	row:SetPoint("RIGHT", -(rightOffset or BFL_FILTER_SORT_EDITOR_SIDE_PADDING), 0)
	if row.SetBackdrop then
		row:SetBackdrop({
			bgFile = "Interface\\Buttons\\WHITE8x8",
			edgeFile = "Interface\\Buttons\\WHITE8x8",
			edgeSize = 1,
			insets = { left = 1, right = 1, top = 1, bottom = 1 },
		})
		row:SetBackdropColor(0.02, 0.02, 0.02, 0.28)
		row:SetBackdropBorderColor(0.55, 0.55, 0.55, 0.16)
	end
	return row
end

local function BFL_Settings_CreateEditorFormRow(parent, labelText, height)
	local row = BFL_Settings_CreateEditorRow(parent, height or 42)
	local label = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlightSmall")
	label:SetPoint("LEFT", 10, 0)
	label:SetWidth(BFL_FILTER_SORT_EDITOR_FORM_LABEL_WIDTH)
	label:SetJustifyH("LEFT")
	label:SetWordWrap(false)
	label:SetText(labelText or "")
	return row, label
end

local function BFL_Settings_ValueToText(value)
	if type(value) == "table" then
		local parts = {}
		for _, item in ipairs(value) do
			table.insert(parts, tostring(item))
		end
		return table.concat(parts, ",")
	elseif value == true then
		return "true"
	elseif value == false then
		return "false"
	end
	return tostring(value or "")
end

local function BFL_Settings_ParseFilterValue(fieldDef, op, text)
	text = text or ""
	if op == "empty" or op == "notempty" then
		return nil
	end
	if op == "between" then
		local left, right = strsplit(",", text)
		return { tonumber(left) or 0, tonumber(right) or tonumber(left) or 0 }
	end
	if op == "in" or op == "notin" then
		local values = {}
		for value in string.gmatch(text, "([^,]+)") do
			value = strtrim(value)
			if value ~= "" then
				table.insert(values, value)
			end
		end
		return values
	end
	if fieldDef and fieldDef.type == "boolean" then
		local lower = text:lower()
		return lower == "true" or lower == "1" or lower == "yes" or lower == "on"
	elseif fieldDef and fieldDef.type == "number" then
		return tonumber(text) or 0
	end
	return text
end

local bflFilterSortDropdownCounter = 0

local function BFL_Settings_GetOptionLabel(entries, value)
	if not entries then
		return tostring(value or "")
	end
	for i, optionValue in ipairs(entries.values or {}) do
		if optionValue == value then
			return entries.labels and entries.labels[i] or tostring(value or "")
		end
	end
	return entries.labels and entries.labels[1] or tostring(value or "")
end

local function BFL_Settings_CreateInlineDropdown(parent, entries, selectedValue, onSelectionCallback, width)
	width = width or 120
	entries = entries or { labels = {}, values = {} }
	local labels = entries.labels or {}
	local values = entries.values or {}
	local dropdown

	local function UpdateText(value)
		local text = BFL_Settings_GetOptionLabel(entries, value)
		BFL.SetDropdownText(dropdown, text)
	end

	dropdown = Components and Components.CreateModernDropdown
		and Components:CreateModernDropdown(parent, width, "BetterFriendlistFontHighlightSmall")
		or nil
	if not dropdown then
		bflFilterSortDropdownCounter = bflFilterSortDropdownCounter + 1
		local dropdownName = "BFLFilterSortDropdown" .. tostring(bflFilterSortDropdownCounter)
		if BFL.CreateDropdown then
			dropdown = BFL.CreateDropdown(parent, dropdownName, math.max(40, width - 30), { forceLegacy = true })
		else
			dropdown = CreateFrame("Frame", dropdownName, parent, "UIDropDownMenuTemplate")
		end
		dropdown:SetSize(width + 24, 24)

		BFL.InitializeDropdown(dropdown, {
			labels = labels,
			values = values,
			getSelectionText = function(value)
				return BFL_Settings_GetOptionLabel(entries, value)
			end,
		}, function(value)
			return selectedValue == value
		end, function(value)
			selectedValue = value
			if onSelectionCallback then
				onSelectionCallback(value)
			end
			UpdateText(value)
		end)
		BFL.SetDropdownWidth(dropdown, math.max(40, width - 30))
		BFL.JustifyDropdownText(dropdown, "LEFT")
	else
		BFL.InitializeDropdown(dropdown, {
			labels = labels,
			values = values,
			getSelectionText = function(value)
				return BFL_Settings_GetOptionLabel(entries, value)
			end,
		}, function(value)
			return selectedValue == value
		end, function(value)
			selectedValue = value
			if onSelectionCallback then
				onSelectionCallback(value)
			end
			UpdateText(value)
		end, 300)
	end

	UpdateText(selectedValue)
	return dropdown
end

local function BFL_Settings_CreateOptions(labels, values)
	return { labels = labels, values = values }
end

local function BFL_Settings_GetOperatorLabel(operator)
	local key = "FILTER_BUILDER_OP_" .. tostring(operator or ""):upper()
	return L[key] or tostring(operator or "")
end

local function BFL_Settings_BuildOperatorOptions(operators)
	local labels = {}
	local values = {}
	for _, operator in ipairs(operators or {}) do
		table.insert(labels, BFL_Settings_GetOperatorLabel(operator))
		table.insert(values, operator)
	end
	return BFL_Settings_CreateOptions(labels, values)
end

local function BFL_Settings_BuildCatalogOptions(catalog, getLabel)
	local labels = {}
	local values = {}
	for _, field in ipairs(catalog or {}) do
		table.insert(labels, getLabel(field))
		table.insert(values, field.id)
	end
	return BFL_Settings_CreateOptions(labels, values)
end

local function BFL_Settings_BuildValueOptions(fieldDef)
	if not fieldDef then
		return nil
	end
	if fieldDef.type == "boolean" then
		return BFL_Settings_CreateOptions({
			L.FILTER_BUILDER_VALUE_TRUE or "True",
			L.FILTER_BUILDER_VALUE_FALSE or "False",
		}, { true, false })
	elseif fieldDef.values then
		local labels = {}
		local values = {}
		for _, value in ipairs(fieldDef.values) do
			table.insert(labels, tostring(value))
			table.insert(values, value)
		end
		return BFL_Settings_CreateOptions(labels, values)
	end
	return nil
end

local function BFL_Settings_GetDefaultValueForField(fieldDef)
	if fieldDef and fieldDef.type == "boolean" then
		return true
	elseif fieldDef and fieldDef.values and fieldDef.values[1] ~= nil then
		return fieldDef.values[1]
	end
	return ""
end

local function BFL_Settings_GetSorterFieldDefaultDirection(fieldDef)
	return (fieldDef and fieldDef.defaultDirection) or "asc"
end

function Settings:EnsureFilterSortEditorPanel()
	local customHost = self.filterSortCustomHost
	local anchorFrame = customHost and customHost.anchor
	if not settingsFrame and not anchorFrame then
		settingsFrame = BetterFriendlistSettingsFrame
	end
	anchorFrame = anchorFrame or settingsFrame
	if not anchorFrame then
		return nil
	end

	if customHost and customHost.inlineEditor then
		local parentFrame = customHost.editorParent or customHost.tab or anchorFrame
		local frame = self.filterSortInlineEditorFrame
		if not frame then
			frame = CreateFrame("Frame", nil, parentFrame, "BackdropTemplate")
			frame:SetHeight(220)
			frame.components = {}
			BFL_Settings_ApplyBuilderPanelStyle(frame, { 0.010, 0.010, 0.009, 0.58 }, { 0.50, 0.36, 0.10, 0.62 })

			frame.title = frame:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlight")
			frame.title:SetPoint("TOPLEFT", frame, "TOPLEFT", 14, -12)
			frame.title:SetPoint("RIGHT", frame, "RIGHT", -14, 0)
			frame.title:SetHeight(20)
			frame.title:SetJustifyH("LEFT")

			frame.Content = CreateFrame("Frame", nil, frame)
			frame.Content:SetPoint("TOPLEFT", frame, "TOPLEFT", 10, -42)
			frame.Content:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -10, -42)
			frame.Content:SetHeight(1)

			self.filterSortInlineEditorFrame = frame
		end

		parentFrame = customHost.editorParent or customHost.tab or anchorFrame
		frame.BFL_SettingsCenterBuilderSkin = customHost.settingsCenter == true
		frame:SetParent(parentFrame)
		frame:SetFrameStrata((anchorFrame.GetFrameStrata and anchorFrame:GetFrameStrata()) or "HIGH")
		frame:SetFrameLevel(((anchorFrame.GetFrameLevel and anchorFrame:GetFrameLevel()) or 1) + 4)
		frame:ClearAllPoints()
		frame:SetPoint("LEFT", parentFrame, "LEFT", 14, 0)
		frame:SetPoint("RIGHT", parentFrame, "RIGHT", -14, 0)
		frame:SetHeight(frame:GetHeight() > 0 and frame:GetHeight() or 220)
		if frame.Content then
			frame.Content:SetWidth(math.max(320, (parentFrame:GetWidth() or BFL_FILTER_SORT_EDITOR_CONTENT_WIDTH) - 28))
		end
		BFL_Settings_ApplyBuilderPanelStyle(frame, { 0.010, 0.010, 0.009, 0.58 }, { 0.50, 0.36, 0.10, 0.62 })
		return frame
	end

	local frame = self.filterSortEditorFrame
	if not frame then
		frame = CreateFrame("Frame", "BetterFriendlistFilterSortEditorFrame", anchorFrame, "ButtonFrameTemplate")
		frame:SetWidth(BFL_FILTER_SORT_EDITOR_WIDTH)
		frame:SetClampedToScreen(true)
		frame:EnableMouse(true)
		frame.components = {}

		if frame.portrait then
			frame.portrait:Hide()
		end
		if frame.PortraitContainer then
			frame.PortraitContainer:Hide()
		end
		if ButtonFrameTemplate_HidePortrait then
			ButtonFrameTemplate_HidePortrait(frame)
		end
		if ButtonFrameTemplate_HideAttic then
			ButtonFrameTemplate_HideAttic(frame)
		end

		if frame.CloseButton then
			frame.CloseButton:SetScript("OnClick", function()
				frame:Hide()
			end)
		end

		if frame.TitleContainer and frame.TitleContainer.TitleText then
			frame.title = frame.TitleContainer.TitleText
			frame.title:SetFontObject("BetterFriendlistFontNormal")
		elseif frame.TitleText then
			frame.title = frame.TitleText
			frame.title:SetFontObject("BetterFriendlistFontNormal")
		else
			frame.title = frame:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlight")
			frame.title:SetPoint("TOP", 0, -5)
		end

		frame.ScrollFrame = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
		frame.ScrollFrame:SetPoint("TOPLEFT", 12, -45)
		frame.ScrollFrame:SetPoint("BOTTOMRIGHT", -30, 8)

		frame.Content = CreateFrame("Frame", nil, frame.ScrollFrame)
		frame.Content:SetSize(BFL_FILTER_SORT_EDITOR_CONTENT_WIDTH, 500)
		frame.ScrollFrame:SetScrollChild(frame.Content)

		frame:SetScript("OnHide", function()
			if frame.ScrollFrame then
				SafeResetVerticalScroll(frame.ScrollFrame)
			end
		end)

		self.filterSortEditorFrame = frame
	end

	frame.BFL_SettingsCenterBuilderSkin = nil
	frame:SetParent(anchorFrame)
	frame:SetFrameStrata(anchorFrame:GetFrameStrata() or "HIGH")
	frame:SetFrameLevel((anchorFrame:GetFrameLevel() or 1) + 10)
	frame:ClearAllPoints()
	if customHost then
		frame:SetPoint("TOPRIGHT", anchorFrame, "TOPRIGHT", -38, -104)
		frame:SetPoint("BOTTOMRIGHT", anchorFrame, "BOTTOMRIGHT", -38, 36)
	else
		frame:SetPoint("TOPLEFT", settingsFrame, "TOPRIGHT", 5, 0)
		frame:SetPoint("BOTTOMLEFT", settingsFrame, "BOTTOMRIGHT", 5, 0)
	end
	frame:SetWidth(BFL_FILTER_SORT_EDITOR_WIDTH)
	if frame.Content then
		frame.Content:SetWidth(BFL_FILTER_SORT_EDITOR_CONTENT_WIDTH)
	end

	return frame
end

function Settings:HideFilterSortEditorPanel()
	if self.filterSortEditorFrame then
		self.filterSortEditorFrame:Hide()
	end
	if self.filterSortInlineEditorFrame then
		self.filterSortInlineEditorFrame:Hide()
	end
end

function Settings:ClearFilterSortEditorPanel()
	local customHost = self.filterSortCustomHost
	local frame = customHost and customHost.inlineEditor and self.filterSortInlineEditorFrame or self.filterSortEditorFrame
	if not frame then
		return
	end
	for _, component in ipairs(frame.components or {}) do
		if component.Hide then
			component:Hide()
		end
	end
	frame.components = {}
	if frame.ScrollFrame then
		SafeResetVerticalScroll(frame.ScrollFrame)
	end
end

function Settings:UpdateFilterSortEditorHeight(frames)
	local customHost = self.filterSortCustomHost
	local frame = customHost and customHost.inlineEditor and self.filterSortInlineEditorFrame or self.filterSortEditorFrame
	if not frame or not frame.Content then
		return
	end
	if customHost and customHost.inlineEditor then
		local height = BFL_Settings_GetStackHeight(frames, -5)
		height = math.max(96, height)
		frame.Content:SetHeight(height)
		frame:SetHeight(height + 54)
		self.filterSortInlineEditorHeight = height + 54
		return
	end

	local height = 80
	for _, child in ipairs(frames or {}) do
		height = height + (child:GetHeight() or 28) + 10
	end
	frame.Content:SetHeight(math.max(500, height))
end

function Settings:ShowFilterSortCreateDialog(kind, templateId)
	kind = kind == "sorter" and "sorter" or "filter"
	local tab = self:GetFilterSortTabHost()
	if not tab then
		return
	end

	tab.bflBuilderKind = kind
	self.filterSortBuilderKind = kind
	tab.bflFilterSortCreate = {
		kind = kind,
		templateId = templateId or "__blank",
	}
	self:RefreshFilterSortTab()
	self:RequestFilterSortLayout()
end

function Settings:IsFilterSortPreviewActive(kind, id)
	local state = self.filterSortPreview
	return state and state.active and state.kind == kind and state.id == id
end

function Settings:ApplyFilterSortPreview(kind, id)
	local Registry = BFL:GetModule("FilterSortRegistry")
	local FriendsList = BFL:GetModule("FriendsList")
	if not Registry or not FriendsList then
		return
	end

	local state = self.filterSortPreview
	if not state or not state.active then
		state = {
			active = true,
			previousFilter = FriendsList.filterMode or Registry.FALLBACK_FILTER,
			previousPrimary = FriendsList.sortMode or Registry.FALLBACK_PRIMARY_SORT,
			previousSecondary = FriendsList.secondarySort or Registry.FALLBACK_SECONDARY_SORT,
		}
		self.filterSortPreview = state
	end

	local filterMode = FriendsList.filterMode or state.previousFilter or Registry.FALLBACK_FILTER
	local primarySort = FriendsList.sortMode or state.previousPrimary or Registry.FALLBACK_PRIMARY_SORT
	local secondarySort = FriendsList.secondarySort or state.previousSecondary or Registry.FALLBACK_SECONDARY_SORT

	if kind == "filter" then
		filterMode = Registry:NormalizeQuickFilterId(id)
	else
		primarySort = Registry:NormalizePrimarySorterId(id)
		secondarySort = Registry:NormalizeSecondarySorterId(secondarySort, primarySort)
	end

	state.kind = kind
	state.id = id
	FriendsList.filterMode = filterMode
	FriendsList.sortMode = primarySort
	FriendsList.secondarySort = secondarySort
	if FriendsList.ApplySort then
		FriendsList:ApplySort()
	end
	BFL:ForceRefreshFriendsList()
	self:RefreshFilterSortTab()
	self:RequestFilterSortLayout()
end

function Settings:ClearFilterSortPreview(skipRefresh)
	local state = self.filterSortPreview
	if not state or not state.active then
		return
	end

	local FriendsList = BFL:GetModule("FriendsList")
	if FriendsList then
		FriendsList.filterMode = state.previousFilter or "all"
		FriendsList.sortMode = state.previousPrimary or "status"
		FriendsList.secondarySort = state.previousSecondary or "name"
		if FriendsList.ApplySort then
			FriendsList:ApplySort()
		end
		BFL:ForceRefreshFriendsList()
	end
	self.filterSortPreview = nil
	if not skipRefresh then
		self:RefreshFilterSortTab()
		self:RequestFilterSortLayout()
	end
end

function Settings:RefreshFilterSortTab()
	if not Components then
		return
	end

	local tab = self:GetFilterSortTabHost()

	local Registry = BFL:GetModule("FilterSortRegistry")
	if not tab or not Registry then
		return
	end
	Registry:NormalizeDB()

	HideComponentList(tab)
	tab.bflBuilderKind = tab.bflBuilderKind or self.filterSortBuilderKind or "filter"

	local selectedKind = tab.bflBuilderKind
	self.filterSortBuilderKind = selectedKind
	local selectedId = selectedKind == "filter" and tab.bflSelectedFilterId or tab.bflSelectedSorterId
	local allEntries = selectedKind == "filter" and Registry:GetQuickFilters(true) or Registry:GetSorters(true)
	if not selectedId and allEntries[1] then
		selectedId = allEntries[1].id
		if selectedKind == "filter" then
			tab.bflSelectedFilterId = selectedId
		else
			tab.bflSelectedSorterId = selectedId
		end
	end

	local DB = GetDB()
	local db = DB and DB:Get() or BetterFriendlistDB or {}
	local allFrames = {}
	local isSettingsCenterInline = self.filterSortCustomHost
		and self.filterSortCustomHost.inlineEditor
		and self.filterSortCustomHost.settingsCenter
	local filterSortViewMode = self.filterSortCustomHost and self.filterSortCustomHost.viewMode or "legacy"
	local isSettingsCenterOverview = isSettingsCenterInline and filterSortViewMode == "overview"
	local isSettingsCenterEditor = isSettingsCenterInline and filterSortViewMode == "editor"
	local filterFields = Registry:GetFilterFieldCatalog()
	local sortFields = Registry:GetSortFieldCatalog()
	local fieldById = {}
	local sortFieldById = {}
	for _, field in ipairs(filterFields) do
		fieldById[field.id] = field
	end
	for _, field in ipairs(sortFields) do
		sortFieldById[field.id] = field
	end
	local listParent = tab
	local listFrames = allFrames
	if self.filterSortCustomHost then
		self.filterSortCustomHost.editorParent = nil
	end
	local editorPanel = not isSettingsCenterOverview and self:EnsureFilterSortEditorPanel() or nil
	local editorContent = editorPanel and editorPanel.Content
	local editorFrames = {}
	if isSettingsCenterOverview then
		self:HideFilterSortEditorPanel()
	end
	if editorPanel then
		self:ClearFilterSortEditorPanel()
		editorPanel:Show()
	end

	local function Refresh()
		self:RefreshFilterSortTab()
		self:RequestFilterSortLayout()
	end

	local function OpenEntryEditor(kind, entryID, templateID)
		local host = self.filterSortCustomHost
		if host and host.openEditorPage then
			host.openEditorPage(kind, entryID, templateID)
			return true
		end
		return false
	end

	local function OpenManagerPage(kind)
		local host = self.filterSortCustomHost
		if host and host.openManagerPage then
			host.openManagerPage(kind)
			return true
		end
		return false
	end

	local function SelectEntry(kind, id)
		if isSettingsCenterOverview and OpenEntryEditor(kind, id) then
			return
		end
		tab.bflBuilderKind = kind
		self.filterSortBuilderKind = kind
		tab.bflFilterSortCreate = nil
		if kind == "filter" then
			tab.bflSelectedFilterId = id
		else
			tab.bflSelectedSorterId = id
		end
		Refresh()
	end

	local function GetFieldLabel(field)
		return (field and (L[field.labelKey] or field.id)) or "?"
	end

	local filterFieldOptions = BFL_Settings_BuildCatalogOptions(filterFields, GetFieldLabel)
	local sortFieldOptions = BFL_Settings_BuildCatalogOptions(sortFields, GetFieldLabel)
	local groupOperatorOptions = BFL_Settings_CreateOptions({ "AND", "OR" }, { "AND", "OR" })
	local directionOptions = BFL_Settings_CreateOptions({
		L.FILTER_BUILDER_DIRECTION_ASC or "ASC",
		L.FILTER_BUILDER_DIRECTION_DESC or "DESC",
	}, { "asc", "desc" })
	local emptyOptions = BFL_Settings_CreateOptions({
		L.FILTER_BUILDER_EMPTY_FIRST or "Empty first",
		L.FILTER_BUILDER_EMPTY_LAST or "Empty last",
	}, { "first", "last" })

	local function GetEntry(kind, id)
		if kind == "filter" then
			return Registry:ResolveQuickFilter(id, false)
		end
		return Registry:ResolveSorter(id, false, Registry.FALLBACK_PRIMARY_SORT)
	end

	local entryListItems = {}
	local draggingEntryRow = nil

	local function SetEntryRowBackground(row, isHover)
		if not row then
			return
		end
		if isSettingsCenterInline and BFL_Settings_ApplyBuilderRowStyle(row, isHover and "hover" or (row.entryId == selectedId and "selected" or nil)) then
			return
		end
		if not row.bg then
			return
		end
		if isHover then
			row.bg:SetColorTexture(0.3, 0.3, 0.3, 0.7)
		elseif row.entryId == selectedId then
			row.bg:SetColorTexture(0.22, 0.18, 0.08, 0.75)
		else
			row.bg:SetColorTexture(0.1, 0.1, 0.1, 0.5)
		end
	end

	local function MoveEntryToIndex(fromIndex, targetIndex)
		if not fromIndex or not targetIndex or fromIndex == targetIndex then
			return false
		end
		local movedEntry = table.remove(allEntries, fromIndex)
		if not movedEntry then
			return false
		end
		table.insert(allEntries, targetIndex, movedEntry)
		local orderedIds = {}
		for _, entry in ipairs(allEntries) do
			table.insert(orderedIds, entry.id)
		end
		if selectedKind == "filter" then
			DB:Set("quickFilterOrder", orderedIds)
		else
			DB:Set("sorterOrder", orderedIds)
		end
		return true
	end

	local function AddModeRow()
		local row = CreateFrame("Frame", nil, listParent, isSettingsCenterInline and "BackdropTemplate" or nil)
		row:SetHeight(isSettingsCenterInline and 44 or 28)
		row:SetPoint("LEFT", isSettingsCenterInline and 14 or 20, 0)
		row:SetPoint("RIGHT", isSettingsCenterInline and -14 or -20, 0)
		if isSettingsCenterInline then
			BFL_Settings_ApplyBuilderPanelStyle(row, { 0.014, 0.012, 0.010, 0.58 }, { 0.32, 0.24, 0.08, 0.52 })
		end

		local filterButton = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_FILTERS or "QuickFilters", 110, function()
			tab.bflBuilderKind = "filter"
			self.filterSortBuilderKind = "filter"
			tab.bflFilterSortCreate = nil
			Refresh()
		end)
		filterButton:SetPoint("LEFT", isSettingsCenterInline and 12 or 0, 0)
		if isSettingsCenterInline then
			filterButton.bflSelected = selectedKind == "filter"
			BFL_Settings_ApplyBuilderButtonStyle(filterButton, filterButton.bflSelected and "selected" or nil)
		else
			filterButton:SetEnabled(selectedKind ~= "filter")
		end

		local sorterButton = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_SORTERS or "Sorters", 90, function()
			tab.bflBuilderKind = "sorter"
			self.filterSortBuilderKind = "sorter"
			tab.bflFilterSortCreate = nil
			Refresh()
		end)
		sorterButton:SetPoint("LEFT", filterButton, "RIGHT", 8, 0)
		if isSettingsCenterInline then
			sorterButton.bflSelected = selectedKind == "sorter"
			BFL_Settings_ApplyBuilderButtonStyle(sorterButton, sorterButton.bflSelected and "selected" or nil)
		else
			sorterButton:SetEnabled(selectedKind ~= "sorter")
		end

		local createButton = BFL_Settings_CreateSmallButton(
			row,
			selectedKind == "filter" and (L.FILTER_BUILDER_ADD_FILTER or "New Filter")
				or (L.FILTER_BUILDER_ADD_SORTER or "New Sorter"),
			120,
			function()
				if not OpenEntryEditor(selectedKind) then
					self:ShowFilterSortCreateDialog(selectedKind)
				end
			end
		)
		createButton:SetPoint("RIGHT", isSettingsCenterInline and -12 or 0, 0)
		table.insert(listFrames, row)
	end

	local function AddEntryRow(entry, orderIndex)
		local row = CreateFrame("Button", nil, listParent, isSettingsCenterInline and "BackdropTemplate" or nil)
		row.entryId = entry.id
		row.orderIndex = orderIndex
		row:SetHeight(isSettingsCenterInline and 38 or 34)
		row:SetPoint("LEFT", isSettingsCenterInline and 14 or 20, 0)
		row:SetPoint("RIGHT", isSettingsCenterInline and -14 or -20, 0)
		row:EnableMouse(true)
		row:RegisterForClicks("LeftButtonUp")
		row:RegisterForDrag("LeftButton")
		row:SetScript("OnClick", function(selfRow)
			if selfRow.bflHandledMouseUp and GetTime() - selfRow.bflHandledMouseUp < 0.15 then
				return
			end
			if selfRow.bflLastDragStop and GetTime() - selfRow.bflLastDragStop < 0.15 then
				return
			end
			SelectEntry(selectedKind, entry.id)
		end)
		row:SetScript("OnMouseUp", function(selfRow, button)
			if button ~= "LeftButton" or not MouseIsOver(selfRow) then
				return
			end
			if selfRow.bflLastDragStop and GetTime() - selfRow.bflLastDragStop < 0.15 then
				return
			end
			selfRow.bflHandledMouseUp = GetTime()
			SelectEntry(selectedKind, entry.id)
		end)

		if not isSettingsCenterInline then
			row.bg = row:CreateTexture(nil, "BACKGROUND")
			row.bg:SetAllPoints()
		end
		SetEntryRowBackground(row, false)

		row.dragHandle = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormal")
		row.dragHandle:SetPoint("LEFT", isSettingsCenterInline and 10 or 5, 0)
		row.dragHandle:SetText(":::")
		row.dragHandle:SetTextColor(isSettingsCenterInline and 1 or 0.5, isSettingsCenterInline and 0.82 or 0.5, isSettingsCenterInline and 0 or 0.5, isSettingsCenterInline and 0.72 or 1)

		row.orderText = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontNormal")
		row.orderText:SetPoint("LEFT", row.dragHandle, "RIGHT", 5, 0)
		row.orderText:SetWidth(24)
		row.orderText:SetJustifyH("RIGHT")
		row.orderText:SetText(orderIndex)
		row.orderText:SetTextColor(0.7, 0.7, 0.7)

		local check = CreateFrame("CheckButton", nil, row, BFL.IsClassic and "InterfaceOptionsCheckButtonTemplate" or "SettingsCheckboxTemplate")
		check:SetSize(24, 24)
		check:SetPoint("LEFT", row.orderText, "RIGHT", 8, 0)
		check:SetChecked(entry.visible ~= false)
		check:SetScript("OnClick", function(self)
			if selectedKind == "filter" then
				Registry:SetQuickFilterVisibility(entry.id, self:GetChecked())
			else
				Registry:SetSorterVisibility(entry.id, self:GetChecked())
			end
			Refresh()
		end)

		local iconButton = CreateFrame("Button", nil, row)
		iconButton:SetSize(24, 24)
		iconButton:SetPoint("LEFT", check, "RIGHT", 4, 0)
		iconButton.texture = iconButton:CreateTexture(nil, "ARTWORK")
		iconButton.texture:SetAllPoints()
		iconButton.texture:SetTexture(entry.icon)
		iconButton:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
		iconButton:SetScript("OnClick", function()
			SelectEntry(selectedKind, entry.id)
		end)

		local name = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlightSmall")
		name:SetPoint("LEFT", iconButton, "RIGHT", 8, 0)
		name:SetPoint("RIGHT", row, "RIGHT", -130, 0)
		name:SetJustifyH("LEFT")
		name:SetWordWrap(false)
		local displayName = entry.name
		if not entry.isCustom then
			displayName = displayName .. " |cff888888(Built-in)|r"
		end
		name:SetText((selectedId == entry.id and "|cffffffff" or "") .. displayName)

		local copy = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_DUPLICATE or "Copy", 48, function()
			if not OpenEntryEditor(selectedKind, nil, entry.id) then
				self:ShowFilterSortCreateDialog(selectedKind, entry.id)
			end
		end)
		copy:SetPoint("RIGHT", row, "RIGHT", -66, 0)

		local delete = BFL_Settings_CreateSmallButton(row, entry.isCustom and (L.FILTER_BUILDER_DELETE or "Delete") or (L.FILTER_BUILDER_RESET or "Reset"), 58, function()
			if entry.isCustom then
				if selectedKind == "filter" then
					Registry:DeleteCustomQuickFilter(entry.id)
					tab.bflSelectedFilterId = nil
				else
					Registry:DeleteCustomSorter(entry.id)
					tab.bflSelectedSorterId = nil
				end
			else
				if selectedKind == "filter" then
					Registry:SetQuickFilterVisibility(entry.id, true)
				else
					Registry:SetSorterVisibility(entry.id, true)
				end
			end
			Refresh()
		end)
		delete:SetPoint("LEFT", copy, "RIGHT", 4, 0)
		row:SetScript("OnDragStart", function(selfRow)
			draggingEntryRow = selfRow
			local ghost = BFL:GetDragGhost()
			local accentR, accentG, accentB = GetAccentColor(1, 0.82, 0)
			ghost.text:SetText(entry.name)
			ghost.text:SetTextColor(accentR, accentG, accentB)
			ghost.stripe:SetColorTexture(accentR, accentG, accentB)
			ghost:SetSize(math.max(160, ghost.text:GetStringWidth() + 55), selfRow:GetHeight())
			ghost:Show()

			local cursorX, cursorY = GetCursorPosition()
			local scale = UIParent:GetEffectiveScale()
			ghost:ClearAllPoints()
			ghost:SetPoint("CENTER", UIParent, "BOTTOMLEFT", cursorX / scale, cursorY / scale)
			selfRow:SetAlpha(0.0)

			ghost:SetScript("OnUpdate", function(g)
				local cX, cY = GetCursorPosition()
				local s = UIParent:GetEffectiveScale()
				g:ClearAllPoints()
				g:SetPoint("CENTER", UIParent, "BOTTOMLEFT", cX / s, cY / s)
			end)

			selfRow:SetScript("OnUpdate", function(updateSelf)
				for _, otherItem in ipairs(entryListItems) do
					if otherItem ~= updateSelf and otherItem:IsVisible() then
						if isSettingsCenterInline and MouseIsOver(otherItem) then
							BFL_Settings_ApplyBuilderRowStyle(otherItem, "target")
						else
							SetEntryRowBackground(otherItem, MouseIsOver(otherItem))
						end
					end
				end
			end)
		end)

		row:SetScript("OnDragStop", function(selfRow)
			local ghost = BFL:GetDragGhost()
			ghost:Hide()
			ghost:SetScript("OnUpdate", nil)
			ghost:ClearAllPoints()
			draggingEntryRow = nil
			selfRow:SetAlpha(1.0)
			selfRow:SetScript("OnUpdate", nil)
			selfRow.bflLastDragStop = GetTime()

			local targetIndex = nil
			for _, otherItem in ipairs(entryListItems) do
				if otherItem ~= selfRow and otherItem:IsVisible() and MouseIsOver(otherItem) then
					targetIndex = otherItem.orderIndex
				end
				SetEntryRowBackground(otherItem, false)
			end

			if MoveEntryToIndex(selfRow.orderIndex, targetIndex) then
				Refresh()
			else
				SetEntryRowBackground(selfRow, false)
			end
		end)

		row:SetScript("OnEnter", function(selfRow)
			if not draggingEntryRow then
				SetEntryRowBackground(selfRow, true)
			end
		end)
		row:SetScript("OnLeave", function(selfRow)
			if not draggingEntryRow then
				SetEntryRowBackground(selfRow, false)
			end
		end)

		table.insert(entryListItems, row)
		table.insert(listFrames, row)
	end

	local function AddEditorNavigationRow()
		local row = CreateFrame("Frame", nil, tab, "BackdropTemplate")
		row:SetHeight(44)
		row:SetPoint("LEFT", 14, 0)
		row:SetPoint("RIGHT", -14, 0)
		BFL_Settings_ApplyBuilderPanelStyle(row, { 0.014, 0.012, 0.010, 0.58 }, { 0.32, 0.24, 0.08, 0.52 })

		local backButton = BFL_Settings_CreateSmallButton(
			row,
			L.SETTINGS_CENTER_BACK_TO_QUICKFILTER_MANAGER or "Back to QuickFilter & Sorter",
			220,
			function()
				if not OpenManagerPage(selectedKind) then
					Refresh()
				end
			end
		)
		backButton:SetPoint("LEFT", 12, 0)

		local hint = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontDisableSmall")
		hint:SetPoint("LEFT", backButton, "RIGHT", 12, 0)
		hint:SetPoint("RIGHT", row, "RIGHT", -12, 0)
		hint:SetJustifyH("LEFT")
		hint:SetWordWrap(false)
		hint:SetText(L.SETTINGS_CENTER_QUICKFILTER_MANAGER_HINT or "Return to the list to choose another entry.")

		table.insert(allFrames, row)
	end

	local function AddNameAndIconEditor(entry)
		local customTable = entry and entry.isCustom and (
			selectedKind == "filter" and db.customQuickFilters and db.customQuickFilters[entry.id]
				or db.customSorters and db.customSorters[entry.id]
		)
		if not customTable then
			local row = BFL_Settings_CreateEditorRow(editorContent, 54)
			local label = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlightSmall")
			label:SetPoint("LEFT", 12, 0)
			label:SetPoint("RIGHT", row, "RIGHT", -12, 0)
			label:SetJustifyH("LEFT")
			label:SetWordWrap(true)
			label:SetText(
				L.FILTER_BUILDER_BUILTIN_LOCKED
					or "Built-ins can be shown, hidden, reordered, or duplicated. Duplicate one to edit its rules."
			)
			table.insert(editorFrames, row)
			return nil
		end

		local nameRow, nameLabel = BFL_Settings_CreateEditorFormRow(editorContent, L.FILTER_BUILDER_NAME or "Name")
		local nameBox = CreateFrame("EditBox", nil, nameRow, "InputBoxTemplate")
		nameBox:SetHeight(20)
		nameBox:SetAutoFocus(false)
		nameBox:SetFontObject("BetterFriendlistFontHighlight")
		nameBox:SetText(customTable.name or "")
		nameBox:SetPoint("LEFT", nameLabel, "RIGHT", 12, 0)
		nameBox:SetPoint("RIGHT", nameRow, "RIGHT", -12, 0)
		local savedName = customTable.name or ""
		local function SaveName()
			local value = nameBox:GetText()
			if value == savedName then
				return
			end
			savedName = value
			if selectedKind == "filter" then
				Registry:UpdateCustomQuickFilter(entry.id, { name = value })
			else
				Registry:UpdateCustomSorter(entry.id, { name = value })
			end
			Refresh()
		end
		nameBox:SetScript("OnEnterPressed", function(selfBox)
			selfBox:ClearFocus()
		end)
		nameBox:SetScript("OnEditFocusLost", SaveName)
		table.insert(editorFrames, nameRow)

		local iconRow = BFL_Settings_CreateEditorFormRow(editorContent, L.FILTER_BUILDER_ICON or "Icon")
		local iconButton = BFL_Settings_CreateSmallButton(iconRow, BFL.FormatIcon(customTable.icon, 18) .. " " .. (L.ICON_SELECTOR_TITLE or "Select Icon"), 170, function(selfButton)
			if BFL.ShowIconSelector then
				BFL.ShowIconSelector(selfButton, customTable.icon, function(iconRef)
					if selectedKind == "filter" then
						Registry:UpdateCustomQuickFilter(entry.id, { icon = iconRef })
					else
						Registry:UpdateCustomSorter(entry.id, { icon = iconRef })
					end
					Refresh()
				end)
			end
		end)
		iconButton:SetPoint("RIGHT", iconRow, "RIGHT", -12, 0)
		table.insert(editorFrames, iconRow)
		return customTable
	end

	local function AddPreviewRow(entry, matches, total)
		local row = BFL_Settings_CreateEditorRow(editorContent, 58)
		local isActive = self:IsFilterSortPreviewActive(selectedKind, entry.id)

		local label = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlightSmall")
		label:SetPoint("TOPLEFT", row, "TOPLEFT", 12, -11)
		label:SetPoint("TOPRIGHT", row, "TOPRIGHT", -12, -11)
		label:SetJustifyH("LEFT")
		label:SetWordWrap(false)
		if selectedKind == "filter" then
			label:SetText(string.format(L.FILTER_BUILDER_PREVIEW_COUNT or "Preview: %d / %d friends match", matches or 0, total or 0))
		else
			label:SetText(L.FILTER_BUILDER_PREVIEW_SORTER or "Preview selected sorter")
		end

		local previewButton = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_PREVIEW_APPLY or "Preview", 92, function()
			self:ApplyFilterSortPreview(selectedKind, entry.id)
		end)

		if isActive then
			local stopButton = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_PREVIEW_STOP or "Stop Preview", 90, function()
				self:ClearFilterSortPreview()
			end)
			stopButton:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", -10, 8)
			previewButton:SetPoint("RIGHT", stopButton, "LEFT", -8, 0)
		else
			previewButton:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", -10, 8)
		end

		table.insert(editorFrames, row)
	end

	local function AddEntryActionsRow(entry)
		local row = BFL_Settings_CreateEditorRow(editorContent, 42)
		local duplicateButton = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_DUPLICATE or "Copy", 90, function()
			self:ShowFilterSortCreateDialog(selectedKind, entry.id)
		end)
		duplicateButton:SetPoint("RIGHT", row, "RIGHT", -12, 0)

		local resetOrDeleteButton = BFL_Settings_CreateSmallButton(
			row,
			entry.isCustom and (L.FILTER_BUILDER_DELETE or "Delete") or (L.FILTER_BUILDER_RESET or "Reset"),
			90,
			function()
				if entry.isCustom then
					if selectedKind == "filter" then
						Registry:DeleteCustomQuickFilter(entry.id)
						tab.bflSelectedFilterId = nil
					else
						Registry:DeleteCustomSorter(entry.id)
						tab.bflSelectedSorterId = nil
					end
				elseif selectedKind == "filter" then
					Registry:SetQuickFilterVisibility(entry.id, true)
				else
					Registry:SetSorterVisibility(entry.id, true)
				end
				Refresh()
			end
		)
		resetOrDeleteButton:SetPoint("RIGHT", duplicateButton, "LEFT", -8, 0)
		table.insert(editorFrames, row)
	end

	local function AddCreateEditor(kind, templateId)
		kind = kind == "sorter" and "sorter" or "filter"
		local entries = kind == "filter" and Registry:GetQuickFilters(true) or Registry:GetSorters(true)
		local selectedTemplate = templateId or "__blank"
		local templateEntry = selectedTemplate ~= "__blank"
			and (kind == "filter" and Registry:ResolveQuickFilter(selectedTemplate, false)
				or Registry:ResolveSorter(selectedTemplate, false, Registry.FALLBACK_PRIMARY_SORT))
			or nil
		local iconRef = templateEntry and templateEntry.icon
			or (kind == "filter" and "Interface\\AddOns\\BetterFriendlist\\Icons\\filter-all"
				or "Interface\\AddOns\\BetterFriendlist\\Icons\\sliders")
		local defaultName = kind == "filter" and (L.FILTER_BUILDER_NEW_FILTER or "Custom Filter")
			or (L.FILTER_BUILDER_NEW_SORTER or "Custom Sorter")
		if templateEntry then
			defaultName = templateEntry.name .. " " .. (L.FILTER_BUILDER_COPY_SUFFIX or "Copy")
		end

		local nameRow, nameLabel = BFL_Settings_CreateEditorFormRow(editorContent, L.FILTER_BUILDER_NAME or "Name")
		local nameBox = CreateFrame("EditBox", nil, nameRow, "InputBoxTemplate")
		nameBox:SetHeight(20)
		nameBox:SetAutoFocus(false)
		nameBox:SetFontObject("BetterFriendlistFontHighlight")
		nameBox:SetText(defaultName)
		nameBox:SetPoint("LEFT", nameLabel, "RIGHT", 12, 0)
		nameBox:SetPoint("RIGHT", nameRow, "RIGHT", -12, 0)
		table.insert(editorFrames, nameRow)

		local templateLabels = { L.FILTER_BUILDER_TEMPLATE_BLANK or "Blank" }
		local templateValues = { "__blank" }
		for _, entry in ipairs(entries) do
			table.insert(templateLabels, (entry.isCustom and "" or "|cff888888") .. entry.name .. (entry.isCustom and "" or "|r"))
			table.insert(templateValues, entry.id)
		end

		local templateRow, templateLabel = BFL_Settings_CreateEditorFormRow(editorContent, L.FILTER_BUILDER_TEMPLATE or "Template")
		local templateDropdown =
			BFL_Settings_CreateInlineDropdown(templateRow, BFL_Settings_CreateOptions(templateLabels, templateValues), selectedTemplate, function(value)
				selectedTemplate = value
				local selectedEntry = value ~= "__blank"
					and (kind == "filter" and Registry:ResolveQuickFilter(value, false)
						or Registry:ResolveSorter(value, false, Registry.FALLBACK_PRIMARY_SORT))
					or nil
				if selectedEntry then
					iconRef = selectedEntry.icon or iconRef
					nameBox:SetText(selectedEntry.name .. " " .. (L.FILTER_BUILDER_COPY_SUFFIX or "Copy"))
				else
					iconRef = kind == "filter" and "Interface\\AddOns\\BetterFriendlist\\Icons\\filter-all"
						or "Interface\\AddOns\\BetterFriendlist\\Icons\\sliders"
					nameBox:SetText(kind == "filter" and (L.FILTER_BUILDER_NEW_FILTER or "Custom Filter") or (L.FILTER_BUILDER_NEW_SORTER or "Custom Sorter"))
				end
				if editorPanel and editorPanel.iconButton then
					editorPanel.iconButton:SetText(BFL.FormatIcon(iconRef, 18) .. " " .. (L.ICON_SELECTOR_TITLE or "Select Icon"))
				end
			end, 230)
		templateDropdown:SetPoint("LEFT", templateLabel, "RIGHT", 12, 0)
		table.insert(editorFrames, templateRow)

		local iconRow = BFL_Settings_CreateEditorFormRow(editorContent, L.FILTER_BUILDER_ICON or "Icon")
		local iconButton = BFL_Settings_CreateSmallButton(iconRow, BFL.FormatIcon(iconRef, 18) .. " " .. (L.ICON_SELECTOR_TITLE or "Select Icon"), 190, function(selfButton)
			if BFL.ShowIconSelector then
				BFL.ShowIconSelector(selfButton, iconRef, function(selectedIcon)
					iconRef = selectedIcon
					selfButton:SetText(BFL.FormatIcon(iconRef, 18) .. " " .. (L.ICON_SELECTOR_TITLE or "Select Icon"))
				end)
			end
		end)
		iconButton:SetPoint("RIGHT", iconRow, "RIGHT", -12, 0)
		if editorPanel then
			editorPanel.iconButton = iconButton
		end
		table.insert(editorFrames, iconRow)

		local actions = BFL_Settings_CreateEditorRow(editorContent, 42)
		local createButton = BFL_Settings_CreateSmallButton(actions, L.FILTER_BUILDER_CREATE or "Create", 100, function()
			local sourceId = selectedTemplate ~= "__blank" and selectedTemplate or nil
			local id
			if kind == "filter" then
				id = Registry:CreateCustomQuickFilter(sourceId)
				if id then
					Registry:UpdateCustomQuickFilter(id, { name = nameBox:GetText(), icon = iconRef })
					tab.bflSelectedFilterId = id
				end
			else
				id = Registry:CreateCustomSorter(sourceId)
				if id then
					Registry:UpdateCustomSorter(id, { name = nameBox:GetText(), icon = iconRef })
					tab.bflSelectedSorterId = id
				end
			end
			if id then
				tab.bflBuilderKind = kind
				self.filterSortBuilderKind = kind
				tab.bflFilterSortCreate = nil
				Refresh()
			end
		end)
		createButton:SetPoint("RIGHT", actions, "RIGHT", -12, 0)
		local cancelButton = BFL_Settings_CreateSmallButton(actions, L.BUTTON_CANCEL or "Cancel", 90, function()
			tab.bflFilterSortCreate = nil
			Refresh()
		end)
		cancelButton:SetPoint("RIGHT", createButton, "LEFT", -8, 0)
		table.insert(editorFrames, actions)

		nameBox:SetFocus()
		nameBox:HighlightText()
	end

	local function SaveFilterAST(filter)
		Registry:UpdateCustomQuickFilter(selectedId, { ast = filter.ast })
		BFL:ForceRefreshFriendsList()
	end

	local function AddFilterNodeRows(filter, parentNode, depth, ownerChildren, ownerIndex)
		depth = depth or 0
		parentNode.children = parentNode.children or {}
		local canDeleteGroup = ownerChildren and ownerIndex

		local groupRow = BFL_Settings_CreateEditorRow(editorContent, 58, BFL_Settings_GetEditorIndent(depth), BFL_FILTER_SORT_EDITOR_SIDE_PADDING)
		local title = groupRow:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlightSmall")
		title:SetPoint("TOPLEFT", groupRow, "TOPLEFT", 10, -12)
		title:SetWidth(46)
		title:SetJustifyH("LEFT")
		title:SetText((L.FILTER_BUILDER_GROUP or "Group") .. ":")
		local opDropdown = BFL_Settings_CreateInlineDropdown(groupRow, groupOperatorOptions, parentNode.op == "OR" and "OR" or "AND", function(value)
			parentNode.op = value == "OR" and "OR" or "AND"
			SaveFilterAST(filter)
			Refresh()
		end, 66)
		opDropdown:SetPoint("LEFT", title, "RIGHT", 8, 0)
		local notButton = BFL_Settings_CreateSmallButton(groupRow, parentNode.negate and "NOT" or "not", 44, function()
			parentNode.negate = not parentNode.negate
			SaveFilterAST(filter)
			Refresh()
		end)
		notButton:SetPoint("TOPRIGHT", groupRow, "TOPRIGHT", -10, -7)
		local addCondition = BFL_Settings_CreateSmallButton(groupRow, L.FILTER_BUILDER_ADD_CONDITION or "Add Condition", 112, function()
			table.insert(parentNode.children, { type = "condition", field = "online", op = "is", value = true })
			SaveFilterAST(filter)
			Refresh()
		end)
		local addGroup = BFL_Settings_CreateSmallButton(groupRow, L.FILTER_BUILDER_ADD_GROUP or "Add Group", 82, function()
			table.insert(parentNode.children, { type = "group", op = "AND", children = {} })
			SaveFilterAST(filter)
			Refresh()
		end)
		addGroup:SetPoint("BOTTOMRIGHT", groupRow, "BOTTOMRIGHT", -10, 7)
		addCondition:SetPoint("RIGHT", addGroup, "LEFT", -8, 0)
		if canDeleteGroup then
			local deleteGroup = BFL_Settings_CreateSmallButton(groupRow, L.FILTER_BUILDER_DELETE or "Delete", 58, function()
				table.remove(ownerChildren, ownerIndex)
				SaveFilterAST(filter)
				Refresh()
			end)
			deleteGroup:SetPoint("RIGHT", addCondition, "LEFT", -8, 0)
		end
		table.insert(editorFrames, groupRow)

		for index, node in ipairs(parentNode.children) do
			if node.type == "group" then
				AddFilterNodeRows(filter, node, depth + 1, parentNode.children, index)
			else
				local row = BFL_Settings_CreateEditorRow(editorContent, 64, BFL_Settings_GetEditorIndent(depth) + 14, BFL_FILTER_SORT_EDITOR_SIDE_PADDING)
				local fieldDef = fieldById[node.field] or filterFields[1]
				node.field = fieldDef.id
				local fieldDropdown = BFL_Settings_CreateInlineDropdown(row, filterFieldOptions, node.field, function(value)
					local nextField = fieldById[value] or filterFields[1]
					node.field = nextField.id
					node.op = Registry:GetOperatorsForField(node.field)[1]
					node.value = BFL_Settings_GetDefaultValueForField(nextField)
					SaveFilterAST(filter)
					Refresh()
				end, 112)
				fieldDropdown:SetPoint("TOPLEFT", row, "TOPLEFT", 10, -10)
				local operatorOptions = BFL_Settings_BuildOperatorOptions(Registry:GetOperatorsForField(node.field))
				local opDropdown = BFL_Settings_CreateInlineDropdown(row, operatorOptions, node.op or "is", function(value)
					node.op = value
					SaveFilterAST(filter)
					Refresh()
				end, 88)
				opDropdown:SetPoint("LEFT", fieldDropdown, "RIGHT", 6, 0)

				local valueHolder = CreateFrame("Frame", nil, row)
				valueHolder:SetSize(106, 24)
				valueHolder:SetPoint("LEFT", opDropdown, "RIGHT", 6, 0)
				if node.op == "empty" or node.op == "notempty" then
					local emptyText = valueHolder:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontDisableSmall")
					emptyText:SetPoint("CENTER")
					emptyText:SetText("-")
				else
					local valueOptions = BFL_Settings_BuildValueOptions(fieldDef)
					if valueOptions then
						if fieldDef.type == "boolean" and type(node.value) ~= "boolean" then
							node.value = BFL_Settings_GetDefaultValueForField(fieldDef)
						end
						local valueDropdown = BFL_Settings_CreateInlineDropdown(valueHolder, valueOptions, node.value, function(value)
							node.value = value
							SaveFilterAST(filter)
							Refresh()
						end, 106)
						valueDropdown:SetPoint("LEFT", 0, 0)
					else
						local valueBox = CreateFrame("EditBox", nil, valueHolder, "InputBoxTemplate")
						valueBox:SetSize(106, 20)
						valueBox:SetAutoFocus(false)
						valueBox:SetText(BFL_Settings_ValueToText(node.value))
						valueBox:SetPoint("LEFT", 0, 0)
						valueBox:SetScript("OnEnterPressed", function(selfBox)
							node.value = BFL_Settings_ParseFilterValue(fieldDef, node.op, selfBox:GetText())
							selfBox:ClearFocus()
							SaveFilterAST(filter)
						end)
						valueBox:SetScript("OnEditFocusLost", function(selfBox)
							node.value = BFL_Settings_ParseFilterValue(fieldDef, node.op, selfBox:GetText())
							SaveFilterAST(filter)
						end)
					end
				end
				local notButton = BFL_Settings_CreateSmallButton(row, node.negate and "NOT" or "not", 46, function()
					node.negate = not node.negate
					SaveFilterAST(filter)
					Refresh()
				end)
				notButton:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 10, 8)
				local up = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_UP or "Up", 36, function()
					if index > 1 then
						parentNode.children[index], parentNode.children[index - 1] =
							parentNode.children[index - 1], parentNode.children[index]
						SaveFilterAST(filter)
						Refresh()
					end
				end)
				up:SetEnabled(index > 1)
				local down = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_DOWN or "Down", 48, function()
					if index < #parentNode.children then
						parentNode.children[index], parentNode.children[index + 1] =
							parentNode.children[index + 1], parentNode.children[index]
						SaveFilterAST(filter)
						Refresh()
					end
				end)
				down:SetEnabled(index < #parentNode.children)
				local delete = BFL_Settings_CreateSmallButton(row, "X", 28, function()
					table.remove(parentNode.children, index)
					SaveFilterAST(filter)
					Refresh()
				end)
				delete:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", -10, 8)
				down:SetPoint("RIGHT", delete, "LEFT", -4, 0)
				up:SetPoint("RIGHT", down, "LEFT", -4, 0)
				table.insert(editorFrames, row)
			end
		end
	end

	local function SaveSorter(sorter)
		Registry:UpdateCustomSorter(selectedId, { chain = sorter.chain })
		local FriendsList = BFL:GetModule("FriendsList")
		if FriendsList and FriendsList.sortMode == selectedId and FriendsList.ApplySort then
			FriendsList:ApplySort()
		end
		BFL:ForceRefreshFriendsList()
	end

	local function AddSorterRows(sorter)
		sorter.chain = sorter.chain or {}
		for index, step in ipairs(sorter.chain) do
			local row = BFL_Settings_CreateEditorRow(editorContent, 64)
			local fieldDef = sortFieldById[step.field] or sortFields[1]
			step.field = fieldDef.id
			local label = row:CreateFontString(nil, "OVERLAY", "BetterFriendlistFontHighlightSmall")
			label:SetPoint("TOPLEFT", row, "TOPLEFT", 10, -14)
			label:SetWidth(24)
			label:SetText(tostring(index) .. ".")
			local fieldDropdown = BFL_Settings_CreateInlineDropdown(row, sortFieldOptions, step.field, function(value)
				local nextField = sortFieldById[value] or sortFields[1]
				step.field = nextField.id
				step.direction = BFL_Settings_GetSorterFieldDefaultDirection(nextField)
				SaveSorter(sorter)
				Refresh()
			end, 126)
			fieldDropdown:SetPoint("LEFT", label, "RIGHT", 4, 0)
			local directionDropdown = BFL_Settings_CreateInlineDropdown(row, directionOptions, step.direction == "desc" and "desc" or "asc", function(value)
				step.direction = value == "desc" and "desc" or "asc"
				SaveSorter(sorter)
				Refresh()
			end, 64)
			directionDropdown:SetPoint("LEFT", fieldDropdown, "RIGHT", 6, 0)
			local emptyDropdown = BFL_Settings_CreateInlineDropdown(row, emptyOptions, step.empty == "first" and "first" or "last", function(value)
				step.empty = value == "first" and "first" or "last"
				SaveSorter(sorter)
				Refresh()
			end, 104)
			emptyDropdown:SetPoint("LEFT", directionDropdown, "RIGHT", 6, 0)
			local up = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_UP or "Up", 36, function()
				if index > 1 then
					sorter.chain[index], sorter.chain[index - 1] = sorter.chain[index - 1], sorter.chain[index]
					SaveSorter(sorter)
					Refresh()
				end
			end)
			up:SetEnabled(index > 1)
			local down = BFL_Settings_CreateSmallButton(row, L.FILTER_BUILDER_DOWN or "Down", 48, function()
				if index < #sorter.chain then
					sorter.chain[index], sorter.chain[index + 1] = sorter.chain[index + 1], sorter.chain[index]
					SaveSorter(sorter)
					Refresh()
				end
			end)
			down:SetEnabled(index < #sorter.chain)
			local delete = BFL_Settings_CreateSmallButton(row, "X", 28, function()
				table.remove(sorter.chain, index)
				SaveSorter(sorter)
				Refresh()
			end)
			delete:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", -10, 8)
			down:SetPoint("RIGHT", delete, "LEFT", -4, 0)
			up:SetPoint("RIGHT", down, "LEFT", -4, 0)
			table.insert(editorFrames, row)
		end

		local addStep = Components:CreateButton(editorContent, L.FILTER_BUILDER_ADD_SORT_STEP or "Add Sort Step", function()
			if #sorter.chain < Registry.MAX_SORT_STEPS then
				table.insert(sorter.chain, { field = "name", direction = "asc", empty = "last" })
				SaveSorter(sorter)
				Refresh()
			end
		end)
		table.insert(editorFrames, addStep)
	end

	if not isSettingsCenterInline then
		table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_TAB_FILTER_SORT or "QuickFilter & Sorter"))
		table.insert(
			allFrames,
			Components:CreateLabel(
				tab,
				L.FILTER_BUILDER_DESC
					or "Choose which entries appear in menus, change their order, and create custom filters or sorter chains.",
				true
			)
		)
	end
	if not isSettingsCenterEditor then
		AddModeRow()

		if not isSettingsCenterInline then
			table.insert(allFrames, Components:CreateHeader(tab, selectedKind == "filter" and (L.FILTER_BUILDER_FILTERS or "QuickFilters") or (L.FILTER_BUILDER_SORTERS or "Sorters")))
		end
		for i, entry in ipairs(allEntries) do
			AddEntryRow(entry, i)
		end
	else
		AddEditorNavigationRow()
	end

	local entry = GetEntry(selectedKind, selectedId)
	local createState = tab.bflFilterSortCreate
	if editorPanel and editorContent then
		if createState then
			editorPanel.title:SetText(
				createState.kind == "filter" and (L.FILTER_BUILDER_CREATE_FILTER_TITLE or "Create QuickFilter")
					or (L.FILTER_BUILDER_CREATE_SORTER_TITLE or "Create Sorter")
			)
			AddCreateEditor(createState.kind, createState.templateId)
		elseif entry then
			editorPanel.title:SetText((L.FILTER_BUILDER_EDITOR or "Editor") .. ": " .. entry.name)
			if selectedKind == "filter" then
				local matches, total = Registry:CountQuickFilterMatches(entry.id)
				AddPreviewRow(entry, matches, total)
			else
				AddPreviewRow(entry)
			end
			if isSettingsCenterEditor then
				AddEntryActionsRow(entry)
			end

			local customTable = AddNameAndIconEditor(entry)
			if selectedKind == "filter" then
				if customTable then
					AddFilterNodeRows(customTable, customTable.ast, 0)
				end
			elseif customTable then
				AddSorterRows(customTable)
			end
		else
			editorPanel:Hide()
		end

		if editorPanel:IsShown() then
			Components:AnchorChain(editorFrames, -5)
			editorPanel.components = editorFrames
			self:UpdateFilterSortEditorHeight(editorFrames)
			if self.filterSortCustomHost and self.filterSortCustomHost.inlineEditor then
				table.insert(allFrames, editorPanel)
			end
		end
	end

	Components:AnchorChain(allFrames, -5)
	if isSettingsCenterInline then
		self.filterSortInlinePageHeight = math.max(560, BFL_Settings_GetStackHeight(allFrames, -5) + 30)
		if tab.SetHeight then
			tab:SetHeight(self.filterSortInlinePageHeight)
		end
	end
	tab.components = allFrames
end

-- ===========================================
-- DATA BROKER TAB (Tab ID 5)
-- ===========================================
function Settings:RefreshBrokerTab()
	if not settingsFrame then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.BrokerTab then
		return
	end

	local tab = content.BrokerTab
	local DB = GetDB()
	if not DB then
		return
	end

	-- Clear existing content using GetChildren for robustness against reference leaks
	local children = { tab:GetChildren() }
	for _, child in ipairs(children) do
		if child.Hide then
			child:Hide()
		end
	end
	-- Legacy compatibility (keep table clear)
	tab.components = {}

	local allFrames = {}

	-- Shared Broker Font Library
	local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
	local fontList = LSM and LSM:List("font") or { "Friz Quadrata TT" }
	local fontPaths = {}
	for i, fontName in ipairs(fontList) do
		fontPaths[i] = BFL.FontManager:ResolveFontPath(fontName)
	end
	local fontOptions = { labels = fontList, values = fontList, fontPaths = fontPaths, useCheckboxes = true }

	local function RefreshBrokerTooltips()
		local Broker = BFL:GetModule("Broker")
		if Broker and Broker.RefreshTooltip then
			Broker:RefreshTooltip()
		end

		local GBroker = BFL:GetModule("GuildBroker")
		if GBroker and GBroker.RefreshTooltip then
			GBroker:RefreshTooltip()
		end
	end

	-- Header: Data Broker Integration
	local header = Components:CreateHeader(tab, L.BROKER_SETTINGS_HEADER_INTEGRATION)
	table.insert(allFrames, header)

	-- Info Text
	local infoText = Components:CreateLabel(tab, L.BROKER_SETTINGS_INFO, true)
	table.insert(allFrames, infoText)

	-- Enable Data Broker
	local enableBroker = Components:CreateCheckbox(
		tab,
		L.BROKER_SETTINGS_ENABLE or "Enable Data Broker",
		DB:Get("brokerEnabled", true),
		function(val)
			BetterFriendlistDB.brokerEnabled = val

			-- Refresh to update sub-options visibility
			self:RefreshBrokerTab()

			-- Show confirmation dialog for reload
			local statusText = val and "|cff00ff00" .. (L.STATUS_ENABLED or "ENABLED") .. "|r"
				or "|cffff0000" .. (L.STATUS_DISABLED or "DISABLED") .. "|r"
			StaticPopupDialogs["BFL_BROKER_RELOAD_CONFIRM"] = {
				text = string.format(
					L.BROKER_SETTINGS_RELOAD_TEXT
						or "Data Broker has been %s.\n\nA UI reload is required for this change to take effect.\n\nReload now?",
					statusText
				),
				button1 = L.BROKER_SETTINGS_RELOAD_BTN or "Reload Now",
				button2 = L.BROKER_SETTINGS_RELOAD_CANCEL or "Later",
				OnAccept = function()
					ReloadUI()
				end,
				timeout = 0,
				whileDead = true,
				hideOnEscape = true,
				preferredIndex = 3,
			}
			StaticPopup_Show("BFL_BROKER_RELOAD_CONFIRM")
		end
	)
	enableBroker:SetTooltip(
		L.BROKER_SETTINGS_ENABLE or "Enable Data Broker",
		L.BROKER_SETTINGS_ENABLE_TOOLTIP
			or "Show BetterFriendlist in Data Broker display addons. Requires UI reload to take effect."
	)
	table.insert(allFrames, enableBroker)

	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateHeader(tab, L.BROKER_SETTINGS_FONT_HEADER or "Broker Tooltip Font"))

	local currentBrokerFont = DB:Get("brokerFont", "Friz Quadrata TT")
	local brokerFontDropdown
	brokerFontDropdown = Components:CreateDropdown(
		tab,
		L.BROKER_SETTINGS_FONT or "Font:",
		fontOptions,
		function(val)
			return val == currentBrokerFont
		end,
		function(val)
			DB:Set("brokerFont", val)
			currentBrokerFont = val
			if brokerFontDropdown.DropDown then
				BFL.SetDropdownText(brokerFontDropdown.DropDown, val)
			end
			RefreshBrokerTooltips()
		end
	)
	if brokerFontDropdown.SetTooltip then
		brokerFontDropdown:SetTooltip(
			L.BROKER_SETTINGS_FONT or "Font:",
			L.BROKER_SETTINGS_FONT_TOOLTIP or "Choose the font used in broker tooltips"
		)
	end
	if brokerFontDropdown.DropDown then
		local point, relativeTo, relativePoint, xOfs, yOfs = brokerFontDropdown.DropDown:GetPoint(1)
		if point then
			brokerFontDropdown.DropDown:SetPoint(point, relativeTo, relativePoint, (xOfs or 0) + 10, yOfs or 0)
		end
	end
	table.insert(allFrames, brokerFontDropdown)

	local brokerFontSize = Components:CreateSlider(
		tab,
		L.BROKER_SETTINGS_FONT_SIZE or "Font Size:",
		8,
		24,
		DB:Get("brokerFontSize", 12),
		function(val)
			return tostring(val)
		end,
		function(val)
			DB:Set("brokerFontSize", val)
			RefreshBrokerTooltips()
		end
	)
	if brokerFontSize.SetStep then
		brokerFontSize:SetStep(1)
	end
	if brokerFontSize.SetTooltip then
		brokerFontSize:SetTooltip(
			L.BROKER_SETTINGS_FONT_SIZE or "Font Size:",
			L.BROKER_SETTINGS_FONT_SIZE_TOOLTIP or "Set the font size used in broker tooltips"
		)
	end
	table.insert(allFrames, brokerFontSize)

	local brokerFlagsDropdown = CreateFontFlagsDropdown(tab, DB, "brokerFontFlags", RefreshBrokerTooltips)
	table.insert(allFrames, brokerFlagsDropdown)

	local brokerNonLatinFont = Components:CreateCheckbox(
		tab,
		L.BROKER_SETTINGS_FONT_NON_LATIN or "Enable Font for non-latin Alphabets",
		DB:Get("brokerUseCustomFontForNonLatin", false),
		function(val)
			DB:Set("brokerUseCustomFontForNonLatin", val)
			RefreshBrokerTooltips()
		end
	)
	if brokerNonLatinFont.SetTooltip then
		brokerNonLatinFont:SetTooltip(
			L.BROKER_SETTINGS_FONT_NON_LATIN or "Enable Font for non-latin Alphabets",
			L.BROKER_SETTINGS_FONT_NON_LATIN_TOOLTIP
				or "Use the selected broker tooltip font for Korean, Chinese, and Russian text instead of WoW's default alphabet fallback."
		)
	end
	table.insert(allFrames, brokerNonLatinFont)

	local brokerNonLatinWarning = Components:CreateLabel(
		tab,
		L.BROKER_SETTINGS_FONT_NON_LATIN_WARNING
			or "Fonts do not always support every alphabet. If text appears as boxes, choose another font or disable this option.",
		true,
		{ r = 1, g = 0.82, b = 0, a = 1 }
	)
	table.insert(allFrames, brokerNonLatinWarning)

	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateHeader(tab, L.BROKER_SETTINGS_APPEARANCE_HEADER or "Broker Tooltip Appearance"))

	local BrokerUtils = BFL.BrokerUtils
	local separatorColor = BrokerUtils and BrokerUtils.GetBrokerSeparatorColor and BrokerUtils.GetBrokerSeparatorColor()
		or DB:Get("brokerSeparatorColor", { r = 0.3, g = 0.3, b = 0.3, a = 0.5 })
	separatorColor = {
		r = separatorColor.r or separatorColor[1] or 0.3,
		g = separatorColor.g or separatorColor[2] or 0.3,
		b = separatorColor.b or separatorColor[3] or 0.3,
		a = separatorColor.a or separatorColor[4] or 0.5,
	}
	local separatorPicker = Components:CreateColorPicker(
		tab,
		L.BROKER_SETTINGS_SEPARATOR_COLOR or "Separator Color",
		separatorColor,
		function(r, g, b, a)
			DB:Set("brokerSeparatorColor", { r = r, g = g, b = b, a = a })
			RefreshBrokerTooltips()
		end,
		{
			tooltipTitle = L.BROKER_SETTINGS_SEPARATOR_COLOR or "Separator Color",
			tooltipDesc = L.BROKER_SETTINGS_SEPARATOR_COLOR_TOOLTIP
				or "Sets the shared color for Friends and Guild Broker group separators and footer dividers.",
			resetCallback = function(holder)
				local color = BrokerUtils and BrokerUtils.GetDefaultBrokerSeparatorColor
					and BrokerUtils.GetDefaultBrokerSeparatorColor()
					or { r = 0.3, g = 0.3, b = 0.3, a = 0.5 }
				DB:Set("brokerSeparatorColor", color)
				if holder and holder.SetColor then
					holder:SetColor(color.r, color.g, color.b, color.a)
				end
				RefreshBrokerTooltips()
			end,
			resetTooltip = L.BROKER_SETTINGS_SEPARATOR_RESET_TOOLTIP or "Right-click to reset the separator color.",
		}
	)
	table.insert(allFrames, separatorPicker)

	if DB:Get("brokerEnabled", true) then
		-- Show Icon
		local showIcon = Components:CreateCheckbox(
			tab,
			L.BROKER_SETTINGS_SHOW_ICON or "Show Icon on Display Addon",
			DB:Get("brokerShowIcon", true),
			function(val)
				BetterFriendlistDB.brokerShowIcon = val
				local Broker = BFL:GetModule("Broker")
				if Broker and Broker.UpdateBrokerText then
					Broker:UpdateBrokerText()
				end
			end
		)
		showIcon:SetTooltip(
			L.BROKER_SETTINGS_SHOW_ICON_TITLE or "Show Icon",
			L.BROKER_SETTINGS_SHOW_ICON_TOOLTIP or "Display the BetterFriendlist icon on your display addon"
		)
		table.insert(allFrames, showIcon)

		-- Show Label
		local showLabel = Components:CreateCheckbox(
			tab,
			L.BROKER_SETTINGS_SHOW_LABEL or "Show Label",
			DB:Get("brokerShowLabel", true),
			function(val)
				BetterFriendlistDB.brokerShowLabel = val
				local Broker = BFL:GetModule("Broker")
				if Broker and Broker.UpdateBrokerText then
					Broker:UpdateBrokerText()
				end
			end
		)
		showLabel:SetTooltip(
			L.BROKER_SETTINGS_SHOW_LABEL_TITLE or "Show Label",
			L.BROKER_SETTINGS_SHOW_LABEL_TOOLTIP or "Display 'Friends:' text before the count"
		)
		table.insert(allFrames, showLabel)

		-- Show Total Count
		local showTotal = Components:CreateCheckbox(
			tab,
			L.BROKER_SETTINGS_SHOW_TOTAL or "Show Total Count",
			DB:Get("brokerShowTotal", true),
			function(val)
				BetterFriendlistDB.brokerShowTotal = val
				local Broker = BFL:GetModule("Broker")
				if Broker and Broker.UpdateBrokerText then
					Broker:UpdateBrokerText()
				end
			end
		)
		showTotal:SetTooltip(
			L.BROKER_SETTINGS_SHOW_TOTAL_TITLE or "Show Total Count",
			L.BROKER_SETTINGS_SHOW_TOTAL_TOOLTIP
				or "Display total friends count (e.g. '5/10') instead of just online count"
		)
		table.insert(allFrames, showTotal)

		-- Split WoW/BNet Counts
		local showGroups = Components:CreateCheckbox(
			tab,
			L.BROKER_SETTINGS_SHOW_GROUPS,
			DB:Get("brokerShowGroups", false),
			function(val)
				BetterFriendlistDB.brokerShowGroups = val
				-- Refresh tab to show/hide sub-options
				self:RefreshBrokerTab()
				-- Update broker text immediately
				local Broker = BFL:GetModule("Broker")
				if Broker and Broker.UpdateBrokerText then
					Broker:UpdateBrokerText()
				end
			end
		)
		showGroups:SetTooltip(
			L.BROKER_SETTINGS_SHOW_GROUPS_TITLE or "Split Counts",
			L.BROKER_SETTINGS_SHOW_GROUPS_TOOLTIP or "Show separate counts for WoW and Battle.net friends"
		)
		table.insert(allFrames, showGroups)

		-- Sub-options for Split Counts (only visible if enabled)
		if DB:Get("brokerShowGroups", false) then
			-- Show WoW Icon
			local showWoWIcon = Components:CreateCheckbox(
				tab,
				L.BROKER_SETTINGS_SHOW_WOW_ICON or "Show WoW Icon",
				DB:Get("brokerShowWoWIcon", true),
				function(val)
					BetterFriendlistDB.brokerShowWoWIcon = val
					local Broker = BFL:GetModule("Broker")
					if Broker and Broker.UpdateBrokerText then
						Broker:UpdateBrokerText()
					end
				end
			)
			showWoWIcon:SetTooltip(
				L.BROKER_SETTINGS_SHOW_WOW_ICON_TITLE or "Show WoW Icon",
				L.BROKER_SETTINGS_SHOW_WOW_ICON_TOOLTIP
					or "Display the World of Warcraft icon next to the WoW friend count"
			)

			table.insert(allFrames, showWoWIcon)

			-- Show Battle.net Icon
			local showBNetIcon = Components:CreateCheckbox(
				tab,
				L.BROKER_SETTINGS_SHOW_BNET_ICON or "Show Battle.net Icon",
				DB:Get("brokerShowBNetIcon", true),
				function(val)
					BetterFriendlistDB.brokerShowBNetIcon = val
					local Broker = BFL:GetModule("Broker")
					if Broker and Broker.UpdateBrokerText then
						Broker:UpdateBrokerText()
					end
				end
			)
			showBNetIcon:SetTooltip(
				L.BROKER_SETTINGS_SHOW_BNET_ICON_TITLE or "Show Battle.net Icon",
				L.BROKER_SETTINGS_SHOW_BNET_ICON_TOOLTIP
					or "Display the Battle.net icon next to the Battle.net friend count"
			)

			table.insert(allFrames, showBNetIcon)
		end

		-- Spacer
		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Show Tooltip Hints
		local showHints = Components:CreateCheckbox(
			tab,
			L.BROKER_SETTINGS_SHOW_HINTS or "Show Tooltip Hints",
			DB:Get("brokerShowHints", true),
			function(val)
				BetterFriendlistDB.brokerShowHints = val
			end
		)
		showHints:SetTooltip(
			L.BROKER_SETTINGS_SHOW_HINTS_TITLE or "Show Tooltip Hints",
			L.BROKER_SETTINGS_SHOW_HINTS_TOOLTIP
				or "Display clickable action hints at the bottom of the broker tooltip"
		)
		table.insert(allFrames, showHints)

		-- Spacer
		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Show Class Icons
		local brokerShowClassIcons = Components:CreateCheckbox(
			tab,
			L.BROKER_SETTINGS_SHOW_CLASS_ICONS or "Show Class Icons",
			DB:Get("brokerShowClassIcons", false),
			function(val)
				BetterFriendlistDB.brokerShowClassIcons = val
			end
		)
		if brokerShowClassIcons.SetTooltip then
			brokerShowClassIcons:SetTooltip(
				L.BROKER_SETTINGS_SHOW_CLASS_ICONS or "Show Class Icons",
				L.BROKER_SETTINGS_SHOW_CLASS_ICONS_TOOLTIP or "Display class icons next to friend names in the broker tooltip"
			)
		end
		table.insert(allFrames, brokerShowClassIcons)

		local brokerGroupHeaderAlignOptions = {
			labels = {
				L.SETTINGS_ALIGN_LEFT or "Left",
				L.SETTINGS_ALIGN_CENTER or "Center",
				L.SETTINGS_ALIGN_RIGHT or "Right",
			},
			values = { "LEFT", "CENTER", "RIGHT" },
		}
		local currentGroupHeaderAlign = DB:Get("brokerGroupHeaderAlign", "LEFT")
		local brokerGroupHeaderAlignDropdown = Components:CreateDropdown(
			tab,
			L.BROKER_SETTINGS_GROUP_HEADER_ALIGN or "Group Name Alignment",
			brokerGroupHeaderAlignOptions,
			function(val)
				return val == currentGroupHeaderAlign
			end,
			function(val)
				DB:Set("brokerGroupHeaderAlign", val)
				currentGroupHeaderAlign = val
				local Broker = BFL:GetModule("Broker")
				if Broker and Broker.RefreshTooltip then
					Broker:RefreshTooltip()
				end
			end
		)
		if brokerGroupHeaderAlignDropdown.SetTooltip then
			brokerGroupHeaderAlignDropdown:SetTooltip(
				L.BROKER_SETTINGS_GROUP_HEADER_ALIGN or "Group Name Alignment",
				L.BROKER_SETTINGS_GROUP_HEADER_ALIGN_TOOLTIP
					or "Set the alignment of group names in the friends broker tooltip"
			)
		end
		table.insert(allFrames, brokerGroupHeaderAlignDropdown)

		-- Spacer
		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Header: Column Colors
		local brokerColumnColorsHeader = Components:CreateHeader(
			tab,
			L.BROKER_SETTINGS_COLUMN_COLORS or "Column Colors"
		)
		table.insert(allFrames, brokerColumnColorsHeader)

		local function CreateBrokerColumnColor(label, settingKey)
			local currentColor = DB:Get(settingKey)
			local picker = Components:CreateColorPicker(
				tab,
				label,
				currentColor and { r = currentColor[1] or currentColor.r, g = currentColor[2] or currentColor.g, b = currentColor[3] or currentColor.b } or
				{ r = 1, g = 1, b = 1 },
				function(r, g, b)
					if math.abs(r - 1) < 0.01 and math.abs(g - 1) < 0.01 and math.abs(b - 1) < 0.01 then
						BetterFriendlistDB[settingKey] = nil
					else
						BetterFriendlistDB[settingKey] = { r, g, b }
					end
				end
			)
			table.insert(allFrames, picker)
		end

		CreateBrokerColumnColor(L.BROKER_SETTINGS_NICKNAME_COLOR or "Nickname Color", "brokerNicknameColor")
		CreateBrokerColumnColor(L.BROKER_SETTINGS_CHARACTER_COLOR or "Character Color", "brokerCharacterColor")
		CreateBrokerColumnColor(L.BROKER_SETTINGS_ZONE_COLOR or "Zone Color", "brokerZoneColor")
		CreateBrokerColumnColor(L.BROKER_SETTINGS_REALM_COLOR or "Realm Color", "brokerRealmColor")
		CreateBrokerColumnColor(L.BROKER_SETTINGS_NOTES_COLOR or "Notes Color", "brokerNotesColor")

		-- Spacer
		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Header: Tooltip Columns
		local columnsHeader = Components:CreateHeader(tab, L.BROKER_SETTINGS_COLUMNS_HEADER)
		table.insert(allFrames, columnsHeader)

		-- Column Reordering Logic
		local availableColumns = {
			{ key = "Nickname", label = L.BROKER_COLUMN_NICKNAME or "Nickname", default = false },
			{ key = "Name", label = L.BROKER_COLUMN_NAME, default = true },
			{ key = "Status", label = L.BROKER_COLUMN_STATUS or "Status", default = false },
			{ key = "Level", label = L.BROKER_COLUMN_LEVEL, default = true },
			{ key = "Character", label = L.BROKER_COLUMN_CHARACTER, default = true },
			{ key = "Game", label = L.BROKER_COLUMN_GAME, default = true },
			{ key = "Zone", label = L.BROKER_COLUMN_ZONE, default = true },
			{ key = "Realm", label = L.BROKER_COLUMN_REALM, default = true },
			{ key = "Notes", label = L.BROKER_COLUMN_NOTES, default = true },
		}

		local currentOrder = DB:Get("brokerColumnOrder")
		local orderedColumns = {}

		if currentOrder and #currentOrder > 0 then
			for _, key in ipairs(currentOrder) do
				for _, col in ipairs(availableColumns) do
					if col.key == key then
						table.insert(orderedColumns, col)
						break
					end
				end
			end
			-- Add missing columns
			for _, col in ipairs(availableColumns) do
				local found = false
				for _, ordered in ipairs(orderedColumns) do
					if ordered.key == col.key then
						found = true
						break
					end
				end
				if not found then
					table.insert(orderedColumns, col)
				end
			end
		else
			-- Default order
			orderedColumns = availableColumns
		end

		-- Create reorderable list items
		local listItems = {}

		for i, col in ipairs(orderedColumns) do
			local onDragStart = function(btn)
				btn:SetScript("OnUpdate", function(self)
					for _, otherItem in ipairs(listItems) do
						if otherItem ~= self and otherItem:IsVisible() then
							if MouseIsOver(otherItem) then
								otherItem.bg:SetColorTexture(0.3, 0.3, 0.3, 0.7)
							else
								otherItem.bg:SetColorTexture(0.1, 0.1, 0.1, 0.5)
							end
						end
					end
				end)
			end

			local onDragStop = function(btn)
				btn:SetScript("OnUpdate", nil)

				local targetIndex = nil
				-- Reset all backgrounds
				for _, otherItem in ipairs(listItems) do
					otherItem.bg:SetColorTexture(0.1, 0.1, 0.1, 0.5)
					if otherItem ~= btn and otherItem:IsVisible() then
						if MouseIsOver(otherItem) then
							targetIndex = otherItem.orderIndex
						end
					end
				end

				if targetIndex then
					if targetIndex ~= btn.orderIndex then
						-- Move
						local itemToMove = table.remove(orderedColumns, btn.orderIndex)
						table.insert(orderedColumns, targetIndex, itemToMove)

						-- Save
						local newOrder = {}
						for _, c in ipairs(orderedColumns) do
							table.insert(newOrder, c.key)
						end
						DB:Set("brokerColumnOrder", newOrder)
						self:RefreshBrokerTab()
					end
				end
			end

			-- Create a custom list item that includes a checkbox for visibility
			local listItem = Components:CreateListItem(
				tab,
				col.label,
				i,
				onDragStart,
				onDragStop,
				nil, -- No rename
				nil, -- No color
				nil -- No delete
			)

			-- Hide unused buttons
			if listItem.colorButton then
				listItem.colorButton:Hide()
			end
			if listItem.orderText then
				listItem.orderText:Hide()
			end

			-- Add Checkbox to the list item (Classic-compatible template selection)
			local isChecked = DB:Get("brokerShowCol" .. col.key, col.default ~= false)
			local checkboxTemplate = BFL.IsClassic and "InterfaceOptionsCheckButtonTemplate"
				or "SettingsCheckboxTemplate"
			local checkbox = CreateFrame("CheckButton", nil, listItem, checkboxTemplate)
			checkbox:SetPoint("LEFT", listItem, "LEFT", 4, 0)
			checkbox:SetSize(20, 20)
			checkbox:SetChecked(isChecked)
			checkbox:SetScript("OnClick", function(self)
				local checked = self:GetChecked()
				DB:Set("brokerShowCol" .. col.key, checked)
			end)

			-- Fix for ugly hover effect
			checkbox:SetScript("OnEnter", function() end)
			checkbox:SetScript("OnLeave", function() end)

			-- Adjust label position to make room for checkbox
			if listItem.nameText then
				listItem.nameText:ClearAllPoints()
				listItem.nameText:SetPoint("LEFT", checkbox, "RIGHT", 10, 0)
			end

			table.insert(listItems, listItem)
			table.insert(allFrames, listItem)
		end
	end

	-- ===========================================
	-- GUILD PLUGIN SUB-SECTION
	-- ===========================================
	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateSpacer(tab))

	local guildHeader = Components:CreateHeader(tab, L.GUILD_BROKER_SETTINGS_HEADER or "Guild Plugin")
	table.insert(allFrames, guildHeader)

	-- Enable Guild Broker
	local enableGuildBroker = Components:CreateCheckbox(
		tab,
		L.GUILD_BROKER_SETTINGS_ENABLE or "Enable Guild Broker",
		DB:Get("guildBrokerEnabled", false),
		function(val)
			BetterFriendlistDB.guildBrokerEnabled = val
			self:RefreshBrokerTab()

			local statusText = val and "|cff00ff00" .. (L.STATUS_ENABLED or "ENABLED") .. "|r"
				or "|cffff0000" .. (L.STATUS_DISABLED or "DISABLED") .. "|r"
			StaticPopupDialogs["BFL_GUILD_BROKER_RELOAD_CONFIRM"] = {
				text = string.format(
					L.BROKER_SETTINGS_RELOAD_TEXT
						or "Data Broker has been %s.\n\nA UI reload is required for this change to take effect.\n\nReload now?",
					statusText
				),
				button1 = L.BROKER_SETTINGS_RELOAD_BTN or "Reload Now",
				button2 = L.BROKER_SETTINGS_RELOAD_CANCEL or "Later",
				OnAccept = function()
					ReloadUI()
				end,
				timeout = 0,
				whileDead = true,
				hideOnEscape = true,
				preferredIndex = 3,
			}
			StaticPopup_Show("BFL_GUILD_BROKER_RELOAD_CONFIRM")
		end
	)
	enableGuildBroker:SetTooltip(
		L.GUILD_BROKER_SETTINGS_ENABLE or "Enable Guild Broker",
		L.GUILD_BROKER_SETTINGS_ENABLE_DESC or "Show guild member data in your Data Broker display addon"
	)
	table.insert(allFrames, enableGuildBroker)

	if DB:Get("guildBrokerEnabled", false) then
		-- Show Icon
		local guildShowIcon = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_SETTINGS_SHOW_ICON or "Show Icon",
			DB:Get("guildBrokerShowIcon", true),
			function(val)
				BetterFriendlistDB.guildBrokerShowIcon = val
				local GBroker = BFL:GetModule("GuildBroker")
				if GBroker and GBroker.UpdateBrokerText then
					GBroker:UpdateBrokerText()
				end
			end
		)
		table.insert(allFrames, guildShowIcon)

		-- Show Label
		local guildShowLabel = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_SETTINGS_SHOW_LABEL or "Show Label",
			DB:Get("guildBrokerShowLabel", true),
			function(val)
				BetterFriendlistDB.guildBrokerShowLabel = val
				local GBroker = BFL:GetModule("GuildBroker")
				if GBroker and GBroker.UpdateBrokerText then
					GBroker:UpdateBrokerText()
				end
			end
		)
		table.insert(allFrames, guildShowLabel)

		-- Show Total Count
		local guildShowTotal = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_SETTINGS_SHOW_TOTAL or "Show Total Count",
			DB:Get("guildBrokerShowTotal", true),
			function(val)
				BetterFriendlistDB.guildBrokerShowTotal = val
				local GBroker = BFL:GetModule("GuildBroker")
				if GBroker and GBroker.UpdateBrokerText then
					GBroker:UpdateBrokerText()
				end
			end
		)
		table.insert(allFrames, guildShowTotal)

		-- Show Applicant Count
		local guildShowApplicants = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_SETTINGS_SHOW_APPLICANTS or "Show Applicant Count",
			DB:Get("guildBrokerShowApplicants", true),
			function(val)
				DB:Set("guildBrokerShowApplicants", val)
				local GBroker = BFL:GetModule("GuildBroker")
				if GBroker and GBroker.RefreshApplicantCount then
					GBroker:RefreshApplicantCount(false)
				end
				if GBroker and GBroker.UpdateBrokerText then
					GBroker:UpdateBrokerText(true)
				end
			end
		)
		if guildShowApplicants.SetTooltip then
			guildShowApplicants:SetTooltip(
				L.GUILD_BROKER_SETTINGS_SHOW_APPLICANTS or "Show Applicant Count",
				L.GUILD_BROKER_SETTINGS_SHOW_APPLICANTS_DESC
					or "Show pending guild applicant count in the broker label when available"
			)
		end
		table.insert(allFrames, guildShowApplicants)

		-- Show Hints
		local guildShowHints = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_SETTINGS_SHOW_HINTS or "Show Hints",
			DB:Get("guildBrokerShowHints", true),
			function(val)
				BetterFriendlistDB.guildBrokerShowHints = val
			end
		)
		table.insert(allFrames, guildShowHints)

		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Left Click Action
		local clickActionOptions = {
			labels = {
				L.GUILD_BROKER_ACTION_GUILD_FRAME or "Open Guild Tab",
				L.GUILD_BROKER_ACTION_SETTINGS or "Open Settings",
			},
			values = { "guild_tab", "settings" },
		}
		local guildClickAction = Components:CreateDropdown(
			tab,
			L.GUILD_BROKER_SETTINGS_CLICK_ACTION or "Left Click Action",
			clickActionOptions,
			function(val)
				local current = DB:Get("guildBrokerClickAction", "guild_tab")
				if current == "guild_frame" then
					current = "guild_tab"
				end
				return val == current
			end,
			function(val)
				BetterFriendlistDB.guildBrokerClickAction = val
			end
		)
		table.insert(allFrames, guildClickAction)

		-- Group Mode
		local groupModeOptions = {
			labels = {
				L.GUILD_BROKER_GROUP_NONE or "No Grouping",
				L.GUILD_BROKER_GROUP_BY_RANK or "Group by Rank",
				L.GUILD_BROKER_GROUP_BY_CLASS or "Group by Class",
			},
			values = { "none", "by_rank", "by_class" },
		}
		local guildGroupMode = Components:CreateDropdown(
			tab,
			L.GUILD_BROKER_SETTINGS_GROUP_MODE or "Group Mode",
			groupModeOptions,
			function(val)
				return val == DB:Get("guildBrokerGroupMode", "none")
			end,
			function(val)
				BetterFriendlistDB.guildBrokerGroupMode = val
			end
		)
		table.insert(allFrames, guildGroupMode)

		-- Default Filter
		local filterOptions = {
			labels = {
				L.GUILD_BROKER_FILTER_ONLINE or "Online Only",
				L.GUILD_BROKER_FILTER_ALL or "All Members",
			},
			values = { "online", "all" },
		}
		local guildFilter = Components:CreateDropdown(
			tab,
			L.GUILD_BROKER_SETTINGS_FILTER or "Default Filter",
			filterOptions,
			function(val)
				return val == DB:Get("guildBrokerFilter", "online")
			end,
			function(val)
				BetterFriendlistDB.guildBrokerFilter = val
			end
		)
		table.insert(allFrames, guildFilter)

		-- Max Rows Slider
		local guildMaxRows = Components:CreateSlider(
			tab,
			L.GUILD_BROKER_SETTINGS_MAX_ROWS or "Maximum Rows",
			25, 200,
			DB:Get("guildBrokerMaxRows", 100),
			function(val)
				return tostring(val)
			end,
			function(val)
				BetterFriendlistDB.guildBrokerMaxRows = val
			end
		)
		if guildMaxRows.SetTooltip then
			guildMaxRows:SetTooltip(
				L.GUILD_BROKER_SETTINGS_MAX_ROWS or "Maximum Rows",
				L.GUILD_BROKER_SETTINGS_MAX_ROWS_DESC or "Limit tooltip rows for large guilds (25-200)"
			)
		end
		table.insert(allFrames, guildMaxRows)

		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Hide Level at Max
		local guildHideLevelAtMax = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_SETTINGS_HIDE_LEVEL_AT_MAX or "Hide Level at Max",
			DB:Get("guildBrokerHideLevelAtMax", false),
			function(val)
				BetterFriendlistDB.guildBrokerHideLevelAtMax = val
			end
		)
		if guildHideLevelAtMax.SetTooltip then
			guildHideLevelAtMax:SetTooltip(
				L.GUILD_BROKER_SETTINGS_HIDE_LEVEL_AT_MAX or "Hide Level at Max",
				L.GUILD_BROKER_SETTINGS_HIDE_LEVEL_AT_MAX_DESC or "Hide the level display for characters at maximum level"
			)
		end
		table.insert(allFrames, guildHideLevelAtMax)

		-- Show Class Icons
		local guildShowClassIcons = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_SETTINGS_SHOW_CLASS_ICONS or "Show Class Icons",
			DB:Get("guildBrokerShowClassIcons", false),
			function(val)
				BetterFriendlistDB.guildBrokerShowClassIcons = val
			end
		)
		if guildShowClassIcons.SetTooltip then
			guildShowClassIcons:SetTooltip(
				L.GUILD_BROKER_SETTINGS_SHOW_CLASS_ICONS or "Show Class Icons",
				L.GUILD_BROKER_SETTINGS_SHOW_CLASS_ICONS_DESC or "Display class icons next to character names in the tooltip"
			)
		end
		table.insert(allFrames, guildShowClassIcons)

		-- Exclude Self
		local guildExcludeSelf = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_SETTINGS_EXCLUDE_SELF or "Exclude Yourself",
			DB:Get("guildBrokerExcludeSelf", false),
			function(val)
				BetterFriendlistDB.guildBrokerExcludeSelf = val
				local GBroker = BFL:GetModule("GuildBroker")
				if GBroker and GBroker.UpdateBrokerText then
					GBroker:UpdateBrokerText()
				end
			end
		)
		if guildExcludeSelf.SetTooltip then
			guildExcludeSelf:SetTooltip(
				L.GUILD_BROKER_SETTINGS_EXCLUDE_SELF or "Exclude Yourself",
				L.GUILD_BROKER_SETTINGS_EXCLUDE_SELF_DESC or "Hide your own character from the guild list and subtract from online count"
			)
		end
		table.insert(allFrames, guildExcludeSelf)

		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Sort Mode
		local sortModeOptions = {
			labels = {
				L.GUILD_BROKER_SORT_NAME or "Name",
				L.GUILD_BROKER_SORT_RANK or "Rank",
				L.GUILD_BROKER_SORT_LEVEL or "Level",
				L.GUILD_BROKER_SORT_CLASS or "Class",
				L.GUILD_BROKER_SORT_ZONE or "Zone",
				L.GUILD_BROKER_SORT_NICKNAME or "Nickname",
			},
			values = { "name", "rank", "level", "class", "zone", "nickname" },
		}
		local guildSortMode = Components:CreateDropdown(
			tab,
			L.GUILD_BROKER_SETTINGS_SORT_MODE or "Sort Mode",
			sortModeOptions,
			function(val)
				return val == DB:Get("guildBrokerSortMode", "name")
			end,
			function(val)
				BetterFriendlistDB.guildBrokerSortMode = val
			end
		)
		table.insert(allFrames, guildSortMode)

		-- Column Colors Header
		local colColorsHeader = Components:CreateHeader(tab, L.GUILD_BROKER_SETTINGS_COLUMN_COLORS or "Column Colors")
		table.insert(allFrames, colColorsHeader)

		-- Nickname Color: Checkbox "Use Class Color" + conditional color picker
		local nickUseClassColor = BetterFriendlistDB.guildBrokerNicknameColor == nil
		local nickClassColorCheckbox = Components:CreateCheckbox(
			tab,
			L.GUILD_BROKER_NICKNAME_USE_CLASS_COLOR or "Nickname: Use Class Color",
			nickUseClassColor,
			function(checked)
				if checked then
					BetterFriendlistDB.guildBrokerNicknameColor = nil
				else
					-- Set a default custom color (white) when unchecking
					local currentColor = { 1, 1, 1 }
					BetterFriendlistDB.guildBrokerNicknameColor = currentColor
				end
				-- Toggle color picker visibility
				if nickColorPicker then
					if checked then
						nickColorPicker:Hide()
						nickColorPicker.isHidden = true
					else
						nickColorPicker:Show()
						nickColorPicker.isHidden = false
					end
				end
			end
		)
		table.insert(allFrames, nickClassColorCheckbox)

		local currentNickColor = DB:Get("guildBrokerNicknameColor")
		local nickColorPicker = Components:CreateColorPicker(
			tab,
			L.GUILD_BROKER_SETTINGS_NICKNAME_COLOR or "Nickname Color",
			currentNickColor and { r = currentNickColor[1], g = currentNickColor[2], b = currentNickColor[3] } or
			{ r = 1, g = 1, b = 1 },
			function(r, g, b)
				BetterFriendlistDB.guildBrokerNicknameColor = { r, g, b }
			end
		)
		if nickUseClassColor then
			nickColorPicker:Hide()
			nickColorPicker.isHidden = true
		end
		table.insert(allFrames, nickColorPicker)

		-- Rank Color
		local currentRankColor = DB:Get("guildBrokerRankColor")
		local rankColorPicker = Components:CreateColorPicker(
			tab,
			L.GUILD_BROKER_SETTINGS_RANK_COLOR or "Rank Color",
			currentRankColor and { r = currentRankColor[1], g = currentRankColor[2], b = currentRankColor[3] } or
			{ r = 1, g = 1, b = 1 },
			function(r, g, b)
				if math.abs(r - 1) < 0.01 and math.abs(g - 1) < 0.01 and math.abs(b - 1) < 0.01 then
					BetterFriendlistDB.guildBrokerRankColor = nil
				else
					BetterFriendlistDB.guildBrokerRankColor = { r, g, b }
				end
			end
		)
		table.insert(allFrames, rankColorPicker)

		-- Zone Color
		local currentZoneColor = DB:Get("guildBrokerZoneColor")
		local zoneColorPicker = Components:CreateColorPicker(
			tab,
			L.GUILD_BROKER_SETTINGS_ZONE_COLOR or "Zone Color",
			currentZoneColor and { r = currentZoneColor[1], g = currentZoneColor[2], b = currentZoneColor[3] } or
			{ r = 1, g = 1, b = 1 },
			function(r, g, b)
				if math.abs(r - 1) < 0.01 and math.abs(g - 1) < 0.01 and math.abs(b - 1) < 0.01 then
					BetterFriendlistDB.guildBrokerZoneColor = nil
				else
					BetterFriendlistDB.guildBrokerZoneColor = { r, g, b }
				end
			end
		)
		table.insert(allFrames, zoneColorPicker)

		-- Note Color
		local currentNoteColor = DB:Get("guildBrokerNoteColor")
		local noteColorPicker = Components:CreateColorPicker(
			tab,
			L.GUILD_BROKER_SETTINGS_NOTE_COLOR or "Note Color",
			currentNoteColor and { r = currentNoteColor[1], g = currentNoteColor[2], b = currentNoteColor[3] } or
			{ r = 1, g = 1, b = 1 },
			function(r, g, b)
				if math.abs(r - 1) < 0.01 and math.abs(g - 1) < 0.01 and math.abs(b - 1) < 0.01 then
					BetterFriendlistDB.guildBrokerNoteColor = nil
				else
					BetterFriendlistDB.guildBrokerNoteColor = { r, g, b }
				end
			end
		)
		table.insert(allFrames, noteColorPicker)

		-- Officer Note Color
		local currentOfficerNoteColor = DB:Get("guildBrokerOfficerNoteColor")
		local officerNoteColorPicker = Components:CreateColorPicker(
			tab,
			L.GUILD_BROKER_SETTINGS_OFFICER_NOTE_COLOR or "Officer Note Color",
			currentOfficerNoteColor and
			{ r = currentOfficerNoteColor[1], g = currentOfficerNoteColor[2], b = currentOfficerNoteColor[3] } or
			{ r = 1, g = 1, b = 1 },
			function(r, g, b)
				if math.abs(r - 1) < 0.01 and math.abs(g - 1) < 0.01 and math.abs(b - 1) < 0.01 then
					BetterFriendlistDB.guildBrokerOfficerNoteColor = nil
				else
					BetterFriendlistDB.guildBrokerOfficerNoteColor = { r, g, b }
				end
			end
		)
		table.insert(allFrames, officerNoteColorPicker)

		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Guild Tooltip Columns Header
		local guildColumnsHeader = Components:CreateHeader(tab, L.GUILD_BROKER_SETTINGS_COLUMNS or "Tooltip Columns")
		table.insert(allFrames, guildColumnsHeader)

		-- Guild Column Reordering (same pattern as Friends Broker)
		local guildAvailableColumns = {
			{ key = "Nickname",    label = L.GUILD_BROKER_COL_NICKNAME or "Nickname",         default = false },
			{ key = "Name",        label = L.GUILD_BROKER_COL_NAME or "Name",                 default = true },
			{ key = "Level",       label = L.GUILD_BROKER_COL_LEVEL or "Level",               default = true },
			{ key = "Class",       label = L.GUILD_BROKER_COL_CLASS or "Class",               default = true },
			{ key = "Professions", label = L.GUILD_BROKER_COL_PROFESSIONS or "Professions",   default = false },
			{ key = "Rank",        label = L.GUILD_BROKER_COL_RANK or "Rank",                 default = true },
			{ key = "Zone",        label = L.GUILD_BROKER_COL_ZONE or "Zone",                 default = true },
			{ key = "Note",        label = L.GUILD_BROKER_COL_NOTE or "Note",                 default = false },
			{ key = "OfficerNote", label = L.GUILD_BROKER_COL_OFFICER_NOTE or "Officer Note", default = false },
			{ key = "LastOnline",  label = L.GUILD_BROKER_COL_LAST_ONLINE or "Last Online",   default = true },
		}

		local guildCurrentOrder = DB:Get("guildBrokerColumnOrder")
		local guildOrderedColumns = {}

		if guildCurrentOrder and #guildCurrentOrder > 0 then
			for _, key in ipairs(guildCurrentOrder) do
				for _, col in ipairs(guildAvailableColumns) do
					if col.key == key then
						table.insert(guildOrderedColumns, col)
						break
					end
				end
			end
			-- Add missing columns (new columns added in updates)
			for _, col in ipairs(guildAvailableColumns) do
				local found = false
				for _, ordered in ipairs(guildOrderedColumns) do
					if ordered.key == col.key then
						found = true
						break
					end
				end
				if not found then
					table.insert(guildOrderedColumns, col)
				end
			end
		else
			guildOrderedColumns = guildAvailableColumns
		end

		-- Create reorderable list items
		local guildListItems = {}

		for i, col in ipairs(guildOrderedColumns) do
			local onDragStart = function(btn)
				btn:SetScript("OnUpdate", function(self)
					for _, otherItem in ipairs(guildListItems) do
						if otherItem ~= self and otherItem:IsVisible() then
							if MouseIsOver(otherItem) then
								otherItem.bg:SetColorTexture(0.3, 0.3, 0.3, 0.7)
							else
								otherItem.bg:SetColorTexture(0.1, 0.1, 0.1, 0.5)
							end
						end
					end
				end)
			end

			local onDragStop = function(btn)
				btn:SetScript("OnUpdate", nil)

				local targetIndex = nil
				for _, otherItem in ipairs(guildListItems) do
					otherItem.bg:SetColorTexture(0.1, 0.1, 0.1, 0.5)
					if otherItem ~= btn and otherItem:IsVisible() then
						if MouseIsOver(otherItem) then
							targetIndex = otherItem.orderIndex
						end
					end
				end

				if targetIndex then
					if targetIndex ~= btn.orderIndex then
						local itemToMove = table.remove(guildOrderedColumns, btn.orderIndex)
						table.insert(guildOrderedColumns, targetIndex, itemToMove)

						local newOrder = {}
						for _, c in ipairs(guildOrderedColumns) do
							table.insert(newOrder, c.key)
						end
						DB:Set("guildBrokerColumnOrder", newOrder)
						self:RefreshBrokerTab()
					end
				end
			end

			local listItem = Components:CreateListItem(
				tab,
				col.label,
				i,
				onDragStart,
				onDragStop,
				nil, nil, nil
			)

			if listItem.colorButton then
				listItem.colorButton:Hide()
			end
			if listItem.orderText then
				listItem.orderText:Hide()
			end

			-- Add visibility checkbox
			local guildColSettingKey = "guildBrokerShowCol" .. col.key
			local isChecked = DB:Get(guildColSettingKey, col.default ~= false)
			local checkboxTemplate = BFL.IsClassic and "InterfaceOptionsCheckButtonTemplate"
				or "SettingsCheckboxTemplate"
			local checkbox = CreateFrame("CheckButton", nil, listItem, checkboxTemplate)
			checkbox:SetPoint("LEFT", listItem, "LEFT", 4, 0)
			checkbox:SetSize(20, 20)
			checkbox:SetChecked(isChecked)
			checkbox:SetScript("OnClick", function(self)
				local checked = self:GetChecked()
				DB:Set(guildColSettingKey, checked)
			end)

			checkbox:SetScript("OnEnter", function() end)
			checkbox:SetScript("OnLeave", function() end)

			if listItem.nameText then
				listItem.nameText:ClearAllPoints()
				listItem.nameText:SetPoint("LEFT", checkbox, "RIGHT", 10, 0)
			end

			table.insert(guildListItems, listItem)
			table.insert(allFrames, listItem)
		end
	end

	-- Anchor all frames vertically
	Components:AnchorChain(allFrames, -5)

	-- Store components for cleanup
	tab.components = allFrames
end

-- ===========================================
-- GLOBAL SYNC TAB (Tab ID 6)
-- ===========================================
function Settings:RefreshGlobalSyncTab()
	local DB = GetDB()
	local tab = self:GetGlobalSyncTabHost()
	if not tab or not DB then
		return
	end

	-- Clear existing content
	HideComponentList(tab)

	local allFrames = {}

	-- Header: Global Friend Sync
	local header = Components:CreateHeader(tab, L.SETTINGS_TAB_GLOBAL_SYNC or "Global Friend Sync")
	table.insert(allFrames, header)

	-- Description
	local desc = Components:CreateLabel(
		tab,
		L.SETTINGS_GLOBAL_SYNC_DESC or "Synchronize your WoW friends list across all characters on this account.",
		true
	)
	table.insert(allFrames, desc)

	-- Enable Global Sync
	local enableSync = Components:CreateCheckbox(
		tab,
		L.SETTINGS_GLOBAL_SYNC_ENABLE or "Enable Global Friend Sync",
		DB:Get("enableGlobalSync", false),
		function(val)
			BetterFriendlistDB.enableGlobalSync = val
			if val then
				BFL:DebugPrint("Global Sync |cff00ff00" .. (L.STATUS_ENABLED or "ENABLED") .. "|r")
				-- Trigger sync
				local GlobalSync = BFL:GetModule("GlobalSync")
				if GlobalSync then
					GlobalSync:OnFriendListUpdate()
				end
			else
				BFL:DebugPrint("Global Sync |cffff0000" .. (L.STATUS_DISABLED or "DISABLED") .. "|r")
			end
			-- Refresh to update table state if needed
			self:RefreshGlobalSyncTab()
		end
	)
	enableSync:SetTooltip(
		L.SETTINGS_GLOBAL_SYNC_ENABLE or "Enable Global Sync",
		L.SETTINGS_GLOBAL_SYNC_ENABLE_TOOLTIP or "Automatically sync friends from other realms to this character."
	)
	table.insert(allFrames, enableSync)

	if DB:Get("enableGlobalSync", false) then
		-- Enable Deletion
		local enableDeletion = Components:CreateCheckbox(
			tab,
			L.SETTINGS_GLOBAL_SYNC_DELETION or "Enable Deletion",
			DB:Get("enableGlobalSyncDeletion", false),
			function(val)
				BetterFriendlistDB.enableGlobalSyncDeletion = val
				if val then
					BFL:DebugPrint("Global Sync Deletion |cff00ff00" .. (L.STATUS_ENABLED or "ENABLED") .. "|r")
				else
					BFL:DebugPrint("Global Sync Deletion |cffff0000" .. (L.STATUS_DISABLED or "DISABLED") .. "|r")
				end
			end
		)
		enableDeletion:SetTooltip(
			L.SETTINGS_GLOBAL_SYNC_DELETION or "Enable Deletion",
			L.SETTINGS_GLOBAL_SYNC_DELETION_DESC
				or "Allow the sync process to remove friends from your list if they are removed from the database."
		)
		table.insert(allFrames, enableDeletion)

		-- Show Deleted Friends
		local showDeleted = Components:CreateCheckbox(
			tab,
			L.SETTINGS_GLOBAL_SYNC_SHOW_DELETED or "Show Deleted Friends",
			self.showDeletedFriends or false,
			function(val)
				self.showDeletedFriends = val
				self:RefreshGlobalSyncTab()
			end
		)
		showDeleted:SetTooltip(
			L.SETTINGS_GLOBAL_SYNC_SHOW_DELETED_TITLE or "Show Deleted Friends",
			L.SETTINGS_GLOBAL_SYNC_SHOW_DELETED_TOOLTIP
				or "Show friends that have been deleted from the database but are kept for history."
		)
		table.insert(allFrames, showDeleted)

		-- Spacer
		table.insert(allFrames, Components:CreateSpacer(tab))

		-- Header: Synced Friends
		local listHeader = Components:CreateHeader(tab, L.SETTINGS_GLOBAL_SYNC_HEADER or "Synced Friends Database")
		table.insert(allFrames, listHeader)

		-- Populate Table
		local count = 0
		local rowHeight = 24

		if BetterFriendlistDB.GlobalFriends then
			for faction, friends in pairs(BetterFriendlistDB.GlobalFriends) do
				for key, value in pairs(friends) do
					if value.guid or value.notes or value.lastSeen or value.deleted then
						-- Check if we should show this friend
						if not value.deleted or self.showDeletedFriends then
							local friendUID = key
							local data = value
							count = count + 1

							local row = CreateFrame("Frame", nil, tab)
							row:SetHeight(rowHeight)
							row:SetPoint("LEFT", 5, 0) -- Maximized width (was 20)
							row:SetPoint("RIGHT", -5, 0) -- Maximized width (was -20)

							-- Background (alternating)
							if count % 2 == 0 then
								local bg = row:CreateTexture(nil, "BACKGROUND")
								bg:SetAllPoints()
								bg:SetColorTexture(1, 1, 1, 0.05)
							end

							-- Parse Name and Realm
							local name, realm = string.match(friendUID, "^(.+)%-(.+)$")
							if not name then
								name = friendUID
								realm = "Unknown"
							end

							-- Name
							local nameText = row:CreateFontString(nil, "ARTWORK", "BetterFriendlistFontNormal")
							nameText:SetPoint("LEFT", 5, 0)
							nameText:SetWidth(130) -- Optimized to prevent clipping (was 155)
							nameText:SetJustifyH("LEFT")
							nameText:SetWordWrap(false)
							nameText:SetText(name)

							-- Realm
							local realmText = row:CreateFontString(nil, "ARTWORK", "BetterFriendlistFontHighlightSmall")
							realmText:SetPoint("LEFT", nameText, "RIGHT", 5, 0)
							realmText:SetWidth(120) -- Optimized to prevent clipping (was 135)
							realmText:SetJustifyH("LEFT")
							realmText:SetWordWrap(false)
							realmText:SetText(realm)

							-- Faction Icon
							local factionIcon = row:CreateTexture(nil, "ARTWORK")
							factionIcon:SetSize(16, 16)
							factionIcon:SetPoint("LEFT", realmText, "RIGHT", 5, 0)
							if faction == "Alliance" then
								factionIcon:SetTexture("Interface\\FriendsFrame\\PlusManz-Alliance")
							elseif faction == "Horde" then
								factionIcon:SetTexture("Interface\\FriendsFrame\\PlusManz-Horde")
							end

							-- Visuals for deleted friends
							if value.deleted then
								nameText:SetTextColor(0.5, 0.5, 0.5)
								realmText:SetTextColor(0.5, 0.5, 0.5)
							end

							-- Action Button (Delete or Restore)
							local actionBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
							actionBtn:SetSize(20, 20)
							actionBtn:SetPoint("RIGHT", -5, 0)

							if value.deleted then
								-- Restore Button
								actionBtn:SetText("R")
								actionBtn:SetScript("OnClick", function()
									BetterFriendlistDB.GlobalFriends[faction][friendUID].deleted = nil
									BetterFriendlistDB.GlobalFriends[faction][friendUID].deletedTime = nil
									BetterFriendlistDB.GlobalFriends[faction][friendUID].restoring = true -- Flag for GlobalSync to restore note

									-- Add back to friend list
									BFL.AddFriend(friendUID)
									BFL:DebugPrint("Restored " .. name .. " to friend list.")

									self:RefreshGlobalSyncTab()
								end)
								actionBtn:SetScript("OnEnter", function(self)
									BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
									BFL_Tooltip:SetText(L.TOOLTIP_RESTORE_FRIEND or "Restore Friend")
									BFL_Tooltip:Show()
								end)
								actionBtn:SetScript("OnLeave", function()
									BFL_Tooltip:Hide()
								end)
							else
								-- Delete Button
								actionBtn:SetText("X")
								actionBtn:SetScript("OnClick", function()
									-- Mark as deleted
									BetterFriendlistDB.GlobalFriends[faction][friendUID].deleted = true
									BetterFriendlistDB.GlobalFriends[faction][friendUID].deletedTime = time()

									-- Immediate deletion from friend list if enabled
									if BetterFriendlistDB.enableGlobalSyncDeletion then
										local removed = false
										-- Check if friend exists in current list
										for i = 1, (C_FriendList.GetNumFriends() or 0) do
											local info = C_FriendList.GetFriendInfoByIndex(i)
											if info then
												-- Robust matching: Check GUID first, then Name-Realm, then Name
												local match = false
												if data.guid and info.guid and data.guid == info.guid then
													match = true
												elseif info.name == friendUID then
													match = true
												elseif info.name == name then
													match = true
												end

												if match then
													BFL.RemoveFriend(info.name) -- Use the name from the API
													BFL:DebugPrint("Removed " .. info.name .. " from friend list.")
													removed = true
													break
												end
											end
										end

										if not removed then
											-- Fallback: Try to remove by UID directly if not found in loop (e.g. offline/cache issue)
											BFL.RemoveFriend(friendUID)
										end
									end

									self:RefreshGlobalSyncTab()
								end)
								actionBtn:SetScript("OnEnter", function(self)
									BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
									BFL_Tooltip:SetText(L.TOOLTIP_DELETE_FRIEND or "Delete Friend")
									BFL_Tooltip:Show()
								end)
								actionBtn:SetScript("OnLeave", function()
									BFL_Tooltip:Hide()
								end)
							end

							-- Edit Button (Note)
							local editBtn = CreateFrame("Button", nil, row)
							editBtn:SetSize(16, 16)
							editBtn:SetPoint("RIGHT", actionBtn, "LEFT", -5, 0)
							editBtn:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Up")
							editBtn:SetScript("OnClick", function()
								StaticPopupDialogs["BFL_EDIT_GLOBAL_NOTE"] = {
									text = string.format(L.POPUP_EDIT_NOTE_TITLE or "Edit Note for %s", name),
									button1 = L.BUTTON_SAVE or "Save",
									button2 = L.BUTTON_CANCEL or "Cancel",
									hasEditBox = true,
									OnShow = function(self)
										self.EditBox:SetText(data.notes or "")
									end,
									OnAccept = function(self)
										local text = self.EditBox:GetText()
										-- Update DB
										BetterFriendlistDB.GlobalFriends[faction][friendUID].notes = text

										-- Update In-Game Friend Note if friend exists locally
										for i = 1, (C_FriendList.GetNumFriends() or 0) do
											local info = C_FriendList.GetFriendInfoByIndex(i)
											if info then
												-- Robust matching
												local match = false
												if data.guid and info.guid and data.guid == info.guid then
													match = true
												elseif info.name == friendUID then
													match = true
												elseif info.name == name then
													match = true
												end

												if match then
													C_FriendList.SetFriendNotes(info.name, text)
													BFL:DebugPrint("Updated note for " .. info.name)
													break
												end
											end
										end

										-- Trigger sync to ensure consistency
										local GlobalSync = BFL:GetModule("GlobalSync")
										if GlobalSync then
											GlobalSync:OnFriendListUpdate()
										end
									end,
									timeout = 0,
									whileDead = true,
									hideOnEscape = true,
								}
								StaticPopup_Show("BFL_EDIT_GLOBAL_NOTE")
							end)

							editBtn:SetScript("OnEnter", function(self)
								BFL_Tooltip:SetOwner(self, "ANCHOR_RIGHT")
								BFL_Tooltip:SetText(L.TOOLTIP_EDIT_NOTE or "Edit Note")
								BFL_Tooltip:Show()
							end)
							editBtn:SetScript("OnLeave", function()
								BFL_Tooltip:Hide()
							end)

							table.insert(allFrames, row)
						end
					end
				end
			end
		end
	end

	-- Anchor all frames vertically
	Components:AnchorChain(allFrames, -5)

	-- Store components for cleanup
	tab.components = allFrames
end

-- Refresh Streamer Mode Tab
function Settings:RefreshStreamerTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.StreamerTab then
		return
	end

	local tab = content.StreamerTab
	local DB = GetDB()

	-- Clear existing content
	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}

	-- Title
	local title = Components:CreateHeader(tab, L.STREAMER_MODE_TITLE or "Streamer Mode")
	table.insert(allFrames, title)

	-- Enable Streamer Mode Toggle
	local enableToggle = Components:CreateCheckbox(
		tab,
		L.SETTINGS_ENABLE_STREAMER_MODE or "Show Streamer Mode Button",
		DB:Get("showStreamerModeButton", true),
		function(checked)
			DB:Set("showStreamerModeButton", checked)
			local StreamerMode = BFL:GetModule("StreamerMode")
			if StreamerMode then
				StreamerMode:UpdateState()
			end
		end
	)
	enableToggle:SetTooltip(L.STREAMER_MODE_TITLE, L.STREAMER_MODE_ENABLE_DESC)
	table.insert(allFrames, enableToggle)

	-- Header: Privacy Options
	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_PRIVACY_OPTIONS or "Privacy Options"))

	-- 1. Custom Header Text Input
	local headerBox = Components:CreateInput(
		tab,
		L.STREAMER_MODE_HEADER_TEXT or "Custom Header Text",
		DB:Get("streamerModeHeaderText", "Streamer Mode"),
		function(val)
			DB:Set("streamerModeHeaderText", val)
			-- Update live if active
			local StreamerMode = BFL:GetModule("StreamerMode")
			if StreamerMode then
				StreamerMode:UpdateState()
			end
		end
	)
	table.insert(allFrames, headerBox)

	-- 3. Name Formatting Dropdown
	local formatOptions = {
		labels = {
			L.SETTINGS_STREAMER_NAME_FORMAT_BATTLENET or "Force BattleTag",
			L.SETTINGS_STREAMER_NAME_FORMAT_NICKNAME or "Force Nickname",
			L.SETTINGS_STREAMER_NAME_FORMAT_NOTE or "Force Note",
		},
		values = { "battletag", "nickname", "note" },
	}

	local formatDropdown = Components:CreateDropdown(
		tab,
		L.SETTINGS_STREAMER_NAME_FORMAT or "Name Formatting",
		formatOptions,
		function(val)
			return val == DB:Get("streamerModeNameFormat", "battletag")
		end,
		function(val)
			DB:Set("streamerModeNameFormat", val)
			-- Refresh Friends List to update names if active
			local StreamerMode = BFL:GetModule("StreamerMode")
			if StreamerMode and StreamerMode:IsActive() then
				if BFL.ForceRefreshFriendsList then
					BFL:ForceRefreshFriendsList()
				end
			end
		end
	)
	-- Shift dropdown to prevent clipping (matching other dropdowns in Settings.lua)
	if formatDropdown.DropDown then
		local point, relativeTo, relativePoint, xOfs, yOfs = formatDropdown.DropDown:GetPoint(1)
		if point then
			formatDropdown.DropDown:SetPoint(point, relativeTo, relativePoint, (xOfs or 0) + 10, yOfs or 0)
		end
	end
	-- Add tooltip
	if formatDropdown.SetTooltip then
		formatDropdown:SetTooltip(L.SETTINGS_STREAMER_NAME_FORMAT, L.SETTINGS_STREAMER_NAME_FORMAT_DESC)
	end
	table.insert(allFrames, formatDropdown)

	-- Anchor all frames vertically
	Components:AnchorChain(allFrames, -5)

	-- Store components for cleanup
	tab.components = allFrames
end

-- Refresh Raid Tab
function Settings:RefreshRaidTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.RaidTab then
		return
	end

	local tab = content.RaidTab
	local DB = GetDB()
	if not DB then
		return
	end

	-- Clear existing content
	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}

	-- Header
	local header = Components:CreateHeader(tab, L.SETTINGS_TAB_RAID or "Raid & Group")
	table.insert(allFrames, header)

	-- Description
	local desc = Components:CreateLabel(
		tab,
		L.SETTINGS_RAID_DESC or "Configure shortcuts for raid and group management actions on the Raid Frame.",
		true
	)
	table.insert(allFrames, desc)

	-- Spacer
	table.insert(allFrames, Components:CreateSpacer(tab))

	local readyCheckToggle = Components:CreateCheckbox(tab, {
		label = L.SETTINGS_RAID_ENABLE_READY_CHECK_BUTTON or "Enable Ready Check Button",
		initialValue = DB:Get("enableReadyCheckButton", false),
		callback = function(val)
			DB:Set("enableReadyCheckButton", val)
			if BetterRaidFrame_UpdateControlPanelButtons then
				BetterRaidFrame_UpdateControlPanelButtons()
			end
			local RaidFrame = BFL:GetModule("RaidFrame")
			if RaidFrame and RaidFrame.UpdateControlPanelLayout then
				RaidFrame:UpdateControlPanelLayout()
			end
		end,
		tooltipTitle = L.SETTINGS_RAID_ENABLE_READY_CHECK_BUTTON or "Enable Ready Check Button",
		tooltipDesc = L.SETTINGS_RAID_ENABLE_READY_CHECK_BUTTON_DESC
			or "Show a compact ready check button next to Raid Info in the Raid tab.",
	})
	table.insert(allFrames, readyCheckToggle)

	table.insert(allFrames, Components:CreateSpacer(tab))
	table.insert(allFrames, Components:CreateHeader(tab, L.SETTINGS_RAID_SHORTCUTS_TITLE or "Raid Shortcuts"))

	-- Shortcuts Configuration
	local shortcuts = DB:Get("raidShortcuts") or {}

	-- Helper Options
	local modifierOptions = {
		labels = {
			L.SETTINGS_RAID_MODIFIER_NONE or "None",
			"Shift",
			"Ctrl",
			"Alt",
			"Shift+Ctrl",
			"Shift+Alt",
			"Ctrl+Alt",
		},
		values = { "NONE", "SHIFT", "CTRL", "ALT", "SHIFT-CTRL", "SHIFT-ALT", "CTRL-ALT" },
	}

	local buttonOptions = {
		labels = {
			L.SETTINGS_RAID_MOUSE_LEFT or "Left Click",
			L.SETTINGS_RAID_MOUSE_RIGHT or "Right Click",
			L.SETTINGS_RAID_MOUSE_MIDDLE or "Middle Click",
			"Button 4",
			"Button 5",
		},
		values = { "LeftButton", "RightButton", "Button3", "Button4", "Button5" },
	}

	-- Helper function to create rows
	local function CreateShortcutRow(actionName, actionKey)
		local isEnabled = DB:Get("raidShortcutEnabled_" .. actionKey, true)

		local currentMod = "NONE"
		local currentBtn = "LeftButton"
		if shortcuts[actionKey] then
			currentMod = shortcuts[actionKey].modifier or "NONE"
			currentBtn = shortcuts[actionKey].button or "LeftButton"
		end

		-- Row container
		local row = CreateFrame("Frame", nil, tab)
		row:SetHeight(40)
		row:SetPoint("LEFT", 0, 0)
		row:SetPoint("RIGHT", 0, 0)

		-- Checkbox (Manual creation for Left-Alignment)
		local template = "SettingsCheckboxTemplate"
		if BFL.IsClassic then
			template = "InterfaceOptionsCheckButtonTemplate"
		end

		local checkbox = CreateFrame("CheckButton", nil, row, template)
		checkbox:SetSize(26, 26)
		checkbox:SetPoint("LEFT", 0, 0)
		checkbox:SetChecked(isEnabled)

		-- Fix text regions in template
		if checkbox.Text then
			checkbox.Text:SetText("")
		end
		if checkbox.SetText then
			checkbox:SetText("")
		end
		local regions = { checkbox:GetRegions() }
		for _, region in ipairs(regions) do
			if region:GetObjectType() == "FontString" then
				region:SetText("")
			end
		end

		-- Disable highlighting when hovering (no tooltip)
		checkbox:SetScript("OnEnter", function() end)

		checkbox:SetScript("OnClick", function(self)
			local val = self:GetChecked()
			DB:Set("raidShortcutEnabled_" .. actionKey, val)

			-- Live refresh: Update all raid buttons
			local RaidFrame = BFL:GetModule("RaidFrame")
			if RaidFrame then
				RaidFrame:UpdateAllMemberButtons()
			end

			-- Refresh tab to show/hide shortcut options
			C_Timer.After(0.05, function()
				Settings:RefreshRaidTab()
			end)
		end)

		-- Label
		local label = row:CreateFontString(nil, "ARTWORK", "BetterFriendlistFontHighlight")
		label:SetPoint("LEFT", checkbox, "RIGHT", 5, 0)
		label:SetWidth(200) -- Increased width for German text
		label:SetJustifyH("LEFT")
		label:SetText(actionName)
		label:SetWordWrap(false)

		-- Validation Function
		local function IsReservedCombination(mod, btn)
			-- Reserved: LeftClick (Drag&Drop), RightClick (Menu), Ctrl+LeftClick (Multi-Select)
			if mod == "NONE" and btn == "LeftButton" then
				return true
			end
			if mod == "NONE" and btn == "RightButton" then
				return true
			end
			if mod == "CTRL" and btn == "LeftButton" then
				return true
			end
			return false
		end

		-- Get valid modifiers for a button
		local function GetValidModifiersForButton(btn)
			local validLabels = {}
			local validValues = {}
			for i = 1, #modifierOptions.values do
				if not IsReservedCombination(modifierOptions.values[i], btn) then
					table.insert(validLabels, modifierOptions.labels[i])
					table.insert(validValues, modifierOptions.values[i])
				end
			end
			return { labels = validLabels, values = validValues }
		end

		-- Get valid buttons for a modifier
		local function GetValidButtonsForModifier(mod)
			local validLabels = {}
			local validValues = {}
			for i = 1, #buttonOptions.values do
				if not IsReservedCombination(mod, buttonOptions.values[i]) then
					table.insert(validLabels, buttonOptions.labels[i])
					table.insert(validValues, buttonOptions.values[i])
				end
			end
			return { labels = validLabels, values = validValues }
		end

		local function ValidateShortcut(mod, btn)
			return not IsReservedCombination(mod, btn)
		end

		-- Only show dropdowns if enabled
		if isEnabled then
			-- Get current shortcuts
			local s = DB:Get("raidShortcuts") or {}

			-- Modifier Dropdown (dynamically filtered)
			local currentBtn = s[actionKey] and s[actionKey].button or "LeftButton"
			local validModifiers = GetValidModifiersForButton(currentBtn)

			local modDropdown = Components:CreateDropdown(
				row,
				"",
				validModifiers, -- Use filtered list
				function(val)
					local s = DB:Get("raidShortcuts") or {}
					local m = "NONE"
					if s[actionKey] then
						m = s[actionKey].modifier or "NONE"
					end
					return val == m
				end,
				function(val)
					-- Snapshot update
					local s = DB:Get("raidShortcuts") or {}
					if not s[actionKey] then
						s[actionKey] = {}
					end

					local currentBtnVal = s[actionKey].button or "LeftButton"
					if not ValidateShortcut(val, currentBtnVal) then
						Settings:RefreshRaidTab() -- Reset UI
						return
					end

					s[actionKey].modifier = val
					DB:Set("raidShortcuts", s)

					-- Live refresh: Update all raid buttons
					local RaidFrame = BFL:GetModule("RaidFrame")
					if RaidFrame then
						RaidFrame:UpdateAllMemberButtons()
					end

					-- Refresh button dropdown (modifier changed, button options may change)
					C_Timer.After(0.05, function()
						Settings:RefreshRaidTab()
					end)
				end
			)
			-- MANUAL LAYOUT FIX: Disable auto-layout to prevent 170px width enforcement
			modDropdown:SetScript("OnSizeChanged", nil)
			modDropdown:ClearAllPoints()
			modDropdown:SetPoint("LEFT", label, "RIGHT", 5, 0)
			modDropdown:SetWidth(115)
			modDropdown:SetScale(1)
			modDropdown.Label:Hide()

			local ddMod = modDropdown.DropDown
			ddMod:ClearAllPoints()
			local ddModIsModern = BFL.IsModernDropdown(ddMod)
			if not ddModIsModern then
				BFL.SetDropdownWidth(ddMod, 95)
				ddMod:SetPoint("TOPLEFT", modDropdown, "TOPLEFT", -15, -2)
			else
				ddMod:SetPoint("LEFT", modDropdown, "LEFT", 0, 0)
				ddMod:SetWidth(115)
			end

			-- Button Dropdown (dynamically filtered)
			local currentMod = s[actionKey] and s[actionKey].modifier or "NONE"
			local validButtons = GetValidButtonsForModifier(currentMod)

			local btnDropdown = Components:CreateDropdown(
				row,
				"",
				validButtons, -- Use filtered list
				function(val)
					local s = DB:Get("raidShortcuts") or {}
					local b = "LeftButton"
					if s[actionKey] then
						b = s[actionKey].button or "LeftButton"
					end
					return val == b
				end,
				function(val)
					local s = DB:Get("raidShortcuts") or {}
					if not s[actionKey] then
						s[actionKey] = {}
					end

					local currentModVal = s[actionKey].modifier or "NONE"
					if not ValidateShortcut(currentModVal, val) then
						Settings:RefreshRaidTab() -- Reset UI
						return
					end

					s[actionKey].button = val
					DB:Set("raidShortcuts", s)

					-- Live refresh: Update all raid buttons
					local RaidFrame = BFL:GetModule("RaidFrame")
					if RaidFrame then
						RaidFrame:UpdateAllMemberButtons()
					end

					-- Refresh modifier dropdown (button changed, modifier options may change)
					C_Timer.After(0.05, function()
						Settings:RefreshRaidTab()
					end)
				end
			)
			-- MANUAL LAYOUT FIX: Disable auto-layout
			btnDropdown:SetScript("OnSizeChanged", nil)
			btnDropdown:ClearAllPoints()
			btnDropdown:SetPoint("LEFT", modDropdown, "RIGHT", 5, 0)
			btnDropdown:SetWidth(115)
			btnDropdown:SetScale(1)
			btnDropdown.Label:Hide()

			local ddBtn = btnDropdown.DropDown
			ddBtn:ClearAllPoints()
			local ddBtnIsModern = BFL.IsModernDropdown(ddBtn)
			if not ddBtnIsModern then
				BFL.SetDropdownWidth(ddBtn, 95)
				ddBtn:SetPoint("TOPLEFT", btnDropdown, "TOPLEFT", -15, -2)
			else
				ddBtn:SetPoint("LEFT", btnDropdown, "LEFT", 0, 0)
				ddBtn:SetWidth(115)
			end

			-- If action is Target, maybe verify it defaults to None+Left?
			-- It is configured in DB defaults.
		end

		table.insert(allFrames, row)
	end

	-- Create rows
	CreateShortcutRow(L.SETTINGS_RAID_ACTION_MAIN_TANK or "Set Main Tank", "mainTank")
	CreateShortcutRow(L.SETTINGS_RAID_ACTION_MAIN_ASSIST or "Set Main Assist", "mainAssist")
	CreateShortcutRow(L.SETTINGS_RAID_ACTION_RAID_LEAD or "Set Raid Leader", "lead")
	CreateShortcutRow(L.SETTINGS_RAID_ACTION_PROMOTE or "Promo/Demote Assistant", "promote")

	-- Warning about reloading/combat
	table.insert(allFrames, Components:CreateSpacer(tab))
	local warning = Components:CreateLabel(
		tab,
		L.SETTINGS_RAID_WARNING or "Note: Shortcuts are secure actions and update immediately (when out of combat).",
		true
	)
	warning:SetTextColor(0.7, 0.7, 0.7)
	table.insert(allFrames, warning)

	local AutoRaidAssist = BFL:GetModule("AutoRaidAssist")
	if AutoRaidAssist and AutoRaidAssist.RenderSettingsPage then
		table.insert(allFrames, Components:CreateSpacer(tab))
		table.insert(allFrames, Components:CreateHeader(tab, L.AUTO_RAID_ASSIST_TITLE or "Auto Raid Assist"))
		table.insert(
			allFrames,
			Components:CreateLabel(
				tab,
				L.AUTO_RAID_ASSIST_DESC
					or "Automatically promote selected friends and characters to raid assistant when they join your raid.",
				true
			)
		)

		local autoRaidAssistContainer = CreateFrame("Frame", nil, tab)
		autoRaidAssistContainer:SetPoint("LEFT", 0, 0)
		autoRaidAssistContainer:SetPoint("RIGHT", 0, 0)
		autoRaidAssistContainer:SetHeight(AutoRaidAssist:GetSettingsPageHeight())
		AutoRaidAssist:RenderSettingsPage(autoRaidAssistContainer, { variant = "legacy" })
		table.insert(allFrames, autoRaidAssistContainer)
	end

	-- Anchor
	Components:AnchorChain(allFrames, -5)

	tab.components = allFrames
end

-- ========================================
-- WHO Tab
-- ========================================
function Settings:RefreshWhoTab()
	if not settingsFrame or not Components then
		return
	end

	local content = settingsFrame.ContentScrollFrame.Content
	if not content or not content.WhoTab then
		return
	end

	local tab = content.WhoTab
	local DB = GetDB()
	if not DB then
		return
	end

	-- Clear existing content
	if tab.components then
		for _, component in ipairs(tab.components) do
			if component.Hide then
				component:Hide()
			end
		end
	end
	tab.components = {}

	local allFrames = {}
	local L = BFL.L

	-- Header
	local header = Components:CreateHeader(tab, L.SETTINGS_TAB_WHO or "Who")
	table.insert(allFrames, header)

	-- Description
	local desc = Components:CreateLabel(
		tab,
		L.WHO_SETTINGS_DESC or "Configure the appearance and behavior of the Who search results.",
		true
	)
	table.insert(allFrames, desc)

	table.insert(allFrames, Components:CreateSpacer(tab))

	-- Visual Options
	local visualHeader = Components:CreateHeader(tab, L.WHO_SETTINGS_VISUAL_HEADER or "Visual")
	table.insert(allFrames, visualHeader)

	-- Show Class Icons
	local classIconsCb = Components:CreateCheckbox(
		tab,
		L.WHO_SETTINGS_CLASS_ICONS or "Show Class Icons",
		DB:Get("whoShowClassIcons", true),
		function(val)
			DB:Set("whoShowClassIcons", val)
			local WhoFrameModule = BFL:GetModule("WhoFrame")
			if WhoFrameModule then
				WhoFrameModule:UpdateResponsiveLayout()
				WhoFrameModule:Update(true)
			end
		end
	)
	classIconsCb:SetTooltip(
		L.WHO_SETTINGS_CLASS_ICONS or "Show Class Icons",
		L.WHO_SETTINGS_CLASS_ICONS_DESC or "Display class icons next to player names."
	)
	table.insert(allFrames, classIconsCb)

	-- Class-Colored Names
	local classColorsCb = Components:CreateCheckbox(
		tab,
		L.WHO_SETTINGS_CLASS_COLORS or "Class-Colored Names",
		DB:Get("whoClassColorNames", true),
		function(val)
			DB:Set("whoClassColorNames", val)
			local WhoFrameModule = BFL:GetModule("WhoFrame")
			if WhoFrameModule then
				WhoFrameModule:Update(true)
			end
		end
	)
	classColorsCb:SetTooltip(
		L.WHO_SETTINGS_CLASS_COLORS or "Class-Colored Names",
		L.WHO_SETTINGS_CLASS_COLORS_DESC or "Color player names by their class."
	)
	table.insert(allFrames, classColorsCb)

	-- Level Difficulty Colors
	local levelColorsCb = Components:CreateCheckbox(
		tab,
		L.WHO_SETTINGS_LEVEL_COLORS or "Level Difficulty Colors",
		DB:Get("whoLevelColors", true),
		function(val)
			DB:Set("whoLevelColors", val)
			local WhoFrameModule = BFL:GetModule("WhoFrame")
			if WhoFrameModule then
				WhoFrameModule:Update(true)
			end
		end
	)
	levelColorsCb:SetTooltip(
		L.WHO_SETTINGS_LEVEL_COLORS or "Level Difficulty Colors",
		L.WHO_SETTINGS_LEVEL_COLORS_DESC or "Color levels by difficulty relative to your level."
	)
	table.insert(allFrames, levelColorsCb)

	-- Zebra Stripes
	local zebraCb = Components:CreateCheckbox(
		tab,
		L.WHO_SETTINGS_ZEBRA or "Alternating Row Background",
		DB:Get("whoZebraStripes", true),
		function(val)
			DB:Set("whoZebraStripes", val)
			local WhoFrameModule = BFL:GetModule("WhoFrame")
			if WhoFrameModule then
				WhoFrameModule:Update(true)
			end
		end
	)
	zebraCb:SetTooltip(
		L.WHO_SETTINGS_ZEBRA or "Alternating Row Background",
		L.WHO_SETTINGS_ZEBRA_DESC or "Show subtle alternating row backgrounds for readability."
	)
	table.insert(allFrames, zebraCb)

	table.insert(allFrames, Components:CreateSpacer(tab))

	-- Behavior Options
	local behaviorHeader = Components:CreateHeader(tab, L.WHO_SETTINGS_BEHAVIOR_HEADER or "Behavior")
	table.insert(allFrames, behaviorHeader)

	-- Double-Click Action
	table.insert(
		allFrames,
		Components:CreateDropdown(tab, L.WHO_SETTINGS_DOUBLE_CLICK or "Double-Click Action", {
			labels = {
				L.WHO_DOUBLE_CLICK_WHISPER or "Whisper",
				L.WHO_DOUBLE_CLICK_INVITE or "Invite to Group",
			},
			values = { "whisper", "invite" },
		}, function(val)
			return val == DB:Get("whoDoubleClickAction", "whisper")
		end, function(val)
			DB:Set("whoDoubleClickAction", val)
		end)
	)

	-- Anchor
	Components:AnchorChain(allFrames, -5)

	tab.components = allFrames
end

return Settings
