local _, ns = ...
local L = ns.L or {}

local UI = {}
ns.UI = UI

local BACKDROP_TEMPLATE = BackdropTemplateMixin and "BackdropTemplate" or nil
local MAIN_FRAME_STRATA = "HIGH"
local BLIZZARD_PANEL_STRATA = "DIALOG"

local BLIZZARD_PANEL_FRAME_NAMES = {
    "AchievementFrame",
    "AddonList",
    "AuctionFrame",
    "AuctionHouseFrame",
    "BankFrame",
    "BarberShopFrame",
    "CalendarFrame",
    "CharacterFrame",
    "ChatConfigFrame",
    "ClassTalentFrame",
    "ClassTrainerFrame",
    "CollectionsJournal",
    "CurrencyTransferMenu",
    "DressUpFrame",
    "EncounterJournal",
    "FriendsFrame",
    "GameMenuFrame",
    "GossipFrame",
    "GuildFrame",
    "HelpFrame",
    "InspectFrame",
    "InspectRecipeFrame",
    "ItemInteractionFrame",
    "ItemSocketingFrame",
    "ItemTextFrame",
    "ItemUpgradeFrame",
    "KeyBindingFrame",
    "LootFrame",
    "MacroFrame",
    "MailFrame",
    "MajorFactionRenownFrame",
    "MerchantFrame",
    "PerksProgramFrame",
    "PlayerSpellsFrame",
    "PlayerTalentFrame",
    "ProfessionsBookFrame",
    "ProfessionsCustomerOrdersFrame",
    "ProfessionsFrame",
    "PVEFrame",
    "QuestFrame",
    "QuestLogDetailFrame",
    "QuestLogFrame",
    "QuestLogPopupDetailFrame",
    "QuickKeybindFrame",
    "SettingsPanel",
    "SpellBookFrame",
    "StableFrame",
    "TabardFrame",
    "TalentFrame",
    "TaxiFrame",
    "TimeManagerFrame",
    "TradeFrame",
    "TradeSkillFrame",
    "TransmogFrame",
    "VoidStorageFrame",
    "WeeklyRewardsFrame",
    "WorldStateScoreFrame",

    "WarbandCollectionFrame",
    "WarbandCollectionsFrame",
    "WarbandSceneCollectionFrame",
    "WarbandSceneJournalFrame",
    "WardrobeCollectionFrame",
    "WardrobeFrame",

    "HouseFinderFrame",
    "HouseListFrame",
    "HousingBulletinBoardFrame",
    "HousingCornerstoneFrame",
    "HousingCornerstoneHouseInfoFrame",
    "HousingCornerstonePurchaseFrame",
    "HousingCornerstoneVisitorFrame",
    "HousingCreateCharterNeighborhoodConfirmationFrame",
    "HousingCreateNeighborhoodCharterFrame",
    "HousingDashboardFrame",
    "HousingHouseSettingsFrame",
    "HousingModelPreviewFrame",

    "ContainerFrameCombinedBags",
}

for containerIndex = 1, 13 do
    BLIZZARD_PANEL_FRAME_NAMES[#BLIZZARD_PANEL_FRAME_NAMES + 1] = "ContainerFrame" .. containerIndex
end

local BLIZZARD_PANEL_FRAME_NAME_SET = {}
for _, frameName in ipairs(BLIZZARD_PANEL_FRAME_NAMES) do
    BLIZZARD_PANEL_FRAME_NAME_SET[frameName] = true
end

local BLIZZARD_PANEL_RAISE_ONLY_FRAME_NAMES = {
    "WorldMapFrame",
}

local BLIZZARD_PANEL_RAISE_ONLY_FRAME_NAME_SET = {}
for _, frameName in ipairs(BLIZZARD_PANEL_RAISE_ONLY_FRAME_NAMES) do
    BLIZZARD_PANEL_RAISE_ONLY_FRAME_NAME_SET[frameName] = true
end

local blizzardPanelOriginalStrata = {}
local blizzardPanelStackHooks = {}
local blizzardPanelRaiseOnlyHooks = {}
local blizzardPanelStackingInstalled = false

local function isVaultloomFrameShown()
    local frame = _G and _G.VaultloomFrame or nil
    return type(frame) == "table" and type(frame.IsShown) == "function" and frame:IsShown()
end

local function restoreBlizzardPanelStacking(frame)
    local originalStrata = blizzardPanelOriginalStrata[frame]
    if originalStrata and type(frame) == "table" and type(frame.SetFrameStrata) == "function" then
        pcall(frame.SetFrameStrata, frame, originalStrata)
    end
    blizzardPanelOriginalStrata[frame] = nil
end

local function raiseBlizzardPanelAboveVaultloom(frame)
    if type(frame) ~= "table" or not isVaultloomFrameShown() then
        return
    end
    if type(frame.IsShown) == "function" and not frame:IsShown() then
        return
    end
    if type(frame.IsForbidden) == "function" and frame:IsForbidden() then
        return
    end

    local originalStrata
    if type(frame.GetFrameStrata) == "function" then
        local ok, strata = pcall(frame.GetFrameStrata, frame)
        if ok and type(strata) == "string" and strata ~= "" then
            originalStrata = strata
        end
    end

    if originalStrata == "FULLSCREEN" or originalStrata == "FULLSCREEN_DIALOG" or originalStrata == "TOOLTIP" then
        return
    end

    if originalStrata and originalStrata ~= BLIZZARD_PANEL_STRATA and not blizzardPanelOriginalStrata[frame] then
        blizzardPanelOriginalStrata[frame] = originalStrata
    end
    if type(frame.SetFrameStrata) == "function" then
        pcall(frame.SetFrameStrata, frame, BLIZZARD_PANEL_STRATA)
    end
    if type(frame.SetToplevel) == "function" then
        pcall(frame.SetToplevel, frame, true)
    end
    if type(frame.Raise) == "function" then
        pcall(frame.Raise, frame)
    end
end

local function raiseBlizzardPanelWithoutRestacking(frame)
    if type(frame) ~= "table" or not isVaultloomFrameShown() then
        return
    end
    if type(frame.IsShown) == "function" and not frame:IsShown() then
        return
    end
    if type(frame.IsForbidden) == "function" and frame:IsForbidden() then
        return
    end

    if type(frame.SetToplevel) == "function" then
        pcall(frame.SetToplevel, frame, true)
    end
    if type(frame.Raise) == "function" then
        pcall(frame.Raise, frame)
    end
end

local function hookBlizzardPanelStacking(frame)
    if type(frame) ~= "table" or blizzardPanelStackHooks[frame] then
        return
    end
    blizzardPanelStackHooks[frame] = true

    if type(frame.HookScript) == "function" then
        pcall(frame.HookScript, frame, "OnShow", function(panel)
            raiseBlizzardPanelAboveVaultloom(panel)
        end)
        pcall(frame.HookScript, frame, "OnHide", function(panel)
            restoreBlizzardPanelStacking(panel)
        end)
    end
end

local function hookBlizzardPanelRaiseOnly(frame)
    if type(frame) ~= "table" or blizzardPanelRaiseOnlyHooks[frame] then
        return
    end
    blizzardPanelRaiseOnlyHooks[frame] = true

    if type(frame.HookScript) == "function" then
        pcall(frame.HookScript, frame, "OnShow", function(panel)
            raiseBlizzardPanelWithoutRestacking(panel)
        end)
    end
end

local function registerBlizzardPanelStackingFrame(frame)
    if type(frame) ~= "table" then
        return
    end
    local frameName = type(frame.GetName) == "function" and frame:GetName() or nil
    if BLIZZARD_PANEL_RAISE_ONLY_FRAME_NAME_SET[frameName] then
        hookBlizzardPanelRaiseOnly(frame)
        raiseBlizzardPanelWithoutRestacking(frame)
        return
    end
    if not BLIZZARD_PANEL_FRAME_NAME_SET[frameName] then
        return
    end
    hookBlizzardPanelStacking(frame)
    raiseBlizzardPanelAboveVaultloom(frame)
end

local function scanBlizzardPanelStackingFrames()
    for _, frameName in ipairs(BLIZZARD_PANEL_FRAME_NAMES) do
        registerBlizzardPanelStackingFrame(_G and _G[frameName] or nil)
    end
    for _, frameName in ipairs(BLIZZARD_PANEL_RAISE_ONLY_FRAME_NAMES) do
        registerBlizzardPanelStackingFrame(_G and _G[frameName] or nil)
    end
end

local function scheduleBlizzardPanelStackingScan(delay)
    if C_Timer and type(C_Timer.After) == "function" then
        C_Timer.After(delay or 0, scanBlizzardPanelStackingFrames)
    else
        scanBlizzardPanelStackingFrames()
    end
end

local function restoreAllBlizzardPanelStacking()
    for frame in pairs(blizzardPanelOriginalStrata) do
        restoreBlizzardPanelStacking(frame)
    end
end

local function installBlizzardPanelStacking()
    if blizzardPanelStackingInstalled then
        return
    end
    blizzardPanelStackingInstalled = true

    if type(hooksecurefunc) == "function" then
        if type(ShowUIPanel) == "function" then
            pcall(hooksecurefunc, "ShowUIPanel", function(frame)
                registerBlizzardPanelStackingFrame(frame)
                scheduleBlizzardPanelStackingScan(0)
                scheduleBlizzardPanelStackingScan(0.1)
            end)
        end
        if type(ToggleFrame) == "function" then
            pcall(hooksecurefunc, "ToggleFrame", function(frame)
                registerBlizzardPanelStackingFrame(frame)
                scheduleBlizzardPanelStackingScan(0)
                scheduleBlizzardPanelStackingScan(0.1)
            end)
        end
        if C_AddOns and type(C_AddOns.LoadAddOn) == "function" then
            pcall(hooksecurefunc, C_AddOns, "LoadAddOn", function()
                scheduleBlizzardPanelStackingScan(0)
                scheduleBlizzardPanelStackingScan(0.2)
            end)
        end
    end

    scanBlizzardPanelStackingFrames()
end

local UIData = ns.UIData or {}
local PREMIUM = UIData.PREMIUM or {}
local THEME = UIData.THEME or {}
local MAIN_TABS = UIData.MAIN_TABS or {}
local SUB_TABS = UIData.SUB_TABS or {}
local UI_LAYOUT = UIData.UI_LAYOUT or {}
local SIDEBAR_SORT_OPTIONS = UIData.SIDEBAR_SORT_OPTIONS or {}
local STATUS_COLORS = UIData.STATUS_COLORS or {}
local RAID_DIFFICULTY_OPTIONS = UIData.RAID_DIFFICULTY_OPTIONS or {}
local DUNGEON_DIFFICULTY_OPTIONS = UIData.DUNGEON_DIFFICULTY_OPTIONS or {}
local UIMythicPlusView = ns.UIMythicPlusView or {}
local UIMythicPlusCard = ns.UIMythicPlusCard or {}
local buildMythicPlusSeasonOneView = UIMythicPlusView.BuildSeasonOneView or function()
    return {
        available = false,
        message = L.MYTHIC_PLUS_UNAVAILABLE or "Mythic+ data is not available yet.",
    }
end
local UIHousingView = ns.UIHousingView or {}
local UIHousingCard = ns.UIHousingCard or {}
local buildHousingEndeavorsView = UIHousingView.BuildEndeavorsView or function()
    return {
        available = false,
        message = L.HOUSING_UNAVAILABLE or "Housing data is not available yet.",
    }
end
local UIPveView = ns.UIPveView or {}
local buildPveWeeklyListView = UIPveView.BuildWeeklyListView or function()
    return { rows = {} }
end
local buildPvpWeeklyListView = UIPveView.BuildPvpWeeklyListView or function()
    return { rows = {} }
end
local buildProfessionWeeklyListView = UIPveView.BuildProfessionWeeklyListView or function()
    return { rows = {}, knowledgeEntries = {} }
end
local buildPveDelvesListView = UIPveView.BuildDelvesListView or function()
    return { rows = {} }
end
local buildPveEventsListView = UIPveView.BuildEventsListView or function()
    return { rows = {} }
end
local buildPveDarkmoonListView = UIPveView.BuildDarkmoonListView or function()
    return { rows = {} }
end
local buildPveWorldListView = UIPveView.BuildWorldListView or function()
    return { rows = {} }
end
local buildPveDailyListView = UIPveView.BuildDailyListView or function()
    return { rows = {} }
end
local buildPveOtherListView = UIPveView.BuildOtherListView or function()
    return { rows = {} }
end
local buildPveRaresView = UIPveView.BuildRaresView or function()
    return { zones = {} }
end
local buildPvePreyView = UIPveView.BuildPreyView or function()
    return {}
end
local UIVaultView = ns.UIVaultView or {}
local UISidebarView = ns.UISidebarView or {}
local UIUtilityView = ns.UIUtilityView or {}
local UIWorkbenchLayout = ns.UIWorkbenchLayout or {}
local UIPveCardRefresh = ns.UIPveCardRefresh or {}
local UITransientPanels = ns.UITransientPanels or {}
local UINavigationTabs = ns.UINavigationTabs or {}
local UIFeaturesPanel = ns.UIFeaturesPanel or {}
local UIProgressBars = ns.UIProgressBars or {}
local UIStatusRows = ns.UIStatusRows or {}
local UISettingsPanel = ns.UISettingsPanel or {}
local UIButtonWidgets = ns.UIButtonWidgets or {}
local UISectionFrames = ns.UISectionFrames or {}
local UIScrollFrames = ns.UIScrollFrames or {}
local UICustomTaskView = ns.UICustomTaskView or {}
local UIRenownView = ns.UIRenownView or {}
local UICompendiumView = ns.UICompendiumView or {}

local function bindFeatureControlTooltip(button)
    if UIFeaturesPanel.BindFeatureControlTooltip then
        UIFeaturesPanel.BindFeatureControlTooltip(button, THEME)
    end
end

local function getMaxSubTabCount()
    local maxCount = 0
    for _, tabs in pairs(SUB_TABS) do
        maxCount = math.max(maxCount, #tabs)
    end
    return maxCount
end

local UIJournalRows = ns.UIJournalRows or {}
local createRaidJournalRowButton = UIJournalRows.CreateRowButton or function(parent)
    return CreateFrame("Button", nil, parent, BACKDROP_TEMPLATE)
end
local createRaidJournalLootRow = UIJournalRows.CreateLootRow or function(parent)
    return CreateFrame("Frame", nil, parent, BACKDROP_TEMPLATE)
end
local applyRaidLootRow = UIJournalRows.ApplyLootRow or function() end

local UIJournalView = ns.UIJournalView or {}
local UIRaidModeCard = ns.UIRaidModeCard or {}
local UIWishlistCard = ns.UIWishlistCard or {}
local getJournalDifficultyOptions = UIJournalView.GetJournalDifficultyOptions or function(mainTabKey)
    if mainTabKey == "dungeons" then
        return DUNGEON_DIFFICULTY_OPTIONS
    end

    return RAID_DIFFICULTY_OPTIONS
end
local usesDungeonDailyCounter = UIJournalView.UsesDungeonDailyCounter or function(difficultyKey)
    return difficultyKey == "mythic"
end
local isJournalSubTab = UIJournalView.IsJournalSubTab or function()
    return false
end
local getRaidDifficultyLabel = UIJournalView.GetRaidDifficultyLabel or function(difficultyKey, mainTabKey)
    for _, option in ipairs(getJournalDifficultyOptions(mainTabKey)) do
        if option.key == difficultyKey then
            return option.label()
        end
    end

    return L.UNKNOWN or "Unknown"
end

local function buildUnavailableJournalView(mainTabKey)
    return {
        raids = {},
        loot = {},
        selectedRaidKey = "",
        selectedBossKey = "",
        difficultyKey = "normal",
        classFilterKey = "player",
        classFilterName = L.UNKNOWN or "Unknown",
        classFilterMode = "player",
        difficultyOptions = getJournalDifficultyOptions(mainTabKey),
        primaryLabel = mainTabKey == "dungeons" and (L.DUNGEONS or "Dungeons") or (L.RAIDS or "Raids"),
        primaryLabelPlural = mainTabKey == "dungeons" and (L.DUNGEONS or "Dungeons") or (L.RAIDS or "Raids"),
        bossesLabel = L.RAID_JOURNAL_BOSSES or "Bosses",
        listSubtitle = L.RAID_JOURNAL_FALLBACK_DESCRIPTION or "Journal data is not available yet.",
        mainTabKey = mainTabKey or "raids",
    }
end

local buildMidnightRaidJournalView = UIJournalView.BuildMidnightRaidJournalView or function()
    return buildUnavailableJournalView("raids")
end
local buildMidnightDungeonJournalView = UIJournalView.BuildMidnightDungeonJournalView or function()
    return buildUnavailableJournalView("dungeons")
end
local buildSeasonOneDungeonJournalView = UIJournalView.BuildSeasonOneDungeonJournalView or function()
    return buildUnavailableJournalView("dungeons")
end

local MID_UPGRADE_CRESTS = type(ns.MID_UPGRADE_CRESTS) == "table" and ns.MID_UPGRADE_CRESTS or {}
local MID_RESOURCE_ENTRIES = type(ns.MID_RESOURCE_ENTRIES) == "table" and ns.MID_RESOURCE_ENTRIES or {}

local function formatElapsed(seconds)
    seconds = math.max(0, math.floor(seconds or 0))

    local days = math.floor(seconds / 86400)
    local hours = math.floor((seconds % 86400) / 3600)
    local minutes = math.floor((seconds % 3600) / 60)

    if days > 0 then
        return string.format(L.TIME_DAY_HOUR or "%d d %d h", days, hours)
    end

    if hours > 0 then
        return string.format(L.TIME_HOUR_MIN or "%d h %d m", hours, minutes)
    end

    return string.format(L.TIME_MIN or "%d m", minutes)
end

local function formatLastSeen(timestamp)
    if not timestamp then
        return L.TIME_NEVER or "never"
    end

    local delta = time() - timestamp
    if delta < 60 then
        return L.TIME_JUST_NOW or "just now"
    end

    return string.format(L.TIME_AGO or "%s ago", formatElapsed(delta))
end

local function formatMoneyText(money, iconSize)
    money = tonumber(money)
    if not money then
        return nil
    end

    money = math.max(0, math.floor(money + 0.5))
    iconSize = tonumber(iconSize) or 12
    if C_CurrencyInfo and type(C_CurrencyInfo.GetCoinTextureString) == "function" then
        local ok, text = pcall(C_CurrencyInfo.GetCoinTextureString, money, iconSize)
        if ok and type(text) == "string" and text ~= "" then
            return text
        end
    end

    local gold = math.floor(money / 10000)
    local silver = math.floor((money % 10000) / 100)
    local copper = money % 100
    local function formatAmount(value)
        value = math.floor(tonumber(value) or 0)
        if type(BreakUpLargeNumbers) == "function" then
            return BreakUpLargeNumbers(value)
        end
        return tostring(value)
    end

    return string.format(
        "%s|TInterface\\MoneyFrame\\UI-GoldIcon:%d:%d:0:0|t %d|TInterface\\MoneyFrame\\UI-SilverIcon:%d:%d:0:0|t %d|TInterface\\MoneyFrame\\UI-CopperIcon:%d:%d:0:0|t",
        formatAmount(gold),
        iconSize,
        iconSize,
        silver,
        iconSize,
        iconSize,
        copper,
        iconSize,
        iconSize
    )
end

local function getClassColor(classFile)
    local classColors = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
    local color = classColors and classColors[classFile or ""]
    if color then
        return color.r, color.g, color.b
    end

    return 0.95, 0.82, 0.18
end



local clampScrollFramePosition = UIScrollFrames.ClampPosition or function() end
local styleScrollFrame = UIScrollFrames.Style or function() end

local function setClassIconTexture(texture, classFile)
    if not texture then
        return
    end

    texture:SetTexture("Interface\\TargetingFrame\\UI-Classes-Circles")
    local coords = CLASS_ICON_TCOORDS and CLASS_ICON_TCOORDS[classFile or ""]
    if coords then
        texture:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
    else
        texture:SetTexCoord(0, 1, 0, 1)
    end
end

local function setFont(fontString, fontObject)
    if fontString and fontObject then
        fontString:SetFontObject(fontObject)
    end
end

local function createLabel(parent, fontObject, justify)
    local text = parent:CreateFontString(nil, "OVERLAY", fontObject)
    text:SetJustifyH(justify or "LEFT")
    text:SetJustifyV("MIDDLE")
    return text
end

local function setTextureColor(texture, color, alpha)
    if not texture or type(color) ~= "table" then
        return
    end

    texture:SetVertexColor(color[1], color[2], color[3], alpha == nil and 1 or alpha)
end

local createMiniActionButton = UIButtonWidgets.CreateMiniActionButton or function(parent)
    return CreateFrame("Button", nil, parent, BACKDROP_TEMPLATE)
end
local setMiniActionButtonActive = UIButtonWidgets.SetMiniActionButtonActive or function() end

if UITransientPanels.Install then
    UITransientPanels.Install(UI, {
        setMiniActionButtonActive = setMiniActionButtonActive,
    })
end

if UIFeaturesPanel.Install then
    UIFeaturesPanel.Install(UI, {
        THEME = THEME,
        setMiniActionButtonActive = setMiniActionButtonActive,
    })
end

local createMainTab = UIButtonWidgets.CreateMainTab or function(parent)
    return CreateFrame("Button", nil, parent, BACKDROP_TEMPLATE)
end
local createSubTab = UIButtonWidgets.CreateSubTab or createMainTab
local createVerticalSubTab = UIButtonWidgets.CreateVerticalSubTab or createSubTab

local createSectionFrame = UISectionFrames.CreateSectionFrame or function(parent)
    return CreateFrame("Frame", nil, parent, BACKDROP_TEMPLATE)
end
local setSectionFrameTextures = UISectionFrames.SetSectionFrameTextures or function() end
local createUtilityHeader = UISectionFrames.CreateUtilityHeader or createSectionFrame

local function getUtilityViewHelpers()
    return {
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        setTextureColor = setTextureColor,
        setMiniActionButtonActive = setMiniActionButtonActive,
    }
end

local function createUpgradeButton(parent)
    if UIUtilityView.CreateUpgradeButton then
        return UIUtilityView.CreateUpgradeButton(parent, getUtilityViewHelpers())
    end

    return CreateFrame("Frame", nil, parent, BACKDROP_TEMPLATE)
end

local createProgressBar = UIProgressBars.Create or function(parent)
    return CreateFrame("StatusBar", nil, parent)
end
local setProgressBar = UIProgressBars.Set or function() end
local setProgressBreakpoints = UIProgressBars.SetBreakpoints or function() end

local function getVaultViewHelpers()
    return {
        createSectionFrame = createSectionFrame,
        createLabel = createLabel,
        createProgressBar = createProgressBar,
        setProgressBar = setProgressBar,
        setProgressBreakpoints = setProgressBreakpoints,
    }
end

local function getSidebarViewHelpers()
    return {
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        setTextureColor = setTextureColor,
        getClassColor = getClassColor,
        setClassIconTexture = setClassIconTexture,
        setMiniActionButtonActive = setMiniActionButtonActive,
    }
end

local function createVaultSlotCard(parent)
    if UIVaultView.CreateSlotCard then
        return UIVaultView.CreateSlotCard(parent, getVaultViewHelpers())
    end

    return createSectionFrame(parent, "cardInset")
end

local createWeeklyStatusRow = UIStatusRows.CreateWeeklyStatusRow or function(parent)
    return CreateFrame("Frame", nil, parent, BACKDROP_TEMPLATE)
end
local setWeeklyStatusRow = UIStatusRows.SetWeeklyStatusRow or function(row)
    if row then
        row:Hide()
    end
end
local setStatusRowWithoutBadge = UIStatusRows.SetStatusRowWithoutBadge or setWeeklyStatusRow
local setHousingSidebarStatusRow = UIStatusRows.SetHousingSidebarStatusRow or setStatusRowWithoutBadge
local getProfessionStatusBadgeTexture = UIStatusRows.GetProfessionStatusBadgeTexture or function()
    return nil
end

local function getCustomTaskCardHelpers()
    return {
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        createSectionFrame = createSectionFrame,
        createWeeklyStatusRow = createWeeklyStatusRow,
        setMiniActionButtonActive = setMiniActionButtonActive,
        setTextureColor = setTextureColor,
        setWeeklyStatusRow = setWeeklyStatusRow,
        styleScrollFrame = styleScrollFrame,
    }
end

local function createCustomTaskCard(parent)
    if UICustomTaskView.CreateCard then
        return UICustomTaskView.CreateCard(parent, getCustomTaskCardHelpers())
    end

    local card = createSectionFrame(parent, "card")
    card:Hide()
    return card
end

local function getRenownCardHelpers()
    return {
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        createSectionFrame = createSectionFrame,
        createProgressBar = createProgressBar,
        getClassColor = getClassColor,
        setClassIconTexture = setClassIconTexture,
        setProgressBar = setProgressBar,
        setMiniActionButtonActive = setMiniActionButtonActive,
        setTextureColor = setTextureColor,
        styleScrollFrame = styleScrollFrame,
    }
end

local function createRenownCard(parent)
    if UIRenownView.CreateCard then
        return UIRenownView.CreateCard(parent, getRenownCardHelpers())
    end

    local card = createSectionFrame(parent, "card")
    card:Hide()
    return card
end

local function getCompendiumCardHelpers()
    return {
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        createSectionFrame = createSectionFrame,
        setMiniActionButtonActive = setMiniActionButtonActive,
        setTextureColor = setTextureColor,
        styleScrollFrame = styleScrollFrame,
    }
end

local function createCompendiumCard(parent)
    if UICompendiumView.CreateCard then
        return UICompendiumView.CreateCard(parent, getCompendiumCardHelpers())
    end

    local card = createSectionFrame(parent, "card")
    card:Hide()
    return card
end

local professionFrameOriginalStrata = nil
local professionFrameRestoreHooked = false

local function restoreProfessionFrameStacking(frame)
    if professionFrameOriginalStrata and type(frame) == "table" and type(frame.SetFrameStrata) == "function" then
        pcall(frame.SetFrameStrata, frame, professionFrameOriginalStrata)
    end

    professionFrameOriginalStrata = nil
end

local function bringProfessionFrameToFront()
    local frame = type(ProfessionsFrame) == "table" and ProfessionsFrame or nil
    if not frame or type(frame.IsShown) ~= "function" or not frame:IsShown() then
        return false
    end

    if not professionFrameRestoreHooked and type(frame.HookScript) == "function" then
        local hooked = pcall(frame.HookScript, frame, "OnHide", restoreProfessionFrameStacking)
        professionFrameRestoreHooked = hooked == true
    end

    if type(frame.GetFrameStrata) == "function" and type(frame.SetFrameStrata) == "function" then
        if not professionFrameOriginalStrata then
            local ok, strata = pcall(frame.GetFrameStrata, frame)
            if ok and type(strata) == "string" and strata ~= "" and strata ~= BLIZZARD_PANEL_STRATA then
                professionFrameOriginalStrata = strata
            end
        end

        pcall(frame.SetFrameStrata, frame, "DIALOG")
    end

    if type(frame.SetToplevel) == "function" then
        pcall(frame.SetToplevel, frame, true)
    end
    if type(frame.Raise) == "function" then
        pcall(frame.Raise, frame)
    end

    return true
end

local function openProfessionEntry(entry)
    if type(entry) ~= "table" then
        return
    end

    if type(ProfessionsBook_LoadUI) == "function" then
        pcall(ProfessionsBook_LoadUI)
    end

    local targetSkillLineID = tonumber(entry.skillLineID)
    local slotIndex = tonumber(entry.slotIndex)
    local fallbackName = entry.professionName or entry.tooltipTitle or entry.label
    local professionSpellOffset = nil
    local baseSkillLineID = tonumber(entry.baseSkillLineID)

    local function isTradeSkillDataSourceChanging()
        if type(C_TradeSkillUI) ~= "table" or type(C_TradeSkillUI.IsDataSourceChanging) ~= "function" then
            return false
        end

        local ok, changing = pcall(C_TradeSkillUI.IsDataSourceChanging)
        return ok and changing == true
    end

    local function professionInfoMatchesTarget(professionInfo)
        if type(professionInfo) ~= "table" then
            return false
        end

        local professionID = tonumber(professionInfo.professionID)
        local parentProfessionID = tonumber(professionInfo.parentProfessionID)
        if targetSkillLineID and (professionID == targetSkillLineID or parentProfessionID == targetSkillLineID) then
            return true
        end
        if baseSkillLineID and (professionID == baseSkillLineID or parentProfessionID == baseSkillLineID) then
            return true
        end

        return not targetSkillLineID and not baseSkillLineID and professionID and professionID > 0
    end

    local function getSpecConfigIDForProfession(professionID)
        professionID = tonumber(professionID)
        if not professionID
            or professionID <= 0
            or type(C_ProfSpecs) ~= "table"
            or type(C_ProfSpecs.GetConfigIDForSkillLine) ~= "function"
        then
            return nil
        end

        local specSkillLineID = professionID
        if type(C_ProfSpecs.SkillLineHasSpecialization) == "function"
            and type(C_ProfSpecs.GetDefaultSpecSkillLine) == "function"
        then
            local okHasSpec, hasSpec = pcall(C_ProfSpecs.SkillLineHasSpecialization, professionID)
            if okHasSpec and not hasSpec then
                local okDefaultSpec, defaultSpecSkillLineID = pcall(C_ProfSpecs.GetDefaultSpecSkillLine)
                defaultSpecSkillLineID = okDefaultSpec and tonumber(defaultSpecSkillLineID) or nil
                if defaultSpecSkillLineID and defaultSpecSkillLineID > 0 then
                    specSkillLineID = defaultSpecSkillLineID
                end
            end
        end

        local okConfig, configID = pcall(C_ProfSpecs.GetConfigIDForSkillLine, specSkillLineID)
        configID = okConfig and tonumber(configID) or nil
        if not configID or configID <= 0 then
            return nil
        end

        return configID
    end

    local function canSelectSpecializationsTab(frame)
        if isTradeSkillDataSourceChanging() then
            return false
        end

        local frameProfessionInfo = type(frame.professionInfo) == "table" and frame.professionInfo or nil
        if not professionInfoMatchesTarget(frameProfessionInfo) then
            return false
        end

        local specPage = type(frame.SpecPage) == "table" and frame.SpecPage or nil
        local specProfessionInfo = specPage and type(specPage.professionInfo) == "table" and specPage.professionInfo or nil
        if not professionInfoMatchesTarget(specProfessionInfo) then
            return false
        end

        if type(Professions) == "table" and type(Professions.InLocalCraftingMode) == "function" then
            local okLocalCrafting, isLocalCrafting = pcall(Professions.InLocalCraftingMode)
            if not okLocalCrafting or not isLocalCrafting then
                return false
            end
        end

        if type(C_ProfSpecs) == "table" and type(C_ProfSpecs.ShouldShowSpecTab) == "function" then
            local okShouldShow, shouldShowSpecTab = pcall(C_ProfSpecs.ShouldShowSpecTab)
            if not okShouldShow or not shouldShowSpecTab then
                return false
            end
        end

        if type(C_ProfSpecs) == "table" and type(C_ProfSpecs.GetSpecTabInfo) == "function" then
            local okSpecTab, specTabInfo = pcall(C_ProfSpecs.GetSpecTabInfo)
            if not okSpecTab or type(specTabInfo) ~= "table" or specTabInfo.enabled ~= true then
                return false
            end
        end

        return getSpecConfigIDForProfession(specProfessionInfo.professionID) ~= nil
    end

    local function selectSpecializationsTab()
        if type(ProfessionsFrame) ~= "table"
            or type(ProfessionsFrame.IsShown) ~= "function"
            or not ProfessionsFrame:IsShown()
            or not ProfessionsFrame.specializationsTabID
            or type(ProfessionsFrame.SetTab) ~= "function"
            or not canSelectSpecializationsTab(ProfessionsFrame)
        then
            return false
        end

        ProfessionsFrame:SetTab(ProfessionsFrame.specializationsTabID, true)
        return true
    end

    local function queueSpecializationsTab()
        local function trySelectSpecializationsTab()
            bringProfessionFrameToFront()
            selectSpecializationsTab()
        end

        trySelectSpecializationsTab()
        if C_Timer and type(C_Timer.After) == "function" then
            C_Timer.After(0, trySelectSpecializationsTab)
            C_Timer.After(0.05, trySelectSpecializationsTab)
            C_Timer.After(0.15, trySelectSpecializationsTab)
            C_Timer.After(0.35, trySelectSpecializationsTab)
            C_Timer.After(0.75, trySelectSpecializationsTab)
            C_Timer.After(1.25, trySelectSpecializationsTab)
        end
    end

    if type(GetProfessions) == "function" and type(GetProfessionInfo) == "function" then
        local professionIndexes = { GetProfessions() }
        for index = 1, 6 do
            local professionIndex = professionIndexes[index]
            if professionIndex then
                local infoName, _, _, _, _, spellOffset, skillLineID = GetProfessionInfo(professionIndex)
                local resolvedSkillLineID = tonumber(skillLineID)
                local matchesSlot = slotIndex and slotIndex == index
                local matchesSkill = targetSkillLineID and resolvedSkillLineID == targetSkillLineID
                local matchesName = type(fallbackName) == "string" and fallbackName ~= "" and infoName == fallbackName
                if matchesSlot or matchesSkill or matchesName then
                    fallbackName = infoName or fallbackName
                    professionSpellOffset = tonumber(spellOffset)
                    baseSkillLineID = baseSkillLineID or resolvedSkillLineID
                    break
                end
            end
        end
    end

    local openSkillLineID = targetSkillLineID or baseSkillLineID

    local frameShown = (type(ProfessionsFrame) == "table" and ProfessionsFrame:IsShown())
        or (type(ProfessionsBookFrame) == "table" and ProfessionsBookFrame:IsShown())

    if type(C_TradeSkillUI) == "table"
        and type(C_TradeSkillUI.OpenTradeSkill) == "function"
        and openSkillLineID
    then
        pcall(C_TradeSkillUI.OpenTradeSkill, openSkillLineID)
        queueSpecializationsTab()
    end

    frameShown = frameShown
        or (type(ProfessionsFrame) == "table" and ProfessionsFrame:IsShown())
        or (type(ProfessionsBookFrame) == "table" and ProfessionsBookFrame:IsShown())

    if not frameShown
        and type(C_SpellBook) == "table"
        and type(C_SpellBook.CastSpellBookItem) == "function"
        and professionSpellOffset
    then
        local spellBank = Enum and Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Player or 0
        pcall(C_SpellBook.CastSpellBookItem, professionSpellOffset + 1, spellBank)
        queueSpecializationsTab()
    end

    frameShown = frameShown
        or (type(ProfessionsFrame) == "table" and ProfessionsFrame:IsShown())
        or (type(ProfessionsBookFrame) == "table" and ProfessionsBookFrame:IsShown())

    if not frameShown
        and type(CastSpellByName) == "function"
        and type(fallbackName) == "string"
        and fallbackName ~= ""
    then
        pcall(CastSpellByName, fallbackName)
        queueSpecializationsTab()
    end
end

local PROFESSION_SKILL_LINE_TO_KEY = (ns.Data and ns.Data.PROFESSION_SKILL_LINE_TO_KEY) or {}
local SECONDARY_PROFESSION_SKILL_LINE_TO_KEY = {
    [185] = "cooking",
    [356] = "fishing",
    [794] = "archaeology",
    [129] = "firstaid",
}
local PROFESSION_BADGE_COLORS = {
    alchemy = { 0.24, 0.78, 0.38 },
    blacksmithing = { 0.54, 0.58, 0.64 },
    enchanting = { 0.62, 0.36, 0.92 },
    engineering = { 0.93, 0.58, 0.22 },
    herbalism = { 0.28, 0.72, 0.32 },
    inscription = { 0.42, 0.58, 0.92 },
    jewelcrafting = { 0.26, 0.78, 0.88 },
    leatherworking = { 0.72, 0.48, 0.28 },
    mining = { 0.58, 0.64, 0.70 },
    skinning = { 0.84, 0.58, 0.28 },
    tailoring = { 0.36, 0.54, 0.88 },
    cooking = { 0.92, 0.50, 0.26 },
    fishing = { 0.24, 0.68, 0.88 },
    archaeology = { 0.76, 0.62, 0.36 },
    firstaid = { 0.86, 0.34, 0.34 },
}
local PROFESSION_FALLBACK_ICONS = {
    alchemy = "Interface\\Icons\\Trade_Alchemy",
    blacksmithing = "Interface\\Icons\\Trade_BlackSmithing",
    enchanting = "Interface\\Icons\\Trade_Engraving",
    engineering = "Interface\\Icons\\Trade_Engineering",
    herbalism = "Interface\\Icons\\Trade_Herbalism",
    inscription = "Interface\\Icons\\INV_Inscription_Tradeskill01",
    jewelcrafting = "Interface\\Icons\\INV_Misc_Gem_01",
    leatherworking = "Interface\\Icons\\Trade_LeatherWorking",
    mining = "Interface\\Icons\\Trade_Mining",
    skinning = "Interface\\Icons\\INV_Misc_Pelt_Wolf_01",
    tailoring = "Interface\\Icons\\Trade_Tailoring",
    cooking = "Interface\\Icons\\INV_Misc_Food_15",
    fishing = "Interface\\Icons\\Trade_Fishing",
    archaeology = "Interface\\Icons\\Trade_Archaeology",
    firstaid = "Interface\\Icons\\Spell_Holy_SealOfSacrifice",
}
local PROFESSION_BADGE_NAME_PATTERNS = {
    alchemy = { "alchemy", "alchemie" },
    blacksmithing = { "blacksmith", "schmied" },
    enchanting = { "enchant", "verzauber" },
    engineering = { "engineer", "ingenieur" },
    herbalism = { "herbal", "kraut" },
    inscription = { "inscription", "inschrift" },
    jewelcrafting = { "jewel", "juwel" },
    leatherworking = { "leather", "leder" },
    mining = { "mining", "bergbau" },
    skinning = { "skinning", "kurschn" },
    tailoring = { "tailor", "schneider", "schneid" },
    cooking = { "cooking", "cook", "koch" },
    fishing = { "fishing", "fish", "angel" },
    archaeology = { "archaeology", "arch", "archa" },
    firstaid = { "firstaid", "first", "erstehilfe" },
}

local function normalizeProfessionText(text)
    local value = string.lower(tostring(text or ""))
    value = value:gsub("\195\132", "a")
    value = value:gsub("\195\164", "a")
    value = value:gsub("\195\150", "o")
    value = value:gsub("\195\182", "o")
    value = value:gsub("\195\156", "u")
    value = value:gsub("\195\188", "u")
    value = value:gsub("\195\159", "ss")
    value = value:gsub("[^%w]+", "")
    return value
end

local function getProfessionBadgeKey(profession)
    if type(profession) ~= "table" then
        return nil
    end

    local skillLineID = tonumber(profession.skillLineID)
    local key = skillLineID and (PROFESSION_SKILL_LINE_TO_KEY[skillLineID] or SECONDARY_PROFESSION_SKILL_LINE_TO_KEY[skillLineID]) or nil
    if key then
        return key
    end

    local normalizedName = normalizeProfessionText(profession.name or profession.professionName or profession.label)
    for professionKey, patterns in pairs(PROFESSION_BADGE_NAME_PATTERNS) do
        for _, pattern in ipairs(patterns) do
            if normalizedName:find(normalizeProfessionText(pattern), 1, true) then
                return professionKey
            end
        end
    end

    return nil
end

local function getHeroProfessionGroups(character)
    local professions = type(character) == "table" and character.professions or nil
    if type(professions) ~= "table" and type(character) == "table" and type(character.key) == "string" and ns.GetStoredCharacterRecord then
        local stored = ns:GetStoredCharacterRecord(character.key, false)
        professions = type(stored) == "table" and stored.professions or nil
    end
    if (type(professions) ~= "table" or #professions == 0)
        and type(character) == "table"
        and type(character.key) == "string"
        and ns.IsCurrentCharacterKey
        and ns:IsCurrentCharacterKey(character.key)
        and ns.RefreshCurrentCharacterRecord
    then
        local refreshed = ns:RefreshCurrentCharacterRecord()
        professions = type(refreshed) == "table" and refreshed.professions or professions
    end

    local primary = {}
    local secondary = {}
    for _, profession in ipairs(type(professions) == "table" and professions or {}) do
        if type(profession) == "table" then
            local badgeKey = getProfessionBadgeKey(profession)
            local entry = {
                slotIndex = tonumber(profession.slotIndex),
                category = profession.category,
                skillLineID = tonumber(profession.skillLineID),
                baseSkillLineID = tonumber(profession.baseSkillLineID or profession.skillLineID),
                professionKey = badgeKey,
                professionName = profession.name or profession.professionName,
                name = profession.name or profession.professionName,
                label = profession.name or profession.professionName,
                tooltipTitle = profession.name or profession.professionName,
                icon = profession.icon or PROFESSION_FALLBACK_ICONS[badgeKey],
                spellOffset = tonumber(profession.spellOffset),
                skillLevel = tonumber(profession.skillLevel),
                maxSkillLevel = tonumber(profession.maxSkillLevel),
            }
            local isPrimary = entry.category == "primary" or (entry.category ~= "secondary" and badgeKey and PROFESSION_SKILL_LINE_TO_KEY[entry.skillLineID or 0])
            if isPrimary then
                primary[#primary + 1] = entry
            else
                secondary[#secondary + 1] = entry
            end
        end
    end

    local function sortProfessions(a, b)
        local aSlot = tonumber(a.slotIndex) or 99
        local bSlot = tonumber(b.slotIndex) or 99
        if aSlot ~= bSlot then
            return aSlot < bSlot
        end
        return (a.name or "") < (b.name or "")
    end

    table.sort(primary, sortProfessions)
    table.sort(secondary, sortProfessions)
    return primary, secondary
end

local function createHeroProfessionBadge(parent)
    local button = CreateFrame("Button", nil, parent)
    button:SetSize(24, 24)
    button:RegisterForClicks("LeftButtonUp")

    button.ring = button:CreateTexture(nil, "ARTWORK")
    button.ring:SetAllPoints()
    button.ring:SetTexture("Interface\\CHARACTERFRAME\\TempPortraitAlphaMask")

    button.inner = button:CreateTexture(nil, "ARTWORK", nil, 1)
    button.inner:SetSize(20, 20)
    button.inner:SetPoint("CENTER")
    button.inner:SetTexture("Interface\\CHARACTERFRAME\\TempPortraitAlphaMask")
    button.inner:SetVertexColor(THEME.metalDeep[1], THEME.metalDeep[2], THEME.metalDeep[3], 0.88)

    button.icon = button:CreateTexture(nil, "OVERLAY")
    button.icon:SetSize(16, 16)
    button.icon:SetPoint("CENTER")
    if button.icon.AddMaskTexture then
        button.iconMask = button:CreateMaskTexture()
        button.iconMask:SetTexture("Interface\\CHARACTERFRAME\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
        button.iconMask:SetAllPoints(button.icon)
        button.icon:AddMaskTexture(button.iconMask)
    end

    button:SetScript("OnClick", function(selfButton)
        if selfButton.vlCanOpen and selfButton.vlProfession then
            openProfessionEntry(selfButton.vlProfession)
        end
    end)
    button:SetScript("OnEnter", function(selfButton)
        local profession = selfButton.vlProfession
        if type(profession) ~= "table" then
            return
        end

        local color = selfButton.vlColor or THEME.gold
        GameTooltip:SetOwner(selfButton, "ANCHOR_CURSOR_RIGHT")
        GameTooltip:AddLine(profession.name or profession.professionName or (L.UNKNOWN or "Unknown"), color[1], color[2], color[3], true)
        if profession.category == "secondary" then
            GameTooltip:AddLine(L.HERO_PROFESSION_SECONDARY_LABEL or "Secondary profession", 0.72, 0.78, 0.88, true)
        else
            GameTooltip:AddLine(L.HERO_PROFESSION_PRIMARY_LABEL or "Main profession", 0.72, 0.78, 0.88, true)
        end
        if profession.skillLevel and profession.maxSkillLevel and profession.maxSkillLevel > 0 then
            GameTooltip:AddLine(string.format("%d/%d", profession.skillLevel, profession.maxSkillLevel), 0.86, 0.86, 0.82, true)
        end
        if selfButton.vlCanOpen then
            GameTooltip:AddLine(L.HERO_PROFESSION_OPEN_HINT or "Left click: open this profession.", 0.72, 0.78, 0.88, true)
        else
            GameTooltip:AddLine(L.HERO_PROFESSION_ALT_HINT or "Log into this character to open the profession window.", 0.62, 0.66, 0.72, true)
        end
        GameTooltip:Show()
    end)
    button:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    return button
end

local function ensureHeroProfessionBadge(buttons, parent, index)
    buttons[index] = buttons[index] or createHeroProfessionBadge(parent)
    return buttons[index]
end

local function applyHeroProfessionBadge(button, profession, canOpen)
    local badgeKey = profession.professionKey or getProfessionBadgeKey(profession)
    local color = THEME.gold
    button.vlProfession = profession
    button.vlCanOpen = canOpen == true
    button.vlColor = color
    button.ring:SetVertexColor(color[1], color[2], color[3], canOpen and 0.96 or 0.62)
    button.icon:SetTexture(profession.icon or PROFESSION_FALLBACK_ICONS[badgeKey] or getProfessionStatusBadgeTexture(profession))
    button:SetAlpha(canOpen and 1 or 0.72)
    button:Show()
end

local function refreshHeroProfessionBadges(frame, character)
    if not frame or not frame.heroProfessionBadgeFrame then
        return false
    end

    local primary, secondary = getHeroProfessionGroups(character)
    local hasBadges = #primary > 0 or #secondary > 0
    frame.heroProfessionBadgeFrame:SetShown(hasBadges)

    local canOpen = type(character) == "table"
        and type(character.key) == "string"
        and ns.IsCurrentCharacterKey
        and ns:IsCurrentCharacterKey(character.key)
        or false
    local badgeGap = 5
    local badgeSize = 24
    local badges = {}
    for _, profession in ipairs(primary) do
        badges[#badges + 1] = profession
    end
    for _, profession in ipairs(secondary) do
        badges[#badges + 1] = profession
    end

    local badgeWidth = #badges > 0 and ((#badges * badgeSize) + ((#badges - 1) * badgeGap)) or badgeSize
    frame.heroProfessionBadgeFrame:SetSize(math.max(badgeSize, badgeWidth), badgeSize)

    frame.heroProfessionPrimaryBadges = frame.heroProfessionPrimaryBadges or {}
    frame.heroProfessionSecondaryBadges = frame.heroProfessionSecondaryBadges or {}

    for index = 1, math.max(#badges, #frame.heroProfessionPrimaryBadges) do
        local button = ensureHeroProfessionBadge(frame.heroProfessionPrimaryBadges, frame.heroProfessionBadgeFrame, index)
        local profession = badges[index]
        if profession then
            button:ClearAllPoints()
            button:SetPoint("TOPLEFT", frame.heroProfessionBadgeFrame, "TOPLEFT", (index - 1) * (badgeSize + badgeGap), 0)
            applyHeroProfessionBadge(button, profession, canOpen)
        else
            button:Hide()
            button.vlProfession = nil
        end
    end

    for index = 1, math.max(#secondary, #frame.heroProfessionSecondaryBadges) do
        local button = ensureHeroProfessionBadge(frame.heroProfessionSecondaryBadges, frame.heroProfessionBadgeFrame, index)
        button:Hide()
        button.vlProfession = nil
    end

    return hasBadges
end

local UIPveListCard = ns.UIPveListCard or {}

local function getPveListCardHelpers()
    return {
        buildPveWeeklyListView = buildPveWeeklyListView,
        buildPvpWeeklyListView = buildPvpWeeklyListView,
        buildProfessionWeeklyListView = buildProfessionWeeklyListView,
        buildPveDelvesListView = buildPveDelvesListView,
        buildPveEventsListView = buildPveEventsListView,
        buildPveDarkmoonListView = buildPveDarkmoonListView,
        buildPveWorldListView = buildPveWorldListView,
        buildPveDailyListView = buildPveDailyListView,
        buildPveOtherListView = buildPveOtherListView,
        createLabel = createLabel,
        createSectionFrame = createSectionFrame,
        createWeeklyStatusRow = createWeeklyStatusRow,
        getProfessionStatusBadgeTexture = getProfessionStatusBadgeTexture,
        openProfessionEntry = openProfessionEntry,
        clampScrollFramePosition = clampScrollFramePosition,
        setWeeklyStatusRow = setWeeklyStatusRow,
        styleScrollFrame = styleScrollFrame,
    }
end

local function createPveWeeklyCard(parent)
    if UIPveListCard.CreateCard then
        return UIPveListCard.CreateCard(parent, getPveListCardHelpers())
    end

    local card = createSectionFrame(parent, "card")
    card:Hide()
    return card
end

local UIProfessionCooldownView = ns.UIProfessionCooldownView or {}

local function getProfessionCooldownCardHelpers()
    return {
        THEME = THEME,
        STATUS_COLORS = STATUS_COLORS,
        createLabel = createLabel,
        createProgressBar = createProgressBar,
        createSectionFrame = createSectionFrame,
        createWeeklyStatusRow = createWeeklyStatusRow,
        formatLastSeen = formatLastSeen,
        setProgressBar = setProgressBar,
        setWeeklyStatusRow = setWeeklyStatusRow,
    }
end

local function createProfessionCooldownCard(parent)
    if UIProfessionCooldownView.CreateCard then
        return UIProfessionCooldownView.CreateCard(parent, getProfessionCooldownCardHelpers())
    end

    local card = createSectionFrame(parent, "card")
    card:Hide()
    return card
end

local UIPvePreyView = ns.UIPvePreyView or {}

local function getPvePreyCardHelpers()
    return {
        buildPvePreyView = buildPvePreyView,
        createLabel = createLabel,
        createProgressBar = createProgressBar,
        createSectionFrame = createSectionFrame,
        createWeeklyStatusRow = createWeeklyStatusRow,
        setProgressBar = setProgressBar,
        setProgressBreakpoints = setProgressBreakpoints,
        setWeeklyStatusRow = setWeeklyStatusRow,
    }
end

local function createPvePreyCard(parent)
    if UIPvePreyView.CreateCard then
        return UIPvePreyView.CreateCard(parent, getPvePreyCardHelpers())
    end

    local card = createSectionFrame(parent, "card")
    card:Hide()
    return card
end

local UIPveRaresView = ns.UIPveRaresView or {}

local function getPveRaresCardHelpers()
    return {
        buildPveRaresView = buildPveRaresView,
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        createSectionFrame = createSectionFrame,
        setMiniActionButtonActive = setMiniActionButtonActive,
        setTextureColor = setTextureColor,
        styleScrollFrame = styleScrollFrame,
    }
end

local function createPveRaresCard(parent)
    if UIPveRaresView.CreateCard then
        return UIPveRaresView.CreateCard(parent, getPveRaresCardHelpers())
    end

    local card = createSectionFrame(parent, "card")
    card:Hide()
    return card
end

if UIPveCardRefresh.Install then
    UIPveCardRefresh.Install(UI, {
        UIPveListCard = UIPveListCard,
        UIProfessionCooldownView = UIProfessionCooldownView,
        UIPvePreyView = UIPvePreyView,
        UIPveRaresView = UIPveRaresView,
        getPveListCardHelpers = getPveListCardHelpers,
        getProfessionCooldownCardHelpers = getProfessionCooldownCardHelpers,
        getPvePreyCardHelpers = getPvePreyCardHelpers,
        getPveRaresCardHelpers = getPveRaresCardHelpers,
    })
end

local function getMythicPlusCardHelpers()
    return {
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        createSectionFrame = createSectionFrame,
        createUtilityHeader = createUtilityHeader,
        createWeeklyStatusRow = createWeeklyStatusRow,
        getClassColor = getClassColor,
        setClassIconTexture = setClassIconTexture,
        setMiniActionButtonActive = setMiniActionButtonActive,
        setStatusRowWithoutBadge = setStatusRowWithoutBadge,
        setTextureColor = setTextureColor,
        styleScrollFrame = styleScrollFrame,
    }
end

local function createMythicPlusStatCard(parent)
    if UIMythicPlusCard.CreateStatCard then
        return UIMythicPlusCard.CreateStatCard(parent, getMythicPlusCardHelpers())
    end

    return createSectionFrame(parent, "cardInset")
end

local function applyMythicPlusStatCard(card, entry)
    if UIMythicPlusCard.ApplyStatCard then
        UIMythicPlusCard.ApplyStatCard(card, entry)
    end
end

local function getHousingCardHelpers()
    return {
        applyStatCard = applyMythicPlusStatCard,
        clampScrollFramePosition = clampScrollFramePosition,
        createLabel = createLabel,
        createProgressBar = createProgressBar,
        createSectionFrame = createSectionFrame,
        createStatCard = createMythicPlusStatCard,
        createUtilityHeader = createUtilityHeader,
        createWeeklyStatusRow = createWeeklyStatusRow,
        setHousingSidebarStatusRow = setHousingSidebarStatusRow,
        setProgressBar = setProgressBar,
        setProgressBreakpoints = setProgressBreakpoints,
        setStatusRowWithoutBadge = setStatusRowWithoutBadge,
        styleScrollFrame = styleScrollFrame,
    }
end

function UI:LayoutVaultSlotCards()
    if UIVaultView.LayoutSlotCards then
        UIVaultView.LayoutSlotCards(self)
    end
end

function UI:LayoutMythicPlusCard()
    if UIMythicPlusCard.Layout then
        UIMythicPlusCard.Layout(self)
    end
end

function UI:EnsureMythicPlusDungeonRows(count)
    if UIMythicPlusCard.EnsureDungeonRows then
        UIMythicPlusCard.EnsureDungeonRows(self, count, getMythicPlusCardHelpers())
    end
end

function UI:EnsureMythicPlusRecentRows(count)
    if UIMythicPlusCard.EnsureRecentRows then
        UIMythicPlusCard.EnsureRecentRows(self, count, getMythicPlusCardHelpers())
    end
end

function UI:EnsureMythicPlusGoalRows(count)
    if UIMythicPlusCard.EnsureGoalRows then
        UIMythicPlusCard.EnsureGoalRows(self, count, getMythicPlusCardHelpers())
    end
end

function UI:EnsureHousingTrackedRows(count)
    if UIHousingCard.EnsureTrackedRows then
        UIHousingCard.EnsureTrackedRows(self, count, getHousingCardHelpers())
    end
end

function UI:EnsureHousingTaskRows(count)
    if UIHousingCard.EnsureTaskRows then
        UIHousingCard.EnsureTaskRows(self, count, getHousingCardHelpers())
    end
end

function UI:EnsureHousingMilestoneRows(count)
    if UIHousingCard.EnsureMilestoneRows then
        UIHousingCard.EnsureMilestoneRows(self, count, getHousingCardHelpers())
    end
end

function UI:EnsureHousingActivityRows(count)
    if UIHousingCard.EnsureActivityRows then
        UIHousingCard.EnsureActivityRows(self, count, getHousingCardHelpers())
    end
end





local function getWishlistCardHelpers()
    return {
        applyRaidLootRow = applyRaidLootRow,
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        createRaidJournalLootRow = createRaidJournalLootRow,
        createSectionFrame = createSectionFrame,
        createSubTab = createSubTab,
        setMiniActionButtonActive = setMiniActionButtonActive,
        styleScrollFrame = styleScrollFrame,
    }
end

local function createWishlistCard(parent)
    if UIWishlistCard.CreateCard then
        return UIWishlistCard.CreateCard(parent, getWishlistCardHelpers())
    end

    local card = createSectionFrame(parent, "card")
    card:Hide()
    return card
end

local function getRaidModeCardHelpers()
    return {
        applyRaidLootRow = applyRaidLootRow,
        createLabel = createLabel,
        createMiniActionButton = createMiniActionButton,
        createRaidJournalLootRow = createRaidJournalLootRow,
        createRaidJournalRowButton = createRaidJournalRowButton,
        createSectionFrame = createSectionFrame,
        createUtilityHeader = createUtilityHeader,
        getJournalDifficultyOptions = getJournalDifficultyOptions,
        getRaidDifficultyLabel = getRaidDifficultyLabel,
        setMiniActionButtonActive = setMiniActionButtonActive,
        styleScrollFrame = styleScrollFrame,
        usesDungeonDailyCounter = usesDungeonDailyCounter,
    }
end

local function createRaidModeSidebar(parent)
    if UIRaidModeCard.CreateModeSidebar then
        return UIRaidModeCard.CreateModeSidebar(parent, getRaidModeCardHelpers())
    end

    local panel = CreateFrame("Frame", nil, parent)
    panel:SetAllPoints(parent)
    panel:Hide()
    return panel
end

local function createRaidModeCenter(parent)
    if UIRaidModeCard.CreateModeCenter then
        return UIRaidModeCard.CreateModeCenter(parent, getRaidModeCardHelpers())
    end

    local card = CreateFrame("Frame", nil, parent)
    card:Hide()
    return card
end

local function createRaidModeUtility(parent)
    if UIRaidModeCard.CreateModeUtility then
        return UIRaidModeCard.CreateModeUtility(parent, getRaidModeCardHelpers())
    end

    local panel = CreateFrame("Frame", nil, parent)
    panel:SetAllPoints(parent)
    panel:Hide()
    return panel
end

local function createMythicPlusModeSidebar(parent)
    if UIMythicPlusCard.CreateModeSidebar then
        return UIMythicPlusCard.CreateModeSidebar(parent, getMythicPlusCardHelpers())
    end

    local panel = CreateFrame("Frame", nil, parent)
    panel:SetAllPoints(parent)
    panel:Hide()
    return panel
end

local function createMythicPlusModeCenter(parent)
    if UIMythicPlusCard.CreateModeCenter then
        return UIMythicPlusCard.CreateModeCenter(parent, getMythicPlusCardHelpers())
    end

    local card = CreateFrame("Frame", nil, parent)
    card:Hide()
    return card
end

local function createMythicPlusModeUtility(parent)
    if UIMythicPlusCard.CreateModeUtility then
        return UIMythicPlusCard.CreateModeUtility(parent, getMythicPlusCardHelpers())
    end

    local panel = CreateFrame("Frame", nil, parent)
    panel:SetAllPoints(parent)
    panel:Hide()
    return panel
end

local function createHousingModeSidebar(parent)
    if UIHousingCard.CreateModeSidebar then
        return UIHousingCard.CreateModeSidebar(parent, getHousingCardHelpers())
    end

    local panel = CreateFrame("Frame", nil, parent)
    panel:SetAllPoints(parent)
    panel:Hide()
    return panel
end

local function createHousingModeCenter(parent)
    if UIHousingCard.CreateModeCenter then
        return UIHousingCard.CreateModeCenter(parent, getHousingCardHelpers())
    end

    local card = CreateFrame("Frame", nil, parent)
    card:Hide()
    return card
end

local function createHousingModeUtility(parent)
    if UIHousingCard.CreateModeUtility then
        return UIHousingCard.CreateModeUtility(parent, getHousingCardHelpers())
    end

    local panel = CreateFrame("Frame", nil, parent)
    panel:SetAllPoints(parent)
    panel:Hide()
    return panel
end

function UI:LayoutHousingCard()
    if UIHousingCard.Layout then
        UIHousingCard.Layout(self)
    end
end

if UINavigationTabs.Install then
    UINavigationTabs.Install(UI, {
        MAIN_TABS = MAIN_TABS,
        SUB_TABS = SUB_TABS,
    })
end

function UI:ApplyWindowPosition()
    if not self.frame then
        return
    end

    local position = ns.GetWindowPosition and ns:GetWindowPosition() or { point = "CENTER", relativePoint = "CENTER", x = 0, y = 0 }
    self.frame:ClearAllPoints()
    self.frame:SetPoint(position.point or "CENTER", UIParent, position.relativePoint or "CENTER", tonumber(position.x) or 0, tonumber(position.y) or 0)
end

function UI:StoreWindowPosition()
    if self.frame and ns.StoreWindowPosition then
        ns:StoreWindowPosition(self.frame)
    end
end

function UI:CreateFrame()
    self.sidebarSortMode = ns.GetSidebarSortMode and ns:GetSidebarSortMode() or nil

    local frame = CreateFrame("Frame", "VaultloomFrame", UIParent, BACKDROP_TEMPLATE)
    frame:SetSize(UI_LAYOUT.frameDefaultWidth, UI_LAYOUT.frameDefaultHeight)
    frame:SetMovable(true)
    frame:SetClampedToScreen(false)
    frame:SetFrameStrata(MAIN_FRAME_STRATA)
    frame:SetToplevel(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", function(selfFrame)
        if selfFrame.SetClampedToScreen then
            selfFrame:SetClampedToScreen(false)
        end
        selfFrame:StartMoving()
    end)
    frame:SetScript("OnDragStop", function(selfFrame)
        selfFrame:StopMovingOrSizing()
        UI:StoreWindowPosition()
    end)
    frame:SetScript("OnShow", function()
        if ns and ns.SetLiveRefreshEventsEnabled then
            ns:SetLiveRefreshEventsEnabled(true)
        end
        installBlizzardPanelStacking()
        scanBlizzardPanelStackingFrames()
        scheduleBlizzardPanelStackingScan(0.1)
    end)
    frame:SetScript("OnHide", function()
        restoreAllBlizzardPanelStacking()
        if ns and ns.SetLiveRefreshEventsEnabled then
            ns:SetLiveRefreshEventsEnabled(false)
        end
        if self.frame == frame then
            if self.CloseTransientPanels then
                self:CloseTransientPanels()
            end
            if frame.sidebarSortMenu then
                frame.sidebarSortMenu:Hide()
            end
            if frame.sidebarVisibilityMenu then
                frame.sidebarVisibilityMenu:Hide()
            end
            if GameTooltip then
                GameTooltip:Hide()
            end
            if self.RequestClosedGarbageCollection then
                self:RequestClosedGarbageCollection()
            end
        end
    end)
    frame:Hide()

    if frame.SetBackdrop then
        frame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
            tile = false,
            edgeSize = 28,
            insets = { left = 9, right = 9, top = 9, bottom = 9 },
        })
        frame:SetBackdropColor(THEME.metalDeep[1], THEME.metalDeep[2], THEME.metalDeep[3], 0)
        frame:SetBackdropBorderColor(0.62, 0.48, 0.18, 0.94)
    end

    if UISpecialFrames then
        local alreadyRegistered = false
        for _, specialFrameName in ipairs(UISpecialFrames) do
            if specialFrameName == frame:GetName() then
                alreadyRegistered = true
                break
            end
        end
        if not alreadyRegistered then
            table.insert(UISpecialFrames, frame:GetName())
        end
    end

    frame.bg = frame:CreateTexture(nil, "BACKGROUND")
    frame.bg:SetPoint("TOPLEFT", 10, -8)
    frame.bg:SetPoint("BOTTOMRIGHT", -10, 8)
    frame.bg:SetTexture(PREMIUM.windowBg)
    setTextureColor(frame.bg, { 1, 1, 1 }, 1)

    frame.overlay = frame:CreateTexture(nil, "BACKGROUND", nil, 1)
    frame.overlay:SetPoint("TOPLEFT", frame.bg, "TOPLEFT", 0, 0)
    frame.overlay:SetPoint("BOTTOMRIGHT", frame.bg, "BOTTOMRIGHT", 0, 0)
    frame.overlay:SetTexture(PREMIUM.windowOverlay)
    setTextureColor(frame.overlay, { 1, 1, 1 }, 0.08)

    frame.tint = frame:CreateTexture(nil, "BACKGROUND", nil, 2)
    frame.tint:SetPoint("TOPLEFT", frame.bg, "TOPLEFT", 0, 0)
    frame.tint:SetPoint("BOTTOMRIGHT", frame.bg, "BOTTOMRIGHT", 0, 0)
    frame.tint:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.tint:SetColorTexture(THEME.charcoal[1], THEME.charcoal[2], THEME.charcoal[3], 0.006)

    frame.topShade = frame:CreateTexture(nil, "ARTWORK")
    frame.topShade:SetPoint("TOPLEFT", 1, -1)
    frame.topShade:SetPoint("TOPRIGHT", -1, -1)
    frame.topShade:SetHeight(40)
    frame.topShade:SetTexture(PREMIUM.windowBg)
    setTextureColor(frame.topShade, { 1, 1, 1 }, 0)

    frame.topLine = frame:CreateTexture(nil, "ARTWORK", nil, 1)
    frame.topLine:SetPoint("TOPLEFT", 16, -46)
    frame.topLine:SetPoint("TOPRIGHT", -16, -46)
    frame.topLine:SetHeight(1)
    frame.topLine:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.topLine:SetAlpha(0)

    frame.titleBar = CreateFrame("Frame", nil, frame, BACKDROP_TEMPLATE)
    frame.titleBar:SetPoint("TOPLEFT", 16, -12)
    frame.titleBar:SetPoint("TOPRIGHT", -16, -12)
    frame.titleBar:SetHeight(28)
    frame.titleBar:EnableMouse(true)
    frame.titleBar:RegisterForDrag("LeftButton")
    frame.titleBar:SetScript("OnDragStart", function()
        if frame.SetClampedToScreen then
            frame:SetClampedToScreen(false)
        end
        frame:StartMoving()
    end)
    frame.titleBar:SetScript("OnDragStop", function()
        frame:StopMovingOrSizing()
        UI:StoreWindowPosition()
    end)

    if frame.titleBar.SetBackdrop then
        frame.titleBar:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            tile = false,
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 },
        })
        frame.titleBar:SetBackdropColor(THEME.metalInset[1], THEME.metalInset[2], THEME.metalInset[3], 0)
        frame.titleBar:SetBackdropBorderColor(THEME.goldDim[1], THEME.goldDim[2], THEME.goldDim[3], 0)
    end

    frame.titleBar.bg = frame.titleBar:CreateTexture(nil, "BACKGROUND")
    frame.titleBar.bg:SetAllPoints()
    frame.titleBar.bg:SetTexture(PREMIUM.windowBg)
    setTextureColor(frame.titleBar.bg, { 1, 1, 1 }, 0)

    frame.titleBar.overlay = frame.titleBar:CreateTexture(nil, "BACKGROUND", nil, 1)
    frame.titleBar.overlay:SetAllPoints()
    frame.titleBar.overlay:SetTexture(PREMIUM.windowOverlay)
    setTextureColor(frame.titleBar.overlay, { 1, 1, 1 }, 0)

    frame.titleBar.accent = frame.titleBar:CreateTexture(nil, "ARTWORK")
    frame.titleBar.accent:SetPoint("LEFT", 68, 0)
    frame.titleBar.accent:SetSize(18, 2)
    frame.titleBar.accent:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.titleBar.accent:SetAlpha(0)

    frame.brandSeal = CreateFrame("Frame", nil, frame)
    frame.brandSeal:SetSize(48, 48)
    frame.brandSeal:SetPoint("TOPLEFT", frame, "TOPLEFT", 18, -18)
    frame.brandSeal:SetFrameLevel(frame:GetFrameLevel() + 8)

    frame.brandSeal.icon = frame.brandSeal:CreateTexture(nil, "OVERLAY")
    frame.brandSeal.icon:SetAllPoints()
    frame.brandSeal.icon:SetTexture(PREMIUM.brandSeal)
    frame.brandSeal:Hide()

    frame.CloseButton = createMiniActionButton(frame.titleBar, "X", 26)
    frame.CloseButton:SetSize(24, 22)
    frame.CloseButton:SetPoint("RIGHT", frame.titleBar, "RIGHT", -2, 0)
    if frame.CloseButton.SetBackdropColor then
        frame.CloseButton:SetBackdropColor(THEME.metal[1], THEME.metal[2], THEME.metal[3], 0.72)
    end
    if frame.CloseButton.SetBackdropBorderColor then
        frame.CloseButton:SetBackdropBorderColor(THEME.goldDim[1], THEME.goldDim[2], THEME.goldDim[3], 0.82)
    end
    if frame.CloseButton.bg then
        frame.CloseButton.bg:SetAlpha(0.82)
    end
    if frame.CloseButton.innerShade then
        frame.CloseButton.innerShade:SetAlpha(0.18)
    end
    if frame.CloseButton.topEdge then
        frame.CloseButton.topEdge:SetAlpha(0)
    end
    frame.CloseButton.label:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3])
    frame.CloseButton:SetScript("OnClick", function()
        frame:Hide()
    end)
    frame.CloseButton:SetScript("OnEnter", function(selfButton)
        if selfButton.SetBackdropColor then
            selfButton:SetBackdropColor(THEME.metalInset[1], THEME.metalInset[2], THEME.metalInset[3], 0.92)
        end
        if selfButton.SetBackdropBorderColor then
            selfButton:SetBackdropBorderColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.96)
        end
        if selfButton.hover then
            selfButton.hover:SetAlpha(0.74)
        end
        if selfButton.topEdge then
            selfButton.topEdge:SetAlpha(0)
        end
        selfButton.label:SetTextColor(1.00, 0.95, 0.42)
        GameTooltip:SetOwner(selfButton, "ANCHOR_CURSOR_RIGHT")
        GameTooltip:AddLine(L.CLOSE or "Close", 0.92, 0.76, 0.24)
        GameTooltip:Show()
    end)
    frame.CloseButton:SetScript("OnLeave", function(selfButton)
        if selfButton.SetBackdropColor then
            selfButton:SetBackdropColor(THEME.metal[1], THEME.metal[2], THEME.metal[3], 0.72)
        end
        if selfButton.SetBackdropBorderColor then
            selfButton:SetBackdropBorderColor(THEME.goldDim[1], THEME.goldDim[2], THEME.goldDim[3], 0.82)
        end
        if selfButton.hover then
            selfButton.hover:SetAlpha(0)
        end
        if selfButton.topEdge then
            selfButton.topEdge:SetAlpha(0)
        end
        selfButton.label:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3])
        GameTooltip:Hide()
    end)

    frame.TitleText = createLabel(frame.titleBar, "GameFontHighlightLarge", "LEFT")
    frame.TitleText:SetPoint("LEFT", frame.titleBar, "LEFT", 0, 0)
    frame.TitleText:SetPoint("RIGHT", frame.titleBar, "RIGHT", -42, 0)
    frame.TitleText:SetText(L.ADDON_TITLE or "Vaultloom")
    frame.TitleText:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3])
    setFont(frame.TitleText, "GameFontHighlightLarge")

    frame.body = CreateFrame("Frame", nil, frame)
    frame.body:SetPoint("TOPLEFT", 16, -44)
    frame.body:SetPoint("BOTTOMRIGHT", -16, 16)

    frame.bodyBg = frame.body:CreateTexture(nil, "BACKGROUND")
    frame.bodyBg:SetAllPoints()
    frame.bodyBg:SetTexture(PREMIUM.windowBg)
    setTextureColor(frame.bodyBg, { 1, 1, 1 }, 0.96)

    frame.bodyOverlay = frame.body:CreateTexture(nil, "BACKGROUND", nil, 1)
    frame.bodyOverlay:SetAllPoints()
    frame.bodyOverlay:SetTexture(PREMIUM.windowOverlay)
    setTextureColor(frame.bodyOverlay, { 1, 1, 1 }, 0.06)

    frame.bodyTint = frame.body:CreateTexture(nil, "BACKGROUND", nil, 2)
    frame.bodyTint:SetAllPoints()
    frame.bodyTint:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.bodyTint:SetColorTexture(THEME.charcoal[1], THEME.charcoal[2], THEME.charcoal[3], 0.006)

    frame.mainTabBar = CreateFrame("Frame", nil, frame.body)
    frame.mainTabBar:SetPoint("TOPLEFT", 0, 0)
    frame.mainTabBar:SetPoint("TOPRIGHT", 0, 0)
    frame.mainTabBar:SetHeight(32)

    frame.mainTabBar.bg = frame.mainTabBar:CreateTexture(nil, "BACKGROUND")
    frame.mainTabBar.bg:SetAllPoints()
    frame.mainTabBar.bg:SetTexture(PREMIUM.windowBg)
    setTextureColor(frame.mainTabBar.bg, { 1, 1, 1 }, 0)

    frame.mainTabBar.overlay = frame.mainTabBar:CreateTexture(nil, "BACKGROUND", nil, 1)
    frame.mainTabBar.overlay:SetAllPoints()
    frame.mainTabBar.overlay:SetTexture(PREMIUM.windowOverlay)
    setTextureColor(frame.mainTabBar.overlay, { 1, 1, 1 }, 0)

    frame.mainTabBar.shade = frame.mainTabBar:CreateTexture(nil, "BACKGROUND", nil, 2)
    frame.mainTabBar.shade:SetAllPoints()
    frame.mainTabBar.shade:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.mainTabBar.shade:SetColorTexture(THEME.shadow[1], THEME.shadow[2], THEME.shadow[3], 0)

    frame.mainTabs = {}
    local previousTab
    for index, tabData in ipairs(MAIN_TABS) do
        local button = createMainTab(frame, index, tabData.label())
        if previousTab then
            button:SetPoint("LEFT", previousTab, "RIGHT", 8, 0)
        else
            button:SetPoint("TOPLEFT", frame.mainTabBar, "TOPLEFT", 0, 0)
        end
        button.tabIndex = index
        button.tabKey = tabData.key
        button:SetScript("OnClick", function(selfButton)
            if self.CloseTransientPanels then
                self:CloseTransientPanels()
            end
            ns:SetSelectedMainTab(selfButton.tabKey)
        end)
        frame.mainTabs[index] = button
        previousTab = button
    end

    frame.featuresButton = createMainTab(frame, #MAIN_TABS + 1, L.FEATURES_BUTTON or "Features")
    frame.settingsButton = createMainTab(frame, #MAIN_TABS + 2, L.SETTINGS_BUTTON or "Options")

    frame.wishlistButton = createMainTab(frame, #MAIN_TABS + 3, "")
    frame.wishlistButton.label:SetText("")

    frame.wishlistButton.check1 = frame.wishlistButton:CreateTexture(nil, "ARTWORK", nil, 1)
    frame.wishlistButton.check1:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
    frame.wishlistButton.check1:SetSize(7, 7)
    frame.wishlistButton.check1:SetPoint("CENTER", -6, 5)
    frame.wishlistButton.check1:SetVertexColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.92)

    frame.wishlistButton.check2 = frame.wishlistButton:CreateTexture(nil, "ARTWORK", nil, 1)
    frame.wishlistButton.check2:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
    frame.wishlistButton.check2:SetSize(7, 7)
    frame.wishlistButton.check2:SetPoint("CENTER", -6, 0)
    frame.wishlistButton.check2:SetVertexColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.92)

    frame.wishlistButton.check3 = frame.wishlistButton:CreateTexture(nil, "ARTWORK", nil, 1)
    frame.wishlistButton.check3:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
    frame.wishlistButton.check3:SetSize(7, 7)
    frame.wishlistButton.check3:SetPoint("CENTER", -6, -5)
    frame.wishlistButton.check3:SetVertexColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.92)

    frame.wishlistButton.line1 = frame.wishlistButton:CreateTexture(nil, "ARTWORK")
    frame.wishlistButton.line1:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.wishlistButton.line1:SetColorTexture(THEME.parchment[1], THEME.parchment[2], THEME.parchment[3], 0.78)
    frame.wishlistButton.line1:SetSize(8, 1)
    frame.wishlistButton.line1:SetPoint("LEFT", frame.wishlistButton.check1, "RIGHT", 4, 0)

    frame.wishlistButton.line2 = frame.wishlistButton:CreateTexture(nil, "ARTWORK")
    frame.wishlistButton.line2:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.wishlistButton.line2:SetColorTexture(THEME.parchment[1], THEME.parchment[2], THEME.parchment[3], 0.78)
    frame.wishlistButton.line2:SetSize(8, 1)
    frame.wishlistButton.line2:SetPoint("LEFT", frame.wishlistButton.check2, "RIGHT", 4, 0)

    frame.wishlistButton.line3 = frame.wishlistButton:CreateTexture(nil, "ARTWORK")
    frame.wishlistButton.line3:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.wishlistButton.line3:SetColorTexture(THEME.parchment[1], THEME.parchment[2], THEME.parchment[3], 0.78)
    frame.wishlistButton.line3:SetSize(8, 1)
    frame.wishlistButton.line3:SetPoint("LEFT", frame.wishlistButton.check3, "RIGHT", 4, 0)

    frame.wishlistButton:SetScript("OnClick", function()
        if self.CloseTransientPanels then
            self:CloseTransientPanels("wishlist")
        end
        self.wishlistPanelOpen = not self.wishlistPanelOpen
        self:Refresh()
    end)
    frame.wishlistButton:SetScript("OnEnter", function(selfButton)
        if selfButton.SetBackdropBorderColor then
            selfButton:SetBackdropBorderColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.92)
        end
        selfButton.hover:SetAlpha(1)

        local character = ns:GetSelectedCharacter() or ns:GetCurrentCharacterInfo()
        local counts = (character and character.key and ns.GetRaidLootTrackerCounts)
            and ns:GetRaidLootTrackerCounts(character.key)
            or { wish = 0, obtained = 0 }

        GameTooltip:SetOwner(selfButton, "ANCHOR_CURSOR_RIGHT")
        GameTooltip:AddLine(L.WISHLIST_TITLE or "Wishlist", THEME.gold[1], THEME.gold[2], THEME.gold[3], true)
        GameTooltip:AddLine(
            string.format(
                L.WISHLIST_BUTTON_SUMMARY or "%d wishlisted  |  %d obtained",
                tonumber(counts.wish) or 0,
                tonumber(counts.obtained) or 0
            ),
            0.86,
            0.86,
            0.82,
            true
        )
        GameTooltip:AddLine(
            self.wishlistPanelOpen
                and (L.WISHLIST_BUTTON_CLOSE or "Left click: close wishlist")
                or (L.WISHLIST_BUTTON_OPEN or "Left click: open wishlist"),
            0.72,
            0.78,
            0.88,
            true
        )
        GameTooltip:Show()
    end)
    frame.wishlistButton:SetScript("OnLeave", function(selfButton)
        if selfButton.SetBackdropBorderColor then
            if selfButton.vlActive then
                selfButton:SetBackdropBorderColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.88)
            else
                selfButton:SetBackdropBorderColor(THEME.goldDim[1], THEME.goldDim[2], THEME.goldDim[3], 0.70)
            end
        end
        selfButton.hover:SetAlpha(selfButton.vlActive and 0.55 or 0)
        GameTooltip:Hide()
    end)

    frame.TitleText:ClearAllPoints()
    frame.TitleText:SetPoint("LEFT", frame.titleBar, "LEFT", 0, 0)
    frame.TitleText:SetPoint("RIGHT", frame.CloseButton, "LEFT", -12, 0)

    if UISettingsPanel.CreatePanel then
        UISettingsPanel.CreatePanel(self, frame, {
            createLabel = createLabel,
            createMiniActionButton = createMiniActionButton,
            createSectionFrame = createSectionFrame,
            MAIN_TABS = MAIN_TABS,
            styleScrollFrame = styleScrollFrame,
        })
    end

    frame.sidebar = createSectionFrame(frame.body, "card")
    frame.sidebar:SetPoint("TOPLEFT", frame.mainTabBar, "BOTTOMLEFT", 0, -12)
    frame.sidebar:SetPoint("BOTTOMLEFT", 0, 0)
    frame.sidebar:SetWidth(268)
    setSectionFrameTextures(frame.sidebar, PREMIUM.sidebarPlate)

    frame.sidebarTitle = createLabel(frame.sidebar, "GameFontNormalLarge", "LEFT")
    frame.sidebarTitle:SetPoint("TOPLEFT", 16, -16)
    frame.sidebarTitle:SetText(L.SIDEBAR_TITLE or "Warband")

    frame.sidebarWarbandBadgeBackplate = frame.sidebar:CreateTexture(nil, "ARTWORK")
    frame.sidebarWarbandBadgeBackplate:SetSize(48, 48)
    frame.sidebarWarbandBadgeBackplate:SetPoint("TOPRIGHT", -16, -10)
    frame.sidebarWarbandBadgeBackplate:SetTexture(PREMIUM.classBackplate)

    frame.sidebarWarbandBadge = frame.sidebar:CreateTexture(nil, "OVERLAY")
    frame.sidebarWarbandBadge:SetSize(40, 40)
    frame.sidebarWarbandBadge:SetPoint("CENTER", frame.sidebarWarbandBadgeBackplate, "CENTER", 0, 0)
    frame.sidebarWarbandBadge:SetTexture(PREMIUM.renownWarbandBadges and PREMIUM.renownWarbandBadges[1] or PREMIUM.statusBadgeOpen)
    frame.sidebarWarbandBadge:SetTexCoord(0, 1, 0, 1)

    frame.sidebarWarbandBadgeHitbox = CreateFrame("Frame", nil, frame.sidebar)
    frame.sidebarWarbandBadgeHitbox:SetAllPoints(frame.sidebarWarbandBadgeBackplate)
    frame.sidebarWarbandBadgeHitbox:EnableMouse(true)

    frame.sidebarTitle:SetPoint("RIGHT", frame.sidebarWarbandBadgeBackplate, "LEFT", -10, 0)

    frame.sidebarSubtitle = createLabel(frame.sidebar, "GameFontHighlightSmall", "LEFT")
    frame.sidebarSubtitle:SetPoint("TOPLEFT", frame.sidebarTitle, "BOTTOMLEFT", 0, -8)
    frame.sidebarSubtitle:SetPoint("TOPRIGHT", -16, 0)
    frame.sidebarSubtitle:SetText(L.SIDEBAR_SUBTITLE or "")

    frame.sidebarCurrentCard = UISidebarView.CreateCurrentCharacterCard
        and UISidebarView.CreateCurrentCharacterCard(frame.sidebar, getSidebarViewHelpers())
        or CreateFrame("Button", nil, frame.sidebar, BACKDROP_TEMPLATE)
    frame.sidebarCurrentCard:SetPoint("TOPLEFT", frame.sidebarSubtitle, "BOTTOMLEFT", 0, -12)
    frame.sidebarCurrentCard:SetPoint("TOPRIGHT", -16, 0)
    frame.sidebarCurrentCard:SetHeight(82)

    frame.sidebarSortRow = CreateFrame("Frame", nil, frame.sidebar)
    frame.sidebarSortRow:SetPoint("TOPLEFT", frame.sidebarCurrentCard, "BOTTOMLEFT", 0, -12)
    frame.sidebarSortRow:SetPoint("TOPRIGHT", -16, 0)
    frame.sidebarSortRow:SetHeight(22)

    frame.sidebarSortDropdown = createMiniActionButton(frame.sidebarSortRow, "", 236)
    frame.sidebarSortDropdown:SetPoint("LEFT", 0, 0)
    frame.sidebarSortDropdown:SetPoint("RIGHT", 0, 0)

    frame.sidebarSortMenu = createSectionFrame(frame.sidebar, "cardInset")
    frame.sidebarSortMenu:SetPoint("TOPLEFT", frame.sidebarSortDropdown, "BOTTOMLEFT", 0, -4)
    frame.sidebarSortMenu:SetPoint("TOPRIGHT", frame.sidebarSortDropdown, "BOTTOMRIGHT", 0, -4)
    frame.sidebarSortMenu:SetFrameLevel(frame.sidebar:GetFrameLevel() + 20)
    frame.sidebarSortMenu:SetHeight(112)
    frame.sidebarSortMenu:EnableMouse(true)
    frame.sidebarSortMenu:Hide()

    frame.sidebarSortMenuRows = {}
    local sortDropdownOptions = {
        { key = "", label = function() return L.SIDEBAR_SORT_DEFAULT or "Manual" end },
    }
    for _, option in ipairs(SIDEBAR_SORT_OPTIONS) do
        sortDropdownOptions[#sortDropdownOptions + 1] = option
    end
    for index, option in ipairs(sortDropdownOptions) do
        local button = createMiniActionButton(frame.sidebarSortMenu, option.label(), 210)
        button:SetPoint("TOPLEFT", 12, -10 - ((index - 1) * 24))
        button:SetPoint("TOPRIGHT", -12, -10 - ((index - 1) * 24))
        button.sortKey = option.key or ""
        button.sortLabel = option.label
        button:SetScript("OnClick", function(selfButton)
            local nextSortMode = selfButton.sortKey ~= "" and selfButton.sortKey or nil
            if UI.sidebarSortMode == nextSortMode then
                nextSortMode = nil
            end
            if not UI.sidebarSortMode and nextSortMode then
                UI.sidebarBaseOrderKeys = {}
                for _, character in ipairs(ns:GetLiveVaultloomCharacters() or {}) do
                    if character and character.key then
                        UI.sidebarBaseOrderKeys[#UI.sidebarBaseOrderKeys + 1] = character.key
                    end
                end
            end
            if ns.SetSidebarSortMode then
                nextSortMode = ns:SetSidebarSortMode(nextSortMode, true)
            else
                UI.sidebarSortMode = nextSortMode
            end
            frame.sidebarSortMenu:Hide()
            UI:RefreshSidebar()
        end)
        frame.sidebarSortMenuRows[index] = button
    end
    frame.sidebarSortDropdown:SetScript("OnClick", function()
        local nextShown = not frame.sidebarSortMenu:IsShown()
        frame.sidebarVisibilityMenu:Hide()
        frame.sidebarSortMenu:SetShown(nextShown)
        UI:RefreshSidebar()
    end)

    frame.sidebarActionRow = CreateFrame("Frame", nil, frame.sidebar)
    frame.sidebarActionRow:SetPoint("TOPLEFT", frame.sidebarSortRow, "BOTTOMLEFT", 0, -8)
    frame.sidebarActionRow:SetPoint("TOPRIGHT", -16, 0)
    frame.sidebarActionRow:SetHeight(22)

    frame.sidebarVisibilityDropdown = createMiniActionButton(frame.sidebarActionRow, "", 236)
    frame.sidebarVisibilityDropdown:SetPoint("LEFT", 0, 0)
    frame.sidebarVisibilityDropdown:SetPoint("RIGHT", 0, 0)

    frame.sidebarVisibilityMenu = createSectionFrame(frame.sidebar, "cardInset")
    frame.sidebarVisibilityMenu:SetPoint("TOPLEFT", frame.sidebarVisibilityDropdown, "BOTTOMLEFT", 0, -4)
    frame.sidebarVisibilityMenu:SetPoint("TOPRIGHT", frame.sidebarVisibilityDropdown, "BOTTOMRIGHT", 0, -4)
    frame.sidebarVisibilityMenu:SetFrameLevel(frame.sidebar:GetFrameLevel() + 20)
    frame.sidebarVisibilityMenu:SetHeight(188)
    frame.sidebarVisibilityMenu:EnableMouse(true)
    frame.sidebarVisibilityMenu:Hide()

    frame.sidebarVisibilityMenuScroll = CreateFrame("ScrollFrame", nil, frame.sidebarVisibilityMenu, "UIPanelScrollFrameTemplate")
    frame.sidebarVisibilityMenuScroll:SetPoint("TOPLEFT", 10, -10)
    frame.sidebarVisibilityMenuScroll:SetPoint("BOTTOMRIGHT", -26, 10)

    frame.sidebarVisibilityMenuScrollChild = CreateFrame("Frame", nil, frame.sidebarVisibilityMenuScroll)
    frame.sidebarVisibilityMenuScrollChild:SetSize(190, 10)
    frame.sidebarVisibilityMenuScroll:SetScrollChild(frame.sidebarVisibilityMenuScrollChild)
    styleScrollFrame(frame.sidebarVisibilityMenuScroll)
    frame.sidebarVisibilityRows = {}
    frame.sidebarVisibilityDropdown:SetScript("OnClick", function()
        local nextShown = not frame.sidebarVisibilityMenu:IsShown()
        frame.sidebarSortMenu:Hide()
        frame.sidebarVisibilityMenu:SetShown(nextShown)
        UI:RefreshSidebar()
    end)

    frame.sidebarScroll = CreateFrame("ScrollFrame", nil, frame.sidebar, "UIPanelScrollFrameTemplate")
    frame.sidebarScroll:SetPoint("TOPLEFT", frame.sidebarActionRow, "BOTTOMLEFT", 0, -10)
    frame.sidebarScroll:SetPoint("BOTTOMRIGHT", -24, 34)

    frame.sidebarScrollChild = CreateFrame("Frame", nil, frame.sidebarScroll)
    frame.sidebarScrollChild:SetSize(214, 10)
    frame.sidebarScroll:SetScrollChild(frame.sidebarScrollChild)
    styleScrollFrame(frame.sidebarScroll)

    frame.sidebarButtons = {}

    frame.sidebarEmpty = createLabel(frame.sidebarScrollChild, "GameFontDisable", "LEFT")
    frame.sidebarEmpty:SetPoint("TOPLEFT", 4, -4)
    frame.sidebarEmpty:SetPoint("TOPRIGHT", -4, -4)
    frame.sidebarEmpty:SetJustifyV("TOP")
    frame.sidebarEmpty:SetWordWrap(true)
    frame.sidebarEmpty:SetText(L.SIDEBAR_EMPTY or "")

    frame.utility = createSectionFrame(frame.body, "card")
    frame.utility:SetPoint("TOPRIGHT", frame.body, "TOPRIGHT", 0, -44)
    frame.utility:SetPoint("BOTTOMRIGHT", frame.body, "BOTTOMRIGHT", 0, 0)
    frame.utility:SetWidth(304)
    setSectionFrameTextures(frame.utility, PREMIUM.utilityPlate)

    frame.utilityTitle = createLabel(frame.utility, "GameFontNormalLarge", "LEFT")
    frame.utilityTitle:SetPoint("TOPLEFT", 16, -16)
    frame.utilityTitle:SetText(L.UTILITY_TITLE or "Character resources")

    frame.utilitySubtitle = createLabel(frame.utility, "GameFontHighlightSmall", "LEFT")
    frame.utilitySubtitle:SetPoint("TOPLEFT", frame.utilityTitle, "BOTTOMLEFT", 0, -8)
    frame.utilitySubtitle:SetPoint("TOPRIGHT", -16, 0)
    frame.utilitySubtitle:SetText(L.UTILITY_SUBTITLE or "")

    frame.upgradeHeader = createUtilityHeader(frame.utility, L.UPGRADE_SECTION or "Item upgrade")
    frame.upgradeHeader:SetPoint("TOPLEFT", frame.utilitySubtitle, "BOTTOMLEFT", 0, -14)
    frame.upgradeHeader:SetPoint("TOPRIGHT", -16, 0)
    if UIUtilityView.StyleHeader then
        UIUtilityView.StyleHeader(frame.upgradeHeader)
    end

    frame.upgradePanel = createSectionFrame(frame.utility, "cardInset")
    frame.upgradePanel:SetPoint("TOPLEFT", frame.upgradeHeader, "BOTTOMLEFT", 0, -8)
    frame.upgradePanel:SetPoint("TOPRIGHT", frame.upgradeHeader, "TOPRIGHT", 0, -8)
    frame.upgradePanel:SetHeight(88)

    frame.upgradeButtons = {}
    for index = 1, #MID_UPGRADE_CRESTS do
        local button = createUpgradeButton(frame.upgradePanel)
        if index == 1 then
            button:SetPoint("TOP", frame.upgradePanel, "TOP", -96, -16)
        else
            button:SetPoint("LEFT", frame.upgradeButtons[index - 1], "RIGHT", 8, 0)
        end
        frame.upgradeButtons[index] = button
    end

    frame.resourceHeader = createUtilityHeader(frame.utility, L.RESOURCE_SECTION or "Resources")
    frame.resourceHeader:SetPoint("TOPLEFT", frame.upgradePanel, "BOTTOMLEFT", 0, -14)
    frame.resourceHeader:SetPoint("TOPRIGHT", -16, 0)
    if UIUtilityView.StyleHeader then
        UIUtilityView.StyleHeader(frame.resourceHeader)
    end

    frame.resourcePanel = createSectionFrame(frame.utility, "cardInset")
    frame.resourcePanel:SetPoint("TOPLEFT", frame.resourceHeader, "BOTTOMLEFT", 0, -8)
    frame.resourcePanel:SetPoint("BOTTOMRIGHT", -16, 16)

    frame.resourceInfo = createLabel(frame.resourcePanel, "GameFontDisableSmall", "LEFT")
    frame.resourceInfo:SetPoint("TOPLEFT", 14, -12)
    frame.resourceInfo:SetPoint("TOPRIGHT", -14, -12)
    frame.resourceInfo:SetText("")
    frame.resourceInfo:Hide()

    frame.resourceRestoreButton = createMiniActionButton(frame.resourcePanel, L.RESOURCE_SHOW_ALL or "Show all", 186)
    frame.resourceRestoreButton:SetPoint("TOPLEFT", 14, -10)
    frame.resourceRestoreButton.hiddenCount = 0
    frame.resourceRestoreButton:SetScript("OnClick", function(self)
        if (tonumber(self.hiddenCount) or 0) <= 0 or not ns.ClearHiddenUtilityResources then
            return
        end

        GameTooltip:Hide()
        ns:ClearHiddenUtilityResources()
    end)
    frame.resourceRestoreButton:HookScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_CURSOR_RIGHT")
        GameTooltip:AddLine(L.RESOURCE_SHOW_ALL or "Show all", 1, 0.82, 0.12)
        if (tonumber(self.hiddenCount) or 0) > 0 then
            GameTooltip:AddLine(L.RESOURCE_SHOW_ALL_TOOLTIP or "Shows all hidden resources again.", 0.82, 0.82, 0.78, true)
        else
            GameTooltip:AddLine(L.RESOURCE_SHOW_ALL_TOOLTIP_NONE or "No resources are currently hidden.", 0.65, 0.67, 0.72, true)
        end
        GameTooltip:Show()
    end)
    frame.resourceRestoreButton:HookScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    frame.resourceScroll = CreateFrame("ScrollFrame", nil, frame.resourcePanel, "UIPanelScrollFrameTemplate")
    frame.resourceScroll:SetPoint("TOPLEFT", frame.resourceRestoreButton, "BOTTOMLEFT", 0, -10)
    frame.resourceScroll:SetPoint("BOTTOMRIGHT", -26, 10)

    frame.resourceScrollChild = CreateFrame("Frame", nil, frame.resourceScroll)
    frame.resourceScrollChild:SetSize(216, 10)
    frame.resourceScroll:SetScrollChild(frame.resourceScrollChild)
    styleScrollFrame(frame.resourceScroll)

    frame.resourceRows = {}

    frame.content = createSectionFrame(frame.body, "card")
    frame.content:SetPoint("TOPLEFT", frame.sidebar, "TOPRIGHT", 14, 0)
    frame.content:SetPoint("BOTTOMRIGHT", frame.utility, "BOTTOMLEFT", -14, 0)
    setSectionFrameTextures(frame.content, PREMIUM.contentPlate)

    frame.leftDivider = frame.body:CreateTexture(nil, "ARTWORK")
    frame.leftDivider:SetPoint("TOPLEFT", frame.sidebar, "TOPRIGHT", 6, -2)
    frame.leftDivider:SetPoint("BOTTOMLEFT", frame.sidebar, "BOTTOMRIGHT", 6, 2)
    frame.leftDivider:SetWidth(2)
    frame.leftDivider:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.leftDivider:SetAlpha(0)

    frame.rightDivider = frame.body:CreateTexture(nil, "ARTWORK")
    frame.rightDivider:SetPoint("TOPRIGHT", frame.utility, "TOPLEFT", -6, -2)
    frame.rightDivider:SetPoint("BOTTOMRIGHT", frame.utility, "BOTTOMLEFT", -6, 2)
    frame.rightDivider:SetWidth(2)
    frame.rightDivider:SetTexture("Interface\\Buttons\\WHITE8X8")
    frame.rightDivider:SetAlpha(0)

    frame.heroPlate = createSectionFrame(frame.content, "card")
    frame.heroPlate:SetPoint("TOPLEFT", 14, -14)
    frame.heroPlate:SetPoint("TOPRIGHT", -14, -14)
    frame.heroPlate:SetHeight(112)
    setSectionFrameTextures(frame.heroPlate, PREMIUM.heroPlateBespoke)

    frame.heroPortraitBackplate = frame.heroPlate:CreateTexture(nil, "ARTWORK")
    frame.heroPortraitBackplate:SetSize(70, 70)
    frame.heroPortraitBackplate:SetPoint("TOPLEFT", 22, -12)
    frame.heroPortraitBackplate:SetTexture(PREMIUM.classBackplate)

    frame.heroPortrait = frame.heroPlate:CreateTexture(nil, "OVERLAY")
    frame.heroPortrait:SetSize(58, 58)
    frame.heroPortrait:SetPoint("CENTER", frame.heroPortraitBackplate, "CENTER", 0, 0)
    if frame.heroPortrait.AddMaskTexture then
        frame.heroPortraitMask = frame.heroPlate:CreateMaskTexture()
        frame.heroPortraitMask:SetTexture("Interface\\CHARACTERFRAME\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
        frame.heroPortraitMask:SetAllPoints(frame.heroPortrait)
        frame.heroPortrait:AddMaskTexture(frame.heroPortraitMask)
    end

    frame.heroPortraitRing = frame.heroPlate:CreateTexture(nil, "OVERLAY", nil, 1)
    frame.heroPortraitRing:SetSize(70, 70)
    frame.heroPortraitRing:SetPoint("CENTER", frame.heroPortraitBackplate, "CENTER", 0, 0)
    frame.heroPortraitRing:SetTexture(PREMIUM.classRing)

    frame.heroRankBadge = frame.heroPlate:CreateTexture(nil, "OVERLAY", nil, 2)
    frame.heroRankBadge:SetSize(68, 68)
    frame.heroRankBadge:SetPoint("RIGHT", frame.heroPlate, "RIGHT", -26, 0)
    frame.heroRankBadge:SetTexture(PREMIUM.statusBadgeOpen)
    frame.heroRankBadge:Hide()

    frame.heroProfessionBadgeFrame = CreateFrame("Frame", nil, frame.heroPlate)
    frame.heroProfessionBadgeFrame:SetPoint("TOPLEFT", frame.heroPortraitBackplate, "TOPRIGHT", 440, -2)
    frame.heroProfessionBadgeFrame:SetSize(111, 51)
    frame.heroProfessionBadgeFrame:Hide()

    frame.heroTitle = createLabel(frame.heroPlate, "GameFontNormalHuge", "LEFT")
    frame.heroTitle:SetPoint("TOPLEFT", frame.heroPortraitBackplate, "TOPRIGHT", 18, -4)
    frame.heroTitle:SetPoint("TOPRIGHT", frame.heroRankBadge, "LEFT", -18, -4)
    frame.heroTitle:SetText(L.HERO_TITLE or "New UI Skeleton")

    frame.heroSubtitle = createLabel(frame.heroPlate, "GameFontHighlightSmall", "LEFT")
    frame.heroSubtitle:SetPoint("TOPLEFT", frame.heroTitle, "BOTTOMLEFT", 0, -8)
    frame.heroSubtitle:SetPoint("TOPRIGHT", frame.heroRankBadge, "LEFT", -18, 0)
    frame.heroSubtitle:SetText(L.HERO_SUBTITLE or "")

    frame.heroMoney = createLabel(frame.heroPlate, "GameFontHighlightSmall", "LEFT")
    frame.heroMoney:SetPoint("TOPLEFT", frame.heroSubtitle, "BOTTOMLEFT", 0, -5)
    frame.heroMoney:SetPoint("TOPRIGHT", frame.heroRankBadge, "LEFT", -18, 0)
    frame.heroMoney:SetTextColor(0.92, 0.92, 0.88, 1)
    frame.heroMoney:Hide()

    frame.heroCharacter = createLabel(frame.heroPlate, "GameFontDisableSmall", "LEFT")
    frame.heroCharacter:SetPoint("TOPLEFT", frame.heroMoney, "BOTTOMLEFT", 0, -4)
    frame.heroCharacter:SetPoint("TOPRIGHT", frame.heroRankBadge, "LEFT", -18, 0)

    frame.subTabBar = CreateFrame("Frame", nil, frame.heroPlate)
    frame.subTabBar:SetPoint("TOPLEFT", frame.heroPlate, "TOPLEFT", 18, -82)
    frame.subTabBar:SetPoint("TOPRIGHT", -14, 0)
    frame.subTabBar:SetHeight(24)

    frame.subTabs = {}
    for index = 1, getMaxSubTabCount() do
        local button = createSubTab(frame.subTabBar, "", 112)
        if index == 1 then
            button:SetPoint("LEFT", 0, 0)
        else
            button:SetPoint("LEFT", frame.subTabs[index - 1], "RIGHT", 8, 0)
        end
        button:Hide()
        frame.subTabs[index] = button
    end

    frame.verticalSubTabRail = createSectionFrame(frame.content, "cardInset")
    frame.verticalSubTabRail:SetPoint("TOPLEFT", frame.heroPlate, "BOTTOMLEFT", 14, -20)
    frame.verticalSubTabRail:SetPoint("BOTTOMLEFT", frame.content, "BOTTOMLEFT", 14, 18)
    frame.verticalSubTabRail:SetWidth(168)
    frame.verticalSubTabRail:Hide()

    frame.verticalSubTabs = {}
    for index = 1, getMaxSubTabCount() do
        local button = createVerticalSubTab(frame.verticalSubTabRail, "", 142)
        button:Hide()
        frame.verticalSubTabs[index] = button
    end

    frame.contentTitle = createLabel(frame.content, "GameFontNormalLarge", "LEFT")
    frame.contentTitle:SetPoint("TOPLEFT", frame.heroPlate, "BOTTOMLEFT", 14, -20)
    frame.contentTitle:SetPoint("TOPRIGHT", -14, 0)

    frame.contentSubtitle = createLabel(frame.content, "GameFontHighlightSmall", "LEFT")
    frame.contentSubtitle:SetPoint("TOPLEFT", frame.contentTitle, "BOTTOMLEFT", 0, -8)
    frame.contentSubtitle:SetPoint("TOPRIGHT", -14, 0)
    frame.contentSubtitle:SetText(L.CONTENT_SUBTITLE or "")

    frame.placeholderBox = createSectionFrame(frame.content, "card")
    frame.placeholderBox:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.placeholderBox:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.placeholderText = createLabel(frame.placeholderBox, "GameFontDisableLarge", "LEFT")
    frame.placeholderText:SetPoint("TOPLEFT", 14, -18)
    frame.placeholderText:SetPoint("BOTTOMRIGHT", -14, 18)
    frame.placeholderText:SetJustifyV("TOP")
    frame.placeholderText:SetWordWrap(true)
    frame.placeholderText:SetText(L.CONTENT_PLACEHOLDER or "")

    frame.vaultCard = createSectionFrame(frame.content, "card")
    frame.vaultCard:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.vaultCard:SetPoint("BOTTOMRIGHT", -14, 18)
    frame.vaultCard:Hide()

    frame.vaultCard.title = createLabel(frame.vaultCard, "GameFontNormalLarge", "LEFT")
    frame.vaultCard.title:SetPoint("TOPLEFT", 14, -18)

    frame.vaultCard.subtitle = createLabel(frame.vaultCard, "GameFontHighlightSmall", "LEFT")
    frame.vaultCard.subtitle:SetPoint("TOPLEFT", frame.vaultCard.title, "BOTTOMLEFT", 0, -8)
    frame.vaultCard.subtitle:SetPoint("TOPRIGHT", -14, 0)

    frame.vaultCard.breakpoints = createLabel(frame.vaultCard, "GameFontDisableSmall", "LEFT")
    frame.vaultCard.breakpoints:SetPoint("TOPLEFT", frame.vaultCard.subtitle, "BOTTOMLEFT", 0, -8)
    frame.vaultCard.breakpoints:SetPoint("TOPRIGHT", -14, 0)

    frame.vaultCard.unlockLabel = createLabel(frame.vaultCard, "GameFontHighlightSmall", "LEFT")
    frame.vaultCard.unlockLabel:SetPoint("TOPLEFT", frame.vaultCard.breakpoints, "BOTTOMLEFT", 0, -18)
    frame.vaultCard.unlockLabel:SetText(L.VAULT_UNLOCK_LABEL or "Vault slots")

    frame.vaultCard.unlockValue = createLabel(frame.vaultCard, "GameFontHighlight", "RIGHT")
    frame.vaultCard.unlockValue:SetPoint("RIGHT", -14, 0)
    frame.vaultCard.unlockValue:SetPoint("CENTER", frame.vaultCard.unlockLabel, "CENTER", 0, 0)

    frame.vaultCard.unlockBar = createProgressBar(frame.vaultCard)
    frame.vaultCard.unlockBar:SetPoint("TOPLEFT", frame.vaultCard.unlockLabel, "BOTTOMLEFT", 0, -8)
    frame.vaultCard.unlockBar:SetPoint("TOPRIGHT", -14, 0)
    frame.vaultCard.unlockBar:SetHeight(14)

    frame.vaultCard.goalLabel = createLabel(frame.vaultCard, "GameFontHighlightSmall", "LEFT")
    frame.vaultCard.goalLabel:SetPoint("TOPLEFT", frame.vaultCard.unlockBar, "BOTTOMLEFT", 0, -18)
    frame.vaultCard.goalLabel:SetText(L.VAULT_GOAL_LABEL or "Max reward")

    frame.vaultCard.goalValue = createLabel(frame.vaultCard, "GameFontHighlight", "RIGHT")
    frame.vaultCard.goalValue:SetPoint("RIGHT", -14, 0)
    frame.vaultCard.goalValue:SetPoint("CENTER", frame.vaultCard.goalLabel, "CENTER", 0, 0)

    frame.vaultCard.goalBar = createProgressBar(frame.vaultCard)
    frame.vaultCard.goalBar:SetPoint("TOPLEFT", frame.vaultCard.goalLabel, "BOTTOMLEFT", 0, -8)
    frame.vaultCard.goalBar:SetPoint("TOPRIGHT", -14, 0)
    frame.vaultCard.goalBar:SetHeight(14)

    frame.vaultCard.note = createLabel(frame.vaultCard, "GameFontDisableSmall", "LEFT")
    frame.vaultCard.note:SetPoint("TOPLEFT", frame.vaultCard.goalBar, "BOTTOMLEFT", 0, -12)
    frame.vaultCard.note:SetPoint("RIGHT", -14, 0)
    frame.vaultCard.note:SetHeight(22)
    frame.vaultCard.note:SetJustifyV("TOP")
    frame.vaultCard.note:SetWordWrap(true)

    frame.vaultCard.slotCards = {}
    for index = 1, 3 do
        local slotCard = createVaultSlotCard(frame.vaultCard)
        frame.vaultCard.slotCards[index] = slotCard
    end
    frame.vaultCard:SetScript("OnSizeChanged", function()
        self:LayoutVaultSlotCards()
    end)

    frame.wishlistCard = createWishlistCard(frame.body)
    frame.wishlistCard:SetPoint("TOPLEFT", frame.mainTabBar, "BOTTOMLEFT", 0, -12)
    frame.wishlistCard:SetPoint("BOTTOMRIGHT", frame.body, "BOTTOMRIGHT", 0, 0)
    frame.wishlistCard:SetFrameLevel(frame.body:GetFrameLevel() + 6)
    frame.wishlistCard:EnableMouse(true)
    frame.wishlistCard.closeButton:SetScript("OnClick", function()
        self.wishlistPanelOpen = false
        self:Refresh()
    end)
    for _, button in ipairs(frame.wishlistCard.filterButtons or {}) do
        button:SetScript("OnClick", function(selfButton)
            self.wishlistFilterKey = selfButton.filterKey or "all"
            self:Refresh()
        end)
    end

    frame.pvePreyCard = createPvePreyCard(frame.content)
    frame.pvePreyCard:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.pvePreyCard:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.pveRaresCard = createPveRaresCard(frame.content)
    frame.pveRaresCard:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.pveRaresCard:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.pveWeeklyCard = createPveWeeklyCard(frame.content)
    frame.pveWeeklyCard:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.pveWeeklyCard:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.customTaskCard = createCustomTaskCard(frame.content)
    frame.customTaskCard:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.customTaskCard:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.renownCard = createRenownCard(frame.content)
    frame.renownCard:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.renownCard:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.compendiumCard = createCompendiumCard(frame.content)
    frame.compendiumCard:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.compendiumCard:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.professionCooldownCard = createProfessionCooldownCard(frame.content)
    frame.professionCooldownCard:SetPoint("TOPLEFT", frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
    frame.professionCooldownCard:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.raidModeSidebar = createRaidModeSidebar(frame.sidebar)

    frame.raidModeUtility = createRaidModeUtility(frame.utility)

    frame.raidModeCenter = createRaidModeCenter(frame.content)
    frame.raidModeCenter:SetPoint("TOPLEFT", frame.heroPlate, "BOTTOMLEFT", 0, -16)
    frame.raidModeCenter:SetPoint("BOTTOMRIGHT", -14, 18)

    frame.mythicPlusSidebar = createMythicPlusModeSidebar(frame.sidebar)

    frame.mythicPlusUtility = createMythicPlusModeUtility(frame.utility)
    frame.mythicPlusUtility:SetScript("OnSizeChanged", function()
        UI:LayoutMythicPlusCard()
    end)

    frame.mythicPlusCenter = createMythicPlusModeCenter(frame.content)
    frame.mythicPlusCenter:SetPoint("TOPLEFT", frame.heroPlate, "BOTTOMLEFT", 0, -16)
    frame.mythicPlusCenter:SetPoint("BOTTOMRIGHT", -14, 18)
    frame.mythicPlusCenter:SetScript("OnSizeChanged", function()
        UI:LayoutMythicPlusCard()
    end)

    frame.housingSidebar = createHousingModeSidebar(frame.sidebar)

    frame.housingUtility = createHousingModeUtility(frame.utility)
    frame.housingUtility:SetScript("OnSizeChanged", function()
        UI:LayoutHousingCard()
    end)

    frame.housingCenter = createHousingModeCenter(frame.content)
    frame.housingCenter:SetPoint("TOPLEFT", frame.heroPlate, "BOTTOMLEFT", 0, -16)
    frame.housingCenter:SetPoint("BOTTOMRIGHT", -14, 18)
    frame.housingCenter:SetScript("OnSizeChanged", function()
        UI:LayoutHousingCard()
    end)

    frame.featuresPanel = createSectionFrame(frame.body, "card")
    frame.featuresPanel:SetPoint("TOPLEFT", frame.mainTabBar, "BOTTOMLEFT", 0, -12)
    frame.featuresPanel:SetPoint("BOTTOMRIGHT", frame.body, "BOTTOMRIGHT", 0, 0)
    frame.featuresPanel:SetFrameLevel(frame.body:GetFrameLevel() + 6)
    frame.featuresPanel:EnableMouse(true)
    frame.featuresPanel:SetScript("OnEnter", function()
        GameTooltip:Hide()
    end)
    frame.featuresPanel:Hide()

    frame.featuresTitle = createLabel(frame.featuresPanel, "GameFontNormalLarge", "LEFT")
    frame.featuresTitle:SetPoint("TOPLEFT", 18, -16)
    frame.featuresTitle:SetPoint("TOPRIGHT", -18, -16)
    frame.featuresTitle:SetText(L.FEATURES_TITLE or "Features")

    frame.featuresSubtitle = createLabel(frame.featuresPanel, "GameFontHighlightSmall", "LEFT")
    frame.featuresSubtitle:SetPoint("TOPLEFT", frame.featuresTitle, "BOTTOMLEFT", 0, -8)
    frame.featuresSubtitle:SetPoint("TOPRIGHT", -18, 0)
    frame.featuresSubtitle:SetText(L.FEATURES_SUBTITLE or "Optional modules stay disabled by default. Turn on only what you want to use.")

    frame.featuresCategoryPanel = createSectionFrame(frame.featuresPanel, "cardInset")
    frame.featuresCategoryPanel:SetPoint("TOPLEFT", frame.featuresSubtitle, "BOTTOMLEFT", 0, -16)
    frame.featuresCategoryPanel:SetPoint("BOTTOMLEFT", frame.featuresPanel, "BOTTOMLEFT", 18, 18)
    frame.featuresCategoryPanel:SetWidth(214)
    frame.featuresCategoryPanel:EnableMouse(true)
    frame.featuresCategoryPanel:SetScript("OnEnter", function()
        GameTooltip:Hide()
    end)
    setSectionFrameTextures(frame.featuresCategoryPanel, PREMIUM.sidebarPlate, PREMIUM.insetOverlay)
    if frame.featuresCategoryPanel.bg then
        frame.featuresCategoryPanel.bg:SetTexCoord(0.06, 0.94, 0, 1)
    end

    frame.featuresCategoryTitle = createLabel(frame.featuresCategoryPanel, "GameFontNormalLarge", "LEFT")
    frame.featuresCategoryTitle:SetPoint("TOPLEFT", 14, -14)
    frame.featuresCategoryTitle:SetText(L.FEATURES_SECTION_CATEGORIES or "Categories")

    frame.featuresCategoryRows = {}
    local featuresCategoryKeys = {
        "progress",
        "character",
        "inventory",
        "crafting",
        "quick_tools",
        "blizzard",
        "layout",
        "experimental",
    }
    local featuresCategoryTexts = {
        L.FEATURE_CATEGORY_PROGRESS or "Progress Tweaks",
        L.FEATURE_CATEGORY_CHARACTER or "Character UI",
        L.FEATURE_CATEGORY_INVENTORY or "Inventory",
        L.FEATURE_CATEGORY_CRAFTING or "Crafting",
        L.FEATURE_CATEGORY_QUICK_TOOLS or "Quick Tools",
        L.FEATURE_CATEGORY_BLIZZARD or "Blizzard UI",
        L.FEATURE_CATEGORY_LAYOUT or "Layout & Scale",
        L.FEATURE_CATEGORY_EXPERIMENTAL or "Experimental",
    }
    local previousFeaturesCategory
    for index, text in ipairs(featuresCategoryTexts) do
        local row = createVerticalSubTab(frame.featuresCategoryPanel, text, 186)
        if previousFeaturesCategory then
            row:SetPoint("TOPLEFT", previousFeaturesCategory, "BOTTOMLEFT", 0, -8)
        else
            row:SetPoint("TOPLEFT", frame.featuresCategoryTitle, "BOTTOMLEFT", 0, -18)
        end
        row:SetPoint("TOPRIGHT", -14, 0)
        row:SetButtonText(text)
        row:SetWidth(186)
        row.categoryKey = featuresCategoryKeys[index]
        if row.SetActive then
            row:SetActive(index == 1)
        end
        row:SetScript("OnClick", function()
            self.featuresSelectedCategory = row.categoryKey
            self:RefreshFeaturesPanel()
        end)
        frame.featuresCategoryRows[index] = row
        previousFeaturesCategory = row
    end

    frame.featuresCategoryNote = createLabel(frame.featuresCategoryPanel, "GameFontDisableSmall", "LEFT")
    frame.featuresCategoryNote:SetPoint("BOTTOMLEFT", 14, 16)
    frame.featuresCategoryNote:SetPoint("BOTTOMRIGHT", -14, 16)
    frame.featuresCategoryNote:SetJustifyV("TOP")
    frame.featuresCategoryNote:SetWordWrap(true)
    frame.featuresCategoryNote:SetText(L.FEATURES_DEFAULT_NOTE or "Everything here starts disabled until you enable it.")

    frame.featuresLibraryPanel = createSectionFrame(frame.featuresPanel, "card")
    frame.featuresLibraryPanel:SetPoint("TOPLEFT", frame.featuresCategoryPanel, "TOPRIGHT", 14, 0)
    frame.featuresLibraryPanel:SetPoint("BOTTOMRIGHT", -18, 18)
    frame.featuresLibraryPanel:EnableMouse(true)
    frame.featuresLibraryPanel:SetScript("OnEnter", function()
        GameTooltip:Hide()
    end)
    setSectionFrameTextures(frame.featuresLibraryPanel, PREMIUM.contentPlate, PREMIUM.panelOverlay)
    if frame.featuresLibraryPanel.bg then
        frame.featuresLibraryPanel.bg:SetTexCoord(0, 1, 0.06, 0.94)
    end

    frame.featuresLibraryTitle = createLabel(frame.featuresLibraryPanel, "GameFontNormalLarge", "LEFT")
    frame.featuresLibraryTitle:SetPoint("TOPLEFT", 18, -16)
    frame.featuresLibraryTitle:SetPoint("TOPRIGHT", -18, -16)
    frame.featuresLibraryTitle:SetText(L.FEATURES_SECTION_LIST or "Feature Library")

    frame.featuresLibrarySubtitle = createLabel(frame.featuresLibraryPanel, "GameFontHighlightSmall", "LEFT")
    frame.featuresLibrarySubtitle:SetPoint("TOPLEFT", frame.featuresLibraryTitle, "BOTTOMLEFT", 0, -8)
    frame.featuresLibrarySubtitle:SetPoint("TOPRIGHT", -18, 0)
    frame.featuresLibrarySubtitle:SetText(L.FEATURES_SUBTITLE or "Turn on only what you want to use.")

    frame.featuresInfoPanel = createSectionFrame(frame.featuresLibraryPanel, "cardInset")
    frame.featuresInfoPanel:SetPoint("TOPLEFT", frame.featuresLibrarySubtitle, "BOTTOMLEFT", 0, -16)
    frame.featuresInfoPanel:SetPoint("TOPRIGHT", -18, 0)
    frame.featuresInfoPanel:SetHeight(112)
    frame.featuresInfoPanel:Hide()

    frame.featuresInfoTitle = createLabel(frame.featuresInfoPanel, "GameFontHighlight", "LEFT")
    frame.featuresInfoTitle:SetPoint("TOPLEFT", 14, -14)
    frame.featuresInfoTitle:SetPoint("TOPRIGHT", -14, -14)
    frame.featuresInfoTitle:SetText(L.FEATURES_SECTION_DETAILS or "Details")
    frame.featuresInfoTitle:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)

    frame.featuresInfoText = createLabel(frame.featuresInfoPanel, "GameFontHighlightSmall", "LEFT")
    frame.featuresInfoText:SetPoint("TOPLEFT", frame.featuresInfoTitle, "BOTTOMLEFT", 0, -10)
    frame.featuresInfoText:SetPoint("TOPRIGHT", -14, 0)
    frame.featuresInfoText:SetJustifyV("TOP")
    frame.featuresInfoText:SetWordWrap(true)
    frame.featuresInfoText:SetText("")

    frame.featuresFirstPanel = createSectionFrame(frame.featuresLibraryPanel, "cardInset")
    frame.featuresFirstPanel:SetPoint("TOPLEFT", frame.featuresLibrarySubtitle, "BOTTOMLEFT", 0, -16)
    frame.featuresFirstPanel:SetPoint("TOPRIGHT", -18, 0)
    frame.featuresFirstPanel:SetHeight(118)

    frame.featuresFirstTitle = createLabel(frame.featuresFirstPanel, "GameFontNormalLarge", "LEFT")
    frame.featuresFirstTitle:SetPoint("TOPLEFT", 14, -14)
    frame.featuresFirstTitle:SetPoint("TOPRIGHT", -128, -14)
    frame.featuresFirstTitle:SetText(L.FEATURE_PREY_BAR_TITLE or "Prey Progress Bar")
    frame.featuresFirstTitle:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)

    frame.featuresFirstStatus = createLabel(frame.featuresFirstPanel, "GameFontDisableSmall", "RIGHT")
    frame.featuresFirstStatus:SetPoint("TOPRIGHT", -14, -16)
    frame.featuresFirstStatus:SetWidth(92)
    frame.featuresFirstStatus:SetText(L.FEATURES_STATUS_NEXT_UP or "Next Up")
    frame.featuresFirstStatus:SetTextColor(0.92, 0.70, 0.38)

    frame.featuresFirstToggle = createMiniActionButton(frame.featuresFirstPanel, L.FEATURES_TOGGLE_OFF or "Off", 78)
    frame.featuresFirstToggle:SetPoint("TOPRIGHT", -14, -36)
    bindFeatureControlTooltip(frame.featuresFirstToggle)

    frame.featuresFirstOptionToggle = createMiniActionButton(frame.featuresFirstPanel, "", 78)
    frame.featuresFirstOptionToggle:SetPoint("TOPRIGHT", frame.featuresFirstToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresFirstOptionToggle)
    frame.featuresFirstOptionToggle:Hide()

    frame.featuresFirstSecondaryOptionToggle = createMiniActionButton(frame.featuresFirstPanel, "", 78)
    frame.featuresFirstSecondaryOptionToggle:SetPoint("TOPRIGHT", frame.featuresFirstOptionToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresFirstSecondaryOptionToggle)
    frame.featuresFirstSecondaryOptionToggle:Hide()

    frame.featuresFirstTravelConfigButton = createMiniActionButton(frame.featuresFirstPanel, "", 78)
    frame.featuresFirstTravelConfigButton:SetPoint("TOPRIGHT", frame.featuresFirstSecondaryOptionToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresFirstTravelConfigButton)
    frame.featuresFirstTravelConfigButton:Hide()

    frame.featuresFirstText = createLabel(frame.featuresFirstPanel, "GameFontHighlightSmall", "LEFT")
    frame.featuresFirstText:SetPoint("TOPLEFT", 14, -68)
    frame.featuresFirstText:SetPoint("TOPRIGHT", -14, 0)
    frame.featuresFirstText:SetJustifyV("TOP")
    frame.featuresFirstText:SetWordWrap(true)
    frame.featuresFirstText:SetText(L.FEATURE_PREY_BAR_INITIAL_TEXT or "First planned module: a clean prey progress bar that only appears during an active prey hunt in the matching zone.")

    frame.featuresFirstPanel:SetScript("OnEnter", function()
        local feature = frame.featuresFirstPanel.featureID and ns.GetFeatureDefinition and ns:GetFeatureDefinition(frame.featuresFirstPanel.featureID) or nil
        if not feature then
            return
        end

        GameTooltip:SetOwner(frame.featuresFirstPanel, "ANCHOR_CURSOR")
        GameTooltip:AddLine(feature.title or (L.FEATURE_PREY_BAR_TITLE or "Prey Progress Bar"), THEME.gold[1], THEME.gold[2], THEME.gold[3], true)
        if feature.description and feature.description ~= "" then
            GameTooltip:AddLine(feature.description, 0.88, 0.86, 0.82, true)
        end
        if feature.note and feature.note ~= "" then
            GameTooltip:AddLine(feature.note, 0.68, 0.74, 0.84, true)
        end
        GameTooltip:Show()
    end)
    frame.featuresFirstPanel:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    frame.featuresSecondPanel = createSectionFrame(frame.featuresLibraryPanel, "cardInset")
    frame.featuresSecondPanel:SetPoint("TOPLEFT", frame.featuresFirstPanel, "BOTTOMLEFT", 0, -12)
    frame.featuresSecondPanel:SetPoint("TOPRIGHT", -18, 0)
    frame.featuresSecondPanel:SetHeight(118)

    frame.featuresSecondTitle = createLabel(frame.featuresSecondPanel, "GameFontNormalLarge", "LEFT")
    frame.featuresSecondTitle:SetPoint("TOPLEFT", 14, -14)
    frame.featuresSecondTitle:SetPoint("TOPRIGHT", -128, -14)
    frame.featuresSecondTitle:SetText(L.FEATURE_GEAR_ITEM_LEVEL_TITLE or "Gear Item Level")
    frame.featuresSecondTitle:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)

    frame.featuresSecondStatus = createLabel(frame.featuresSecondPanel, "GameFontDisableSmall", "RIGHT")
    frame.featuresSecondStatus:SetPoint("TOPRIGHT", -14, -16)
    frame.featuresSecondStatus:SetWidth(92)
    frame.featuresSecondStatus:SetText("")

    frame.featuresSecondToggle = createMiniActionButton(frame.featuresSecondPanel, L.FEATURES_TOGGLE_OFF or "Off", 78)
    frame.featuresSecondToggle:SetPoint("TOPRIGHT", -14, -36)
    bindFeatureControlTooltip(frame.featuresSecondToggle)

    frame.featuresSecondOptionToggle = createMiniActionButton(frame.featuresSecondPanel, "", 78)
    frame.featuresSecondOptionToggle:SetPoint("TOPRIGHT", frame.featuresSecondToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresSecondOptionToggle)
    frame.featuresSecondOptionToggle:Hide()

    frame.featuresSecondSecondaryOptionToggle = createMiniActionButton(frame.featuresSecondPanel, "", 78)
    frame.featuresSecondSecondaryOptionToggle:SetPoint("TOPRIGHT", frame.featuresSecondOptionToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresSecondSecondaryOptionToggle)
    frame.featuresSecondSecondaryOptionToggle:Hide()

    frame.featuresSecondTravelConfigButton = createMiniActionButton(frame.featuresSecondPanel, "", 78)
    frame.featuresSecondTravelConfigButton:SetPoint("TOPRIGHT", frame.featuresSecondSecondaryOptionToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresSecondTravelConfigButton)
    frame.featuresSecondTravelConfigButton:Hide()

    frame.featuresSecondText = createLabel(frame.featuresSecondPanel, "GameFontHighlightSmall", "LEFT")
    frame.featuresSecondText:SetPoint("TOPLEFT", 14, -68)
    frame.featuresSecondText:SetPoint("TOPRIGHT", -14, 0)
    frame.featuresSecondText:SetJustifyV("TOP")
    frame.featuresSecondText:SetWordWrap(true)
    frame.featuresSecondText:SetText(L.FEATURE_GEAR_ITEM_LEVEL_DESCRIPTION or "Shows each equipped item's item level directly on the character gear slots.")

    frame.featuresSecondPanel:SetScript("OnEnter", function()
        local feature = frame.featuresSecondPanel.featureID and ns.GetFeatureDefinition and ns:GetFeatureDefinition(frame.featuresSecondPanel.featureID) or nil
        if not feature then
            return
        end

        GameTooltip:SetOwner(frame.featuresSecondPanel, "ANCHOR_CURSOR")
        GameTooltip:AddLine(feature.title or (L.FEATURE_GEAR_ITEM_LEVEL_TITLE or "Gear Item Level"), THEME.gold[1], THEME.gold[2], THEME.gold[3], true)
        if feature.description and feature.description ~= "" then
            GameTooltip:AddLine(feature.description, 0.88, 0.86, 0.82, true)
        end
        if feature.note and feature.note ~= "" then
            GameTooltip:AddLine(feature.note, 0.68, 0.74, 0.84, true)
        end
        GameTooltip:Show()
    end)
    frame.featuresSecondPanel:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    frame.featuresThirdPanel = createSectionFrame(frame.featuresLibraryPanel, "cardInset")
    frame.featuresThirdPanel:SetPoint("TOPLEFT", frame.featuresSecondPanel, "BOTTOMLEFT", 0, -12)
    frame.featuresThirdPanel:SetPoint("TOPRIGHT", -18, 0)
    frame.featuresThirdPanel:SetHeight(118)

    frame.featuresThirdTitle = createLabel(frame.featuresThirdPanel, "GameFontNormalLarge", "LEFT")
    frame.featuresThirdTitle:SetPoint("TOPLEFT", 14, -14)
    frame.featuresThirdTitle:SetPoint("TOPRIGHT", -128, -14)
    frame.featuresThirdTitle:SetText("")
    frame.featuresThirdTitle:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)

    frame.featuresThirdStatus = createLabel(frame.featuresThirdPanel, "GameFontDisableSmall", "RIGHT")
    frame.featuresThirdStatus:SetPoint("TOPRIGHT", -14, -16)
    frame.featuresThirdStatus:SetWidth(92)
    frame.featuresThirdStatus:SetText("")

    frame.featuresThirdToggle = createMiniActionButton(frame.featuresThirdPanel, L.FEATURES_TOGGLE_OFF or "Off", 78)
    frame.featuresThirdToggle:SetPoint("TOPRIGHT", -14, -36)
    bindFeatureControlTooltip(frame.featuresThirdToggle)

    frame.featuresThirdOptionToggle = createMiniActionButton(frame.featuresThirdPanel, "", 78)
    frame.featuresThirdOptionToggle:SetPoint("TOPRIGHT", frame.featuresThirdToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresThirdOptionToggle)
    frame.featuresThirdOptionToggle:Hide()

    frame.featuresThirdSecondaryOptionToggle = createMiniActionButton(frame.featuresThirdPanel, "", 78)
    frame.featuresThirdSecondaryOptionToggle:SetPoint("TOPRIGHT", frame.featuresThirdOptionToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresThirdSecondaryOptionToggle)
    frame.featuresThirdSecondaryOptionToggle:Hide()

    frame.featuresThirdTravelConfigButton = createMiniActionButton(frame.featuresThirdPanel, "", 78)
    frame.featuresThirdTravelConfigButton:SetPoint("TOPRIGHT", frame.featuresThirdSecondaryOptionToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresThirdTravelConfigButton)
    frame.featuresThirdTravelConfigButton:Hide()

    frame.featuresThirdText = createLabel(frame.featuresThirdPanel, "GameFontHighlightSmall", "LEFT")
    frame.featuresThirdText:SetPoint("TOPLEFT", 14, -68)
    frame.featuresThirdText:SetPoint("TOPRIGHT", -14, 0)
    frame.featuresThirdText:SetJustifyV("TOP")
    frame.featuresThirdText:SetWordWrap(true)
    frame.featuresThirdText:SetText("")

    frame.featuresThirdPanel:SetScript("OnEnter", function()
        local feature = frame.featuresThirdPanel.featureID and ns.GetFeatureDefinition and ns:GetFeatureDefinition(frame.featuresThirdPanel.featureID) or nil
        if not feature then
            return
        end

        GameTooltip:SetOwner(frame.featuresThirdPanel, "ANCHOR_CURSOR")
        GameTooltip:AddLine(feature.title or "", THEME.gold[1], THEME.gold[2], THEME.gold[3], true)
        if feature.description and feature.description ~= "" then
            GameTooltip:AddLine(feature.description, 0.88, 0.86, 0.82, true)
        end
        if feature.note and feature.note ~= "" then
            GameTooltip:AddLine(feature.note, 0.68, 0.74, 0.84, true)
        end
        GameTooltip:Show()
    end)
    frame.featuresThirdPanel:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    frame.featuresFourthPanel = createSectionFrame(frame.featuresLibraryPanel, "cardInset")
    frame.featuresFourthPanel:SetPoint("TOPLEFT", frame.featuresThirdPanel, "BOTTOMLEFT", 0, -12)
    frame.featuresFourthPanel:SetPoint("TOPRIGHT", -18, 0)
    frame.featuresFourthPanel:SetHeight(118)

    frame.featuresFourthTitle = createLabel(frame.featuresFourthPanel, "GameFontNormalLarge", "LEFT")
    frame.featuresFourthTitle:SetPoint("TOPLEFT", 14, -14)
    frame.featuresFourthTitle:SetPoint("TOPRIGHT", -128, -14)
    frame.featuresFourthTitle:SetText("")
    frame.featuresFourthTitle:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)

    frame.featuresFourthStatus = createLabel(frame.featuresFourthPanel, "GameFontDisableSmall", "RIGHT")
    frame.featuresFourthStatus:SetPoint("TOPRIGHT", -14, -16)
    frame.featuresFourthStatus:SetWidth(92)
    frame.featuresFourthStatus:SetText("")

    frame.featuresFourthToggle = createMiniActionButton(frame.featuresFourthPanel, L.FEATURES_TOGGLE_OFF or "Off", 78)
    frame.featuresFourthToggle:SetPoint("TOPRIGHT", -14, -36)
    bindFeatureControlTooltip(frame.featuresFourthToggle)

    frame.featuresFourthOptionToggle = createMiniActionButton(frame.featuresFourthPanel, "", 78)
    frame.featuresFourthOptionToggle:SetPoint("TOPRIGHT", frame.featuresFourthToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresFourthOptionToggle)
    frame.featuresFourthOptionToggle:Hide()

    frame.featuresFourthSecondaryOptionToggle = createMiniActionButton(frame.featuresFourthPanel, "", 78)
    frame.featuresFourthSecondaryOptionToggle:SetPoint("TOPRIGHT", frame.featuresFourthOptionToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresFourthSecondaryOptionToggle)
    frame.featuresFourthSecondaryOptionToggle:Hide()

    frame.featuresFourthTravelConfigButton = createMiniActionButton(frame.featuresFourthPanel, "", 78)
    frame.featuresFourthTravelConfigButton:SetPoint("TOPRIGHT", frame.featuresFourthSecondaryOptionToggle, "TOPLEFT", -8, 0)
    bindFeatureControlTooltip(frame.featuresFourthTravelConfigButton)
    frame.featuresFourthTravelConfigButton:Hide()

    frame.featuresFourthText = createLabel(frame.featuresFourthPanel, "GameFontHighlightSmall", "LEFT")
    frame.featuresFourthText:SetPoint("TOPLEFT", 14, -68)
    frame.featuresFourthText:SetPoint("TOPRIGHT", -14, 0)
    frame.featuresFourthText:SetJustifyV("TOP")
    frame.featuresFourthText:SetWordWrap(true)
    frame.featuresFourthText:SetText("")

    frame.featuresFourthPanel:SetScript("OnEnter", function()
        local feature = frame.featuresFourthPanel.featureID and ns.GetFeatureDefinition and ns:GetFeatureDefinition(frame.featuresFourthPanel.featureID) or nil
        if not feature then
            return
        end

        GameTooltip:SetOwner(frame.featuresFourthPanel, "ANCHOR_CURSOR")
        GameTooltip:AddLine(feature.title or "", THEME.gold[1], THEME.gold[2], THEME.gold[3], true)
        if feature.description and feature.description ~= "" then
            GameTooltip:AddLine(feature.description, 0.88, 0.86, 0.82, true)
        end
        if feature.note and feature.note ~= "" then
            GameTooltip:AddLine(feature.note, 0.68, 0.74, 0.84, true)
        end
        GameTooltip:Show()
    end)
    frame.featuresFourthPanel:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    frame.featuresFirstPanel:Hide()
    frame.featuresSecondPanel:Hide()
    frame.featuresThirdPanel:Hide()
    frame.featuresFourthPanel:Hide()

    frame.featuresListHeader = CreateFrame("Frame", nil, frame.featuresLibraryPanel)
    frame.featuresListHeader:SetPoint("TOPLEFT", frame.featuresLibrarySubtitle, "BOTTOMLEFT", 0, -14)
    frame.featuresListHeader:SetPoint("TOPRIGHT", -28, 0)
    frame.featuresListHeader:SetHeight(22)

    frame.featuresListFeatureHeader = createLabel(frame.featuresListHeader, "GameFontDisableSmall", "LEFT")
    frame.featuresListFeatureHeader:SetPoint("LEFT", 16, 0)
    frame.featuresListFeatureHeader:SetWidth(300)
    frame.featuresListFeatureHeader:SetText(L.FEATURES_HEADER_FEATURE or "Feature")

    frame.featuresListStatusHeader = createLabel(frame.featuresListHeader, "GameFontDisableSmall", "RIGHT")
    frame.featuresListStatusHeader:SetPoint("RIGHT", -318, 0)
    frame.featuresListStatusHeader:SetWidth(92)
    frame.featuresListStatusHeader:SetText(L.FEATURES_HEADER_STATUS or "Status")

    frame.featuresListOptionsHeader = createLabel(frame.featuresListHeader, "GameFontDisableSmall", "RIGHT")
    frame.featuresListOptionsHeader:SetPoint("RIGHT", -14, 0)
    frame.featuresListOptionsHeader:SetWidth(286)
    frame.featuresListOptionsHeader:SetText(L.FEATURES_HEADER_OPTIONS or "Settings")

    frame.featuresListScroll = CreateFrame("ScrollFrame", nil, frame.featuresLibraryPanel, "UIPanelScrollFrameTemplate")
    frame.featuresListScroll:SetPoint("TOPLEFT", frame.featuresListHeader, "BOTTOMLEFT", 0, -6)
    frame.featuresListScroll:SetPoint("BOTTOMRIGHT", -28, 16)

    frame.featuresListScrollChild = CreateFrame("Frame", nil, frame.featuresListScroll)
    frame.featuresListScrollChild:SetSize(720, 10)
    frame.featuresListScroll:SetScrollChild(frame.featuresListScrollChild)
    styleScrollFrame(frame.featuresListScroll)

    frame.featuresEmptyText = createLabel(frame.featuresListScrollChild, "GameFontDisableLarge", "LEFT")
    frame.featuresEmptyText:SetPoint("TOPLEFT", 16, -18)
    frame.featuresEmptyText:SetPoint("TOPRIGHT", -16, -18)
    frame.featuresEmptyText:SetText(L.FEATURES_EMPTY or "No features are available in this category yet.")
    frame.featuresEmptyText:Hide()

    frame.featuresRows = {}
    local function createFeatureCheckbox(parent)
        local checkbox = CreateFrame("Button", nil, parent, BACKDROP_TEMPLATE)
        checkbox:SetSize(22, 22)
        if checkbox.SetBackdrop then
            checkbox:SetBackdrop({
                bgFile = "Interface\\Buttons\\WHITE8X8",
                edgeFile = "Interface\\Buttons\\WHITE8X8",
                tile = false,
                edgeSize = 1,
                insets = { left = 1, right = 1, top = 1, bottom = 1 },
            })
            checkbox:SetBackdropColor(THEME.metalDeep[1], THEME.metalDeep[2], THEME.metalDeep[3], 0.92)
            checkbox:SetBackdropBorderColor(THEME.goldDim[1], THEME.goldDim[2], THEME.goldDim[3], 0.70)
        end

        checkbox.hover = checkbox:CreateTexture(nil, "BACKGROUND", nil, 1)
        checkbox.hover:SetAllPoints()
        checkbox.hover:SetTexture(PREMIUM.buttonHover)
        setTextureColor(checkbox.hover, { 1, 1, 1 }, 1)
        checkbox.hover:SetAlpha(0)

        checkbox.mark = checkbox:CreateTexture(nil, "ARTWORK")
        checkbox.mark:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
        checkbox.mark:SetSize(26, 26)
        checkbox.mark:SetPoint("CENTER", 0, 0)
        checkbox.mark:SetVertexColor(0.15, 1.00, 0.46, 1)
        checkbox.mark:Hide()

        checkbox:SetScript("OnEnter", function(selfButton)
            if selfButton.SetBackdropBorderColor then
                selfButton:SetBackdropBorderColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.92)
            end
            selfButton.hover:SetAlpha(1)
        end)
        checkbox:SetScript("OnLeave", function(selfButton)
            if selfButton.SetBackdropBorderColor then
                if selfButton.vlChecked then
                    selfButton:SetBackdropBorderColor(0.12, 0.86, 0.42, 0.92)
                else
                    selfButton:SetBackdropBorderColor(THEME.goldDim[1], THEME.goldDim[2], THEME.goldDim[3], 0.70)
                end
            end
            selfButton.hover:SetAlpha(0)
            GameTooltip:Hide()
        end)
        return checkbox
    end

    frame.CreateFeatureSettingsRow = function(parent)
        local row = createSectionFrame(parent, "cardInset")
        row:SetHeight(82)
        row:EnableMouse(true)

        row.checkbox = createFeatureCheckbox(row)
        row.checkbox:SetPoint("TOPLEFT", 14, -18)
        bindFeatureControlTooltip(row.checkbox)

        row.title = createLabel(row, "GameFontNormal", "LEFT")
        row.title:SetPoint("TOPLEFT", row.checkbox, "TOPRIGHT", 12, 2)
        row.title:SetPoint("RIGHT", -430, 0)
        row.title:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)

        row.description = createLabel(row, "GameFontHighlightSmall", "LEFT")
        row.description:SetPoint("TOPLEFT", row.title, "BOTTOMLEFT", 0, -5)
        row.description:SetPoint("RIGHT", -430, 0)
        row.description:SetJustifyV("TOP")
        row.description:SetWordWrap(true)

        row.status = createLabel(row, "GameFontHighlightSmall", "RIGHT")
        row.status:SetPoint("TOPRIGHT", -318, -18)
        row.status:SetWidth(92)

        row.applyMode = createLabel(row, "GameFontDisableSmall", "RIGHT")
        row.applyMode:SetPoint("TOPRIGHT", row.status, "BOTTOMRIGHT", 0, -5)
        row.applyMode:SetWidth(92)

        row.optionsLabel = createLabel(row, "GameFontDisableSmall", "RIGHT")
        row.optionsLabel:SetPoint("TOPRIGHT", -14, -16)
        row.optionsLabel:SetWidth(286)
        row.optionsLabel:SetText(L.FEATURES_ROW_OPTIONS or "Settings")

        row.optionToggle = createMiniActionButton(row, "", 104)
        row.optionToggle:SetPoint("BOTTOMRIGHT", -14, 14)
        bindFeatureControlTooltip(row.optionToggle)
        row.optionToggle:Hide()

        row.secondaryOptionToggle = createMiniActionButton(row, "", 104)
        row.secondaryOptionToggle:SetPoint("BOTTOMRIGHT", row.optionToggle, "BOTTOMLEFT", -8, 0)
        bindFeatureControlTooltip(row.secondaryOptionToggle)
        row.secondaryOptionToggle:Hide()

        row.noOptions = createLabel(row, "GameFontDisableSmall", "RIGHT")
        row.noOptions:SetPoint("BOTTOMRIGHT", -14, 17)
        row.noOptions:SetWidth(286)
        row.noOptions:SetText(L.FEATURES_ROW_NO_OPTIONS or "No extra settings")

        row.travelConfigButton = createMiniActionButton(row, "", 104)
        row.travelConfigButton:Hide()
        bindFeatureControlTooltip(row.travelConfigButton)

        return row
    end

    if UISettingsPanel.BindActions then
        UISettingsPanel.BindActions(self, frame)
    end
    if UIFeaturesPanel.BindActions then
        UIFeaturesPanel.BindActions(self, frame)
    end
    if frame.sidebarHideButton then
        frame.sidebarHideButton:SetScript("OnClick", function()
            local character = ns:GetSelectedCharacter()
            local key = character and character.key or nil
            local ok, reason = ns:SetCharacterHidden(key, true)
            if not ok and reason == "current" then
                ns:Print(L.SIDEBAR_HIDE_CURRENT_ERROR or "The currently logged-in character cannot be hidden.")
            end
        end)
    end
    if frame.sidebarUnhideButton then
        frame.sidebarUnhideButton:SetScript("OnClick", function()
            ns:ClearHiddenCharacters()
        end)
    end

    self.frame = frame
    self:ApplyWindowPosition()
end

function UI:EnsureSidebarButtons(count)
    if UISidebarView.EnsureButtons then
        UISidebarView.EnsureButtons(self, count, getSidebarViewHelpers())
    end
end

function UI:RefreshSidebar()
    if UISidebarView.Refresh then
        UISidebarView.Refresh(self, getSidebarViewHelpers())
    end
end

function UI:EnsureResourceRows(count)
    if UIUtilityView.EnsureResourceRows then
        UIUtilityView.EnsureResourceRows(self, count, getUtilityViewHelpers())
    end
end



function UI:EnsureWishlistRows(count)
    if UIWishlistCard.EnsureRows then
        UIWishlistCard.EnsureRows(self, count, getWishlistCardHelpers())
    end
end

function UI:RefreshWishlistButton(character)
    if UIWishlistCard.RefreshButton then
        UIWishlistCard.RefreshButton(self, character, getWishlistCardHelpers())
    end
end

function UI:RefreshWishlistCard(character)
    if UIWishlistCard.RefreshCard then
        UIWishlistCard.RefreshCard(self, character, getWishlistCardHelpers())
    end
end

function UI:EnsureRaidModeRaidRows(count)
    if UIRaidModeCard.EnsureRaidRows then
        UIRaidModeCard.EnsureRaidRows(self, count, getRaidModeCardHelpers())
    end
end

function UI:EnsureRaidModeBossRows(count)
    if UIRaidModeCard.EnsureBossRows then
        UIRaidModeCard.EnsureBossRows(self, count, getRaidModeCardHelpers())
    end
end

function UI:EnsureRaidModeLootRows(count)
    if UIRaidModeCard.EnsureLootRows then
        UIRaidModeCard.EnsureLootRows(self, count, getRaidModeCardHelpers())
    end
end

function UI:RefreshRaidMode(character, view)
    if UIRaidModeCard.Refresh then
        UIRaidModeCard.Refresh(self, character, view, getRaidModeCardHelpers())
    end
end

function UI:ApplyRaidModeLayout(layoutMode)
    if UIWorkbenchLayout.ApplyModeLayout then
        UIWorkbenchLayout.ApplyModeLayout(self, layoutMode)
    end
end

local function getRenownBadgeTexture(character)
    if ns.IsRenownSystemEnabled and not ns:IsRenownSystemEnabled() then
        return nil
    end

    local view = ns.GetRenownView and ns:GetRenownView(character, "character") or nil
    local rank = view and view.rank or nil
    local index = tonumber(rank and rank.assetIndex)
    if index and PREMIUM.renownBadges and PREMIUM.renownBadges[index] then
        return PREMIUM.renownBadges[index]
    end
    return PREMIUM.statusBadgeOpen
end

function UI:ApplyContentNavigationLayout(selectedMainTab)
    if not self.frame then
        return
    end

    local frame = self.frame
    local useVerticalSubTabs = self.UsesVerticalSubTabs and self:UsesVerticalSubTabs(selectedMainTab)

    if frame.verticalSubTabRail then
        frame.verticalSubTabRail:ClearAllPoints()
        frame.verticalSubTabRail:SetPoint("TOPLEFT", frame.heroPlate, "BOTTOMLEFT", 14, -20)
        frame.verticalSubTabRail:SetPoint("BOTTOMLEFT", frame.content, "BOTTOMLEFT", 14, 18)
        frame.verticalSubTabRail:SetWidth(168)
        frame.verticalSubTabRail:SetShown(useVerticalSubTabs == true)
    end

    if frame.contentTitle then
        frame.contentTitle:ClearAllPoints()
        if selectedMainTab == "chronicle" or selectedMainTab == "compendium" then
            frame.contentTitle:SetPoint("TOPLEFT", frame.content, "TOPLEFT", 16, -16)
        elseif useVerticalSubTabs and frame.verticalSubTabRail then
            frame.contentTitle:SetPoint("TOPLEFT", frame.verticalSubTabRail, "TOPRIGHT", 14, 0)
        else
            frame.contentTitle:SetPoint("TOPLEFT", frame.heroPlate, "BOTTOMLEFT", 14, -20)
        end
        frame.contentTitle:SetPoint("TOPRIGHT", -14, 0)
    end

    if frame.contentSubtitle then
        frame.contentSubtitle:ClearAllPoints()
        frame.contentSubtitle:SetPoint("TOPLEFT", frame.contentTitle, "BOTTOMLEFT", 0, -8)
        frame.contentSubtitle:SetPoint("TOPRIGHT", -14, 0)
    end
end

function UI:RefreshUtility(character)
    if UIUtilityView.Refresh then
        UIUtilityView.Refresh(self, character, getUtilityViewHelpers())
    end
end

function UI:RefreshCustomTasksCard(character, snapshot)
    if UICustomTaskView.RefreshCard then
        UICustomTaskView.RefreshCard(self, character, snapshot, getCustomTaskCardHelpers())
    end
end

function UI:RefreshCustomTaskTracker()
    if UICustomTaskView.RefreshTracker then
        UICustomTaskView.RefreshTracker(self, getCustomTaskCardHelpers())
    end
end

function UI:RefreshRenownCard(character)
    if UIRenownView.RefreshCard then
        UIRenownView.RefreshCard(self, character, getRenownCardHelpers())
    end
end

function UI:RefreshCompendiumCard(character)
    if UICompendiumView.RefreshCard then
        UICompendiumView.RefreshCard(self, character, getCompendiumCardHelpers())
    end
end

local function hasRenownLevelAdvanced(beforeLevel, afterLevel, beforeParagon, afterParagon)
    beforeLevel = tonumber(beforeLevel) or 1
    afterLevel = tonumber(afterLevel) or 1
    beforeParagon = tonumber(beforeParagon) or 0
    afterParagon = tonumber(afterParagon) or 0
    return afterLevel > beforeLevel or (afterLevel == beforeLevel and afterParagon > beforeParagon)
end

function UI:ApplyRenownToastPosition(toast)
    toast = toast or self.renownToast
    if not toast then
        return
    end

    local position = ns.GetRenownToastPosition and ns:GetRenownToastPosition() or { point = "CENTER", relativePoint = "CENTER", x = 0, y = 170 }
    toast:ClearAllPoints()
    toast:SetPoint(position.point or "CENTER", UIParent or self.frame, position.relativePoint or "CENTER", tonumber(position.x) or 0, tonumber(position.y) or 170)
end

function UI:StoreRenownToastPosition()
    local toast = self.renownToast
    local parent = UIParent or self.frame
    if not toast or not parent or not ns.SetRenownToastPosition then
        return
    end

    local centerX, centerY = toast:GetCenter()
    local parentWidth = parent:GetWidth()
    local parentHeight = parent:GetHeight()
    if not centerX or not centerY or not parentWidth or not parentHeight then
        return
    end

    ns:SetRenownToastPosition(math.floor((centerX - (parentWidth / 2)) + 0.5), math.floor((centerY - (parentHeight / 2)) + 0.5))
end

local RENOWN_TOAST_STYLE_DEFAULT = "normal"
local RENOWN_TOAST_ANIMATION_DEFAULT = "soft"
local RENOWN_TOAST_SCALE_DEFAULT = 1
local RENOWN_TOAST_SCALE_MIN = 0.75
local RENOWN_TOAST_SCALE_MAX = 1.35
local RENOWN_TOAST_STYLES = {
    normal = true,
    clean = true,
    simple = true,
}
local RENOWN_TOAST_ANIMATIONS = {
    soft = true,
    fade = true,
    pop = true,
}

local function getRenownToastStyle()
    local style = ns.GetRenownToastStyle and ns:GetRenownToastStyle() or RENOWN_TOAST_STYLE_DEFAULT
    return RENOWN_TOAST_STYLES[style] and style or RENOWN_TOAST_STYLE_DEFAULT
end

local function getRenownToastAnimation()
    local animation = ns.GetRenownToastAnimation and ns:GetRenownToastAnimation() or RENOWN_TOAST_ANIMATION_DEFAULT
    return RENOWN_TOAST_ANIMATIONS[animation] and animation or RENOWN_TOAST_ANIMATION_DEFAULT
end

local function getRenownToastScale()
    local scale = ns.GetRenownToastScale and ns:GetRenownToastScale() or RENOWN_TOAST_SCALE_DEFAULT
    scale = tonumber(scale) or RENOWN_TOAST_SCALE_DEFAULT
    if scale < RENOWN_TOAST_SCALE_MIN then
        return RENOWN_TOAST_SCALE_MIN
    end
    if scale > RENOWN_TOAST_SCALE_MAX then
        return RENOWN_TOAST_SCALE_MAX
    end
    return scale
end

local function setRenownToastVisualScale(toast, animationScale)
    if toast then
        toast:SetScale(getRenownToastScale() * (tonumber(animationScale) or 1))
    end
end

local function setRenownToastRegionShown(region, shown)
    if not region then
        return
    end
    if shown then
        region:Show()
    else
        region:Hide()
    end
end

local function getRenownToastStartScale(animation)
    if animation == "fade" then
        return 1
    end
    if animation == "pop" then
        return 0.88
    end
    return 0.94
end

local function getRenownToastFadeInScale(animation, ratio)
    if animation == "fade" then
        return 1
    end
    if animation == "pop" then
        return 0.88 + (0.12 * ratio)
    end
    return 0.94 + (0.06 * ratio)
end

local function getRenownToastFadeOutScale(animation, ratio)
    if animation == "fade" then
        return 1
    end
    if animation == "pop" then
        return 1 + (0.03 * ratio)
    end
    return 1 + (0.015 * ratio)
end

local function applyRenownToastStyle(toast, style)
    if not toast then
        return RENOWN_TOAST_STYLE_DEFAULT
    end

    style = RENOWN_TOAST_STYLES[style] and style or RENOWN_TOAST_STYLE_DEFAULT
    local isNormal = style == "normal"
    local isSimple = style == "simple"
    toast.vlRenownToastStyle = style

    toast:SetSize(isSimple and 190 or 438, isSimple and 70 or 94)
    if toast.SetBackdropColor then
        if isNormal then
            toast:SetBackdropColor(THEME.metalDeep[1], THEME.metalDeep[2], THEME.metalDeep[3], 0.88)
            toast:SetBackdropBorderColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.82)
        else
            toast:SetBackdropColor(0, 0, 0, 0)
            toast:SetBackdropBorderColor(0, 0, 0, 0)
        end
    end

    setRenownToastRegionShown(toast.backGlow, isNormal)
    setRenownToastRegionShown(toast.topRule, isNormal)
    setRenownToastRegionShown(toast.bottomRule, isNormal)
    if toast.backGlow and isNormal then
        toast.backGlow:SetVertexColor(THEME.shadow[1], THEME.shadow[2], THEME.shadow[3], 0.34)
    end
    setRenownToastRegionShown(toast.title, not isSimple)
    setRenownToastRegionShown(toast.detail, not isSimple)
    setRenownToastRegionShown(toast.meta, not isSimple)

    if toast.icon then
        toast.icon:ClearAllPoints()
        if isSimple then
            toast.icon:SetSize(52, 52)
            toast.icon:SetPoint("LEFT", toast, "LEFT", 14, 0)
        else
            toast.icon:SetSize(62, 62)
            toast.icon:SetPoint("LEFT", toast, "LEFT", 18, 0)
        end
    end
    if toast.title then
        toast.title:ClearAllPoints()
        toast.title:SetPoint("TOPLEFT", toast.icon, "TOPRIGHT", 12, -2)
        toast.title:SetPoint("TOPRIGHT", toast, "TOPRIGHT", -112, -18)
    end
    if toast.detail then
        toast.detail:ClearAllPoints()
        toast.detail:SetPoint("TOPLEFT", toast.title, "BOTTOMLEFT", 0, -7)
        toast.detail:SetPoint("TOPRIGHT", toast.title, "BOTTOMRIGHT", 0, -7)
    end
    if toast.meta then
        toast.meta:ClearAllPoints()
        toast.meta:SetPoint("TOPLEFT", toast.detail, "BOTTOMLEFT", 0, -5)
        toast.meta:SetPoint("TOPRIGHT", toast.detail, "BOTTOMRIGHT", 0, -5)
    end
    if toast.amount then
        toast.amount:ClearAllPoints()
        if toast.amount.SetJustifyH then
            toast.amount:SetJustifyH(isSimple and "LEFT" or "RIGHT")
        end
        if isSimple then
            toast.amount:SetPoint("LEFT", toast.icon, "RIGHT", 12, 0)
            toast.amount:SetPoint("RIGHT", toast, "RIGHT", -12, 0)
            toast.amount:SetWidth(104)
        else
            toast.amount:SetPoint("RIGHT", toast, "RIGHT", -18, 0)
            toast.amount:SetWidth(92)
        end
    end

    return style
end

function UI:EnsureRenownToast()
    if self.renownToast then
        return self.renownToast
    end

    local parent = UIParent or self.frame
    if not parent then
        return nil
    end

    local toast = CreateFrame("Frame", nil, parent, BACKDROP_TEMPLATE)
    toast:SetSize(438, 94)
    toast:SetFrameStrata("TOOLTIP")
    toast:SetFrameLevel(10000)
    toast:SetAlpha(0)
    toast:Hide()
    if toast.SetToplevel then
        toast:SetToplevel(true)
    end
    if toast.EnableMouse then
        toast:EnableMouse(false)
    end
    if toast.SetClampedToScreen then
        toast:SetClampedToScreen(true)
    end
    if toast.SetBackdrop then
        toast:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            tile = false,
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 },
        })
        toast:SetBackdropColor(THEME.metalDeep[1], THEME.metalDeep[2], THEME.metalDeep[3], 0.88)
        toast:SetBackdropBorderColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.82)
    end

    toast.backGlow = toast:CreateTexture(nil, "BACKGROUND")
    toast.backGlow:SetAllPoints()
    toast.backGlow:SetTexture("Interface\\Buttons\\WHITE8X8")
    toast.backGlow:SetVertexColor(THEME.shadow[1], THEME.shadow[2], THEME.shadow[3], 0.34)

    toast.topRule = toast:CreateTexture(nil, "ARTWORK")
    toast.topRule:SetPoint("TOPLEFT", 8, -7)
    toast.topRule:SetPoint("TOPRIGHT", -8, -7)
    toast.topRule:SetHeight(2)
    toast.topRule:SetTexture("Interface\\Buttons\\WHITE8X8")
    toast.topRule:SetVertexColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.62)

    toast.bottomRule = toast:CreateTexture(nil, "ARTWORK")
    toast.bottomRule:SetPoint("BOTTOMLEFT", 8, 7)
    toast.bottomRule:SetPoint("BOTTOMRIGHT", -8, 7)
    toast.bottomRule:SetHeight(2)
    toast.bottomRule:SetTexture("Interface\\Buttons\\WHITE8X8")
    toast.bottomRule:SetVertexColor(THEME.goldDim[1], THEME.goldDim[2], THEME.goldDim[3], 0.56)

    toast.icon = toast:CreateTexture(nil, "OVERLAY")
    toast.icon:SetSize(62, 62)
    toast.icon:SetPoint("LEFT", 18, 0)
    toast.icon:SetTexture(PREMIUM.renownToastTask or PREMIUM.renownToastEmblem or PREMIUM.statusBadgeTurnin or PREMIUM.statusBadgeOpen)

    toast.title = createLabel(toast, "GameFontNormalLarge", "LEFT")
    toast.title:SetPoint("TOPLEFT", toast.icon, "TOPRIGHT", 12, -2)
    toast.title:SetPoint("TOPRIGHT", -112, -18)
    toast.title:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)

    toast.detail = createLabel(toast, "GameFontHighlightSmall", "LEFT")
    toast.detail:SetPoint("TOPLEFT", toast.title, "BOTTOMLEFT", 0, -7)
    toast.detail:SetPoint("TOPRIGHT", toast.title, "BOTTOMRIGHT", 0, -7)
    toast.detail:SetTextColor(THEME.parchment[1], THEME.parchment[2], THEME.parchment[3], 0.94)
    if toast.detail.SetWordWrap then
        toast.detail:SetWordWrap(false)
    end
    if toast.detail.SetNonSpaceWrap then
        toast.detail:SetNonSpaceWrap(false)
    end

    toast.meta = createLabel(toast, "GameFontDisableSmall", "LEFT")
    toast.meta:SetPoint("TOPLEFT", toast.detail, "BOTTOMLEFT", 0, -5)
    toast.meta:SetPoint("TOPRIGHT", toast.detail, "BOTTOMRIGHT", 0, -5)
    toast.meta:SetTextColor(0.70, 0.68, 0.62, 0.88)

    toast.amount = createLabel(toast, "GameFontNormalLarge", "RIGHT")
    toast.amount:SetPoint("RIGHT", -18, 0)
    toast.amount:SetWidth(92)
    toast.amount:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)

    self.renownToast = toast
    applyRenownToastStyle(toast, getRenownToastStyle())
    self:ApplyRenownToastPosition(toast)
    return toast
end

function UI:RefreshRenownToastStyle()
    if not self.renownToast then
        return
    end
    applyRenownToastStyle(self.renownToast, getRenownToastStyle())
    setRenownToastVisualScale(self.renownToast, 1)
    self:ApplyRenownToastPosition(self.renownToast)
end

function UI:HideRenownToast()
    self.renownToastQueue = nil
    self.renownToastActive = false
    self.renownToastPlacement = false
    if self.renownToast then
        self.renownToast:SetScript("OnUpdate", nil)
        self.renownToast:SetScript("OnDragStart", nil)
        self.renownToast:SetScript("OnDragStop", nil)
        self.renownToast:RegisterForDrag()
        self.renownToast:SetMovable(false)
        self.renownToast:EnableMouse(false)
        self.renownToast:SetAlpha(0)
        setRenownToastVisualScale(self.renownToast, 1)
        self.renownToast:Hide()
    end
end

function UI:ShowRenownTestToast(placementMode)
    local toast = self:EnsureRenownToast()
    if not toast then
        return
    end

    self.renownToastQueue = nil
    self.renownToastActive = false
    self.renownToastPlacement = placementMode == true
    toast:SetScript("OnUpdate", nil)
    self:ApplyRenownToastPosition(toast)
    toast.title:SetText(L.RENOWN_TOAST_TEST_TITLE or "Chronicle test popup")
    toast.detail:SetText(L.RENOWN_TOAST_TEST_DETAIL or "Move this popup while positioning mode is active.")
    toast.amount:SetText("+1 XP")
    toast.meta:SetText(placementMode and (L.RENOWN_TOAST_TEST_META_MOVE or "Drag to place, then close the test popup.") or (L.RENOWN_TOAST_CHARACTER_PROGRESS or "Character progress"))
    toast.icon:SetTexture(PREMIUM.renownToastTask or PREMIUM.renownToastEmblem or PREMIUM.statusBadgeTurnin or PREMIUM.statusBadgeOpen)
    local style = applyRenownToastStyle(toast, getRenownToastStyle())
    local animation = getRenownToastAnimation()
    toast:SetAlpha(placementMode and 1 or 0)
    setRenownToastVisualScale(toast, placementMode and 1 or getRenownToastStartScale(animation))
    toast:Show()

    if placementMode then
        toast:SetMovable(true)
        toast:SetClampedToScreen(true)
        toast:EnableMouse(true)
        toast:RegisterForDrag("LeftButton")
        toast:SetScript("OnDragStart", function(selfToast)
            selfToast:StartMoving()
        end)
        toast:SetScript("OnDragStop", function(selfToast)
            selfToast:StopMovingOrSizing()
            UI:StoreRenownToastPosition()
        end)
    else
        toast:SetMovable(false)
        toast:EnableMouse(false)
        toast:RegisterForDrag()
        toast:SetScript("OnDragStart", nil)
        toast:SetScript("OnDragStop", nil)
        toast.elapsed = 0
        toast.duration = 2.35
        toast:SetScript("OnUpdate", function(selfToast, elapsed)
            selfToast.elapsed = (selfToast.elapsed or 0) + (elapsed or 0)
            local t = selfToast.elapsed
            local fadeIn = 0.18
            local fadeOut = 0.30
            local duration = selfToast.duration or 2.35
            if t < fadeIn then
                local ratio = t / fadeIn
                selfToast:SetAlpha(ratio)
                setRenownToastVisualScale(selfToast, getRenownToastFadeInScale(animation, ratio))
            elseif t < duration then
                selfToast:SetAlpha(1)
                setRenownToastVisualScale(selfToast, 1)
            elseif t < duration + fadeOut then
                selfToast:SetAlpha(1 - ((t - duration) / fadeOut))
                setRenownToastVisualScale(selfToast, getRenownToastFadeOutScale(animation, (t - duration) / fadeOut))
            else
                selfToast:SetScript("OnUpdate", nil)
                selfToast:Hide()
                selfToast:SetAlpha(0)
                setRenownToastVisualScale(selfToast, 1)
            end
            if selfToast.backGlow and style == "normal" and animation == "soft" then
                local pulse = math.sin((t or 0) * 5) * 0.025
                selfToast.backGlow:SetVertexColor(THEME.shadow[1], THEME.shadow[2], THEME.shadow[3], 0.34 + pulse)
            end
        end)
    end
end

local function playSoundKitCandidate(soundKey, fallbackKey)
    if type(PlaySound) ~= "function" or type(SOUNDKIT) ~= "table" then
        return false
    end

    local sound = SOUNDKIT[soundKey or ""] or SOUNDKIT[fallbackKey or ""] or SOUNDKIT.LEVEL_UP
    if sound then
        pcall(PlaySound, sound, "Master")
        return true
    end
    return false
end

local function playRenownToastSound(rankUp, levelUp, warbandLevelUp)
    if warbandLevelUp then
        playSoundKitCandidate("UI_BONUS_LOOT_ROLL_START", "LEVEL_UP")
    elseif rankUp or levelUp then
        playSoundKitCandidate("LEVEL_UP", "UI_70_BOOST_THANKSFORPLAYING_SMALLER")
    else
        playSoundKitCandidate("IG_QUEST_LIST_COMPLETE", "UI_QUEST_ROLLING_FORWARD_01")
    end
end

function UI:ProcessRenownToastQueue()
    if ns.IsRenownSystemEnabled and not ns:IsRenownSystemEnabled() then
        self:HideRenownToast()
        return
    end
    if self.renownToastPlacement == true then
        return
    end
    if ns.GetRenownToastVisible and not ns:GetRenownToastVisible() then
        self:HideRenownToast()
        return
    end

    if self.renownToastActive then
        return
    end

    local queue = self.renownToastQueue
    local entry = queue and table.remove(queue, 1) or nil
    if type(entry) ~= "table" then
        return
    end

    local toast = self:EnsureRenownToast()
    if not toast then
        self.renownToastActive = false
        return
    end

    self.renownToastActive = true
    local amount = math.max(0, math.floor(tonumber(entry.amount) or 0))
    local characterLevelUp = hasRenownLevelAdvanced(entry.levelBefore, entry.levelAfter, entry.paragonBefore, entry.paragonAfter)
    local warbandLevelUp = hasRenownLevelAdvanced(entry.warbandLevelBefore, entry.warbandLevelAfter, entry.warbandParagonBefore, entry.warbandParagonAfter)
    local rankUp = type(entry.rankAfter) == "string" and entry.rankAfter ~= "" and entry.rankAfter ~= entry.rankBefore
    local levelUp = characterLevelUp or warbandLevelUp

    if warbandLevelUp then
        toast.title:SetText(string.format(L.RENOWN_TOAST_WARBAND_LEVEL_FORMAT or "Warband level %d", tonumber(entry.warbandLevelAfter) or 1))
    elseif rankUp then
        toast.title:SetText(string.format(L.RENOWN_TOAST_RANK_UP_FORMAT or "New rank: %s", entry.rankAfter))
    elseif characterLevelUp then
        toast.title:SetText(string.format(L.RENOWN_TOAST_LEVEL_UP_FORMAT or "Chronicle level %d", tonumber(entry.levelAfter) or 1))
    else
        toast.title:SetText(L.RENOWN_TOAST_TASK_DONE or "Task complete")
    end

    toast.detail:SetText(entry.label or L.RENOWN_TOAST_ACTIVITY or "Activity complete")
    toast.amount:SetText(string.format("+%d XP", amount))

    if warbandLevelUp then
        toast.meta:SetText(string.format(L.RENOWN_TOAST_WARBAND_LEVEL_FORMAT or "Warband level %d", tonumber(entry.warbandLevelAfter) or 1))
    elseif characterLevelUp then
        toast.meta:SetText(entry.rankAfter or (L.RENOWN_TOAST_CHARACTER_PROGRESS or "Character progress"))
    else
        toast.meta:SetText(L.RENOWN_TOAST_CHARACTER_PROGRESS or "Character progress")
    end

    local toastIcon = PREMIUM.renownToastTask or PREMIUM.renownToastEmblem or PREMIUM.statusBadgeTurnin or PREMIUM.statusBadgeOpen
    if warbandLevelUp then
        toastIcon = PREMIUM.renownToastWarband or PREMIUM.renownToastLevel or toastIcon
    elseif rankUp then
        toastIcon = PREMIUM.renownToastRank or toastIcon
    elseif characterLevelUp or warbandLevelUp then
        toastIcon = PREMIUM.renownToastLevel or toastIcon
    end
    toast.icon:SetTexture(toastIcon)
    local style = applyRenownToastStyle(toast, getRenownToastStyle())
    local animation = getRenownToastAnimation()
    playRenownToastSound(rankUp, levelUp, warbandLevelUp)
    self:ApplyRenownToastPosition(toast)
    toast:Show()
    toast:SetAlpha(0)
    setRenownToastVisualScale(toast, getRenownToastStartScale(animation))
    toast.elapsed = 0
    toast.duration = (rankUp or levelUp) and 3.55 or 2.95
    toast:SetScript("OnUpdate", function(selfToast, elapsed)
        selfToast.elapsed = (selfToast.elapsed or 0) + (elapsed or 0)
        local t = selfToast.elapsed
        local fadeIn = 0.22
        local fadeOut = 0.36
        local duration = selfToast.duration or 2.65
        if t < fadeIn then
            local ratio = t / fadeIn
            selfToast:SetAlpha(ratio)
            setRenownToastVisualScale(selfToast, getRenownToastFadeInScale(animation, ratio))
        elseif t < duration then
            selfToast:SetAlpha(1)
            setRenownToastVisualScale(selfToast, 1)
        elseif t < duration + fadeOut then
            local ratio = (t - duration) / fadeOut
            selfToast:SetAlpha(1 - ratio)
            setRenownToastVisualScale(selfToast, getRenownToastFadeOutScale(animation, ratio))
        else
            selfToast:SetScript("OnUpdate", nil)
            selfToast:Hide()
            selfToast:SetAlpha(0)
            setRenownToastVisualScale(selfToast, 1)
            self.renownToastActive = false
            self:ProcessRenownToastQueue()
        end
        if selfToast.backGlow and style == "normal" and animation == "soft" then
            local pulse = math.sin((t or 0) * 5) * 0.025
            selfToast.backGlow:SetVertexColor(THEME.shadow[1], THEME.shadow[2], THEME.shadow[3], 0.34 + pulse)
        end
    end)
end

function UI:ShowRenownToast(entry)
    if ns.IsRenownSystemEnabled and not ns:IsRenownSystemEnabled() then
        self:HideRenownToast()
        return
    end
    if ns.GetRenownToastVisible and not ns:GetRenownToastVisible() then
        self:HideRenownToast()
        return
    end

    if type(entry) ~= "table" or (tonumber(entry.amount) or 0) <= 0 then
        return
    end

    self.renownToastQueue = self.renownToastQueue or {}
    self.renownToastQueue[#self.renownToastQueue + 1] = entry
    self:ProcessRenownToastQueue()
end

function UI:RefreshVaultCard(character, snapshot)
    if UIVaultView.RefreshCard then
        UIVaultView.RefreshCard(self, character, snapshot, getVaultViewHelpers())
    end
end

function UI:RefreshMythicPlusCard(character, view)
    if UIMythicPlusCard.Refresh then
        UIMythicPlusCard.Refresh(self, character, view, getMythicPlusCardHelpers())
    end
end

function UI:RefreshHousingCard(character, view)
    if UIHousingCard.Refresh then
        UIHousingCard.Refresh(self, character, view, getHousingCardHelpers())
    end
end

function UI:RefreshContent()
    if not self.frame then
        return
    end

    local currentCharacter = ns:GetCurrentCharacterInfo()
    local selectedCharacter = ns:GetSelectedCharacter() or currentCharacter
    local character = selectedCharacter
    local transientMode = self.GetTransientPanelMode and self:GetTransientPanelMode() or nil

    if transientMode then
        self:RefreshWishlistButton(character)
        self:ApplyWorkbenchLayout(transientMode)
        if transientMode == "wishlist" then
            self:RefreshWishlistCard(character)
        end
        return
    end

    self.frame.sidebar:Show()
    self.frame.content:Show()
    self.frame.utility:Show()
    if self.frame.settingsPanel then
        self.frame.settingsPanel:Hide()
    end
    if self.frame.featuresPanel then
        self.frame.featuresPanel:Hide()
    end
    if self.frame.wishlistCard then
        self.frame.wishlistCard:Hide()
    end

    local selectedMainTab = ns:GetSelectedMainTab()
    local selectedSubTab = ns:GetSelectedSubTab(selectedMainTab)
    local wishlistOpen = self.wishlistPanelOpen == true
    local isHousingMode = selectedMainTab == "housing" and selectedSubTab == "endeavors" and not wishlistOpen
    local isChronicleMode = selectedMainTab == "chronicle" and not wishlistOpen
    local isCompendiumMode = selectedMainTab == "compendium" and not wishlistOpen
    local snapshot = ns:GetLiveVaultSnapshot(character)
    local mainLabel
    local subLabel
    local isJournalMode = isJournalSubTab(selectedMainTab, selectedSubTab)
    local isMythicPlusMode = selectedMainTab == "mythicplus" and selectedSubTab == "season1" and not wishlistOpen
    local journalView
    local mythicPlusView
    local housingView
    if isJournalMode and not wishlistOpen then
        if selectedMainTab == "dungeons" then
            if selectedSubTab == "season1" then
                journalView = buildSeasonOneDungeonJournalView(character)
            else
                journalView = buildMidnightDungeonJournalView(character)
            end
        else
            journalView = buildMidnightRaidJournalView(character)
        end
    elseif isMythicPlusMode then
        mythicPlusView = buildMythicPlusSeasonOneView(character)
    elseif isHousingMode then
        housingView = buildHousingEndeavorsView(character)
    end
    local roleLabel = ns:IsLiveMainCharacter(character.key) and (L.ROLE_MAIN or "Main") or (L.ROLE_ALT or "Alt")
    local classR, classG, classB = getClassColor(character.classFile or character.classToken or character.classFilename)

    local function applyHeroCharacterHeader()
        local titleText = string.format("%s-%s", character.name or (L.UNKNOWN or "Unknown"), character.realm or (L.UNKNOWN or "Unknown"))
        self.frame.heroTitle:SetText(titleText)
        self.frame.heroTitle:SetTextColor(classR, classG, classB, 1)

        local hasProfessionBadges = refreshHeroProfessionBadges(self.frame, character)
        if hasProfessionBadges and self.frame.heroProfessionBadgeFrame then
            local titleWidth = self.frame.heroTitle.GetStringWidth and math.ceil(self.frame.heroTitle:GetStringWidth()) or 0
            local badgeFrame = self.frame.heroProfessionBadgeFrame
            local badgeWidth = badgeFrame.GetWidth and badgeFrame:GetWidth() or 111
            local portraitRight = self.frame.heroPortraitBackplate.GetRight and self.frame.heroPortraitBackplate:GetRight() or nil
            local rankLeft = self.frame.heroRankBadge.GetLeft and self.frame.heroRankBadge:GetLeft() or nil
            local maxOffset = nil
            if portraitRight and rankLeft then
                maxOffset = math.max(160, rankLeft - portraitRight - badgeWidth - 20)
            end
            local badgeOffset = titleWidth + 32
            if maxOffset then
                badgeOffset = math.min(badgeOffset, maxOffset)
            end

            badgeFrame:ClearAllPoints()
            badgeFrame:SetPoint("TOPLEFT", self.frame.heroPortraitBackplate, "TOPRIGHT", badgeOffset, -2)
        end

        self.frame.heroTitle:ClearAllPoints()
        self.frame.heroTitle:SetPoint("TOPLEFT", self.frame.heroPortraitBackplate, "TOPRIGHT", 18, -4)
        if hasProfessionBadges and self.frame.heroProfessionBadgeFrame then
            self.frame.heroTitle:SetPoint("TOPRIGHT", self.frame.heroProfessionBadgeFrame, "LEFT", -8, -4)
        else
            self.frame.heroTitle:SetPoint("TOPRIGHT", self.frame.heroRankBadge, "LEFT", -18, -4)
        end
        self.frame.heroSubtitle:ClearAllPoints()
        self.frame.heroSubtitle:SetPoint("TOPLEFT", self.frame.heroTitle, "BOTTOMLEFT", 0, -8)
        self.frame.heroSubtitle:SetPoint("TOPRIGHT", self.frame.heroRankBadge, "LEFT", -18, 0)
        if character.itemLevel and character.itemLevel > 0 then
            self.frame.heroSubtitle:SetText(string.format(L.HERO_META_WITH_ILVL or "%s  |  Level %d  |  %s  |  ilvl %d", character.className or (L.UNKNOWN or "Unknown"), character.level or 0, roleLabel, character.itemLevel))
        else
            self.frame.heroSubtitle:SetText(string.format(L.HERO_META or "%s  |  Level %d  |  %s", character.className or (L.UNKNOWN or "Unknown"), character.level or 0, roleLabel))
        end

        local money = tonumber(character.money)
        if ns.GetCurrentMoney
            and ns.IsCurrentCharacterKey
            and ns:IsCurrentCharacterKey(character.key)
        then
            local liveMoney = ns:GetCurrentMoney()
            if liveMoney ~= nil then
                money = liveMoney
            end
        end
        local moneyText = formatMoneyText(money, 12)
        if moneyText and self.frame.heroMoney then
            self.frame.heroMoney:ClearAllPoints()
            self.frame.heroMoney:SetPoint("TOPLEFT", self.frame.heroSubtitle, "BOTTOMLEFT", 0, -5)
            self.frame.heroMoney:SetPoint("TOPRIGHT", self.frame.heroRankBadge, "LEFT", -18, 0)
            self.frame.heroMoney:SetText(string.format(L.HERO_MONEY or "Gold %s", moneyText))
            self.frame.heroMoney:SetTextColor(0.92, 0.92, 0.88, 1)
            self.frame.heroMoney:Show()
            self.frame.heroCharacter:ClearAllPoints()
            self.frame.heroCharacter:SetPoint("TOPLEFT", self.frame.heroMoney, "BOTTOMLEFT", 0, -4)
            self.frame.heroCharacter:SetPoint("TOPRIGHT", self.frame.heroRankBadge, "LEFT", -18, 0)
        else
            if self.frame.heroMoney then
                self.frame.heroMoney:Hide()
            end
            self.frame.heroCharacter:ClearAllPoints()
            self.frame.heroCharacter:SetPoint("TOPLEFT", self.frame.heroSubtitle, "BOTTOMLEFT", 0, -8)
            self.frame.heroCharacter:SetPoint("TOPRIGHT", self.frame.heroRankBadge, "LEFT", -18, 0)
        end
        self.frame.heroCharacter:SetText(string.format(L.HERO_SYNC or "Last sync %s", formatLastSeen(character.lastSeen)))
    end

    if self.frame.heroPortrait then
        local isCurrentHeroCharacter = type(ns.IsCurrentCharacterKey) == "function"
            and type(character) == "table"
            and ns:IsCurrentCharacterKey(character.key)
            or false
        if not isCurrentHeroCharacter and type(character) == "table" and type(currentCharacter) == "table" then
            isCurrentHeroCharacter = character.name == currentCharacter.name and character.realm == currentCharacter.realm
        end
        if isCurrentHeroCharacter and type(SetPortraitTexture) == "function" then
            self.frame.heroPortrait:SetTexCoord(0, 1, 0, 1)
            SetPortraitTexture(self.frame.heroPortrait, "player")
        else
            setClassIconTexture(self.frame.heroPortrait, character.classFile or character.classToken or character.classFilename)
        end
    end
    self:LayoutVaultSlotCards()

    for _, tabData in ipairs(MAIN_TABS) do
        if tabData.key == selectedMainTab then
            mainLabel = tabData.label()
            break
        end
    end

    for _, subTabData in ipairs(SUB_TABS[selectedMainTab] or {}) do
        if subTabData.key == selectedSubTab then
            subLabel = subTabData.label()
            break
        end
    end

    self:ApplyRaidModeLayout(
        (isJournalMode and not wishlistOpen and "journal")
            or (isMythicPlusMode and "mythicplus")
            or (isHousingMode and "housing")
            or (isChronicleMode and "chronicle")
            or (isCompendiumMode and "compendium")
            or false
    )
    self:ApplyContentNavigationLayout(selectedMainTab)
    applyHeroCharacterHeader()
    if self.frame.heroPortraitRing then
        local portraitFrameCharacter = character
        if ns.GetRenownScope and ns:GetRenownScope() == "warband" and ns.GetCurrentCharacterInfo then
            local currentInfo = ns:GetCurrentCharacterInfo()
            if type(currentInfo) == "table" and type(currentInfo.key) == "string" and currentInfo.key ~= "" then
                portraitFrameCharacter = currentInfo
            end
        end
        local frameState = ns.GetRenownSelectedPortraitFrame and ns:GetRenownSelectedPortraitFrame(portraitFrameCharacter) or { visible = true, index = nil }
        local frameTexture = nil
        if frameState.visible ~= false and frameState.index and PREMIUM.renownFrames then
            frameTexture = PREMIUM.renownFrames[tonumber(frameState.index)]
        end
        if frameState.visible == false then
            self.frame.heroPortraitRing:Hide()
        else
            self.frame.heroPortraitRing:SetTexture(frameTexture or PREMIUM.classRing)
            self.frame.heroPortraitRing:Show()
        end
        if self.frame.heroRankBadge then
            local badgeState = ns.GetRenownSelectedBadge and ns:GetRenownSelectedBadge(portraitFrameCharacter) or { visible = true, index = nil }
            if badgeState.visible == false then
                self.frame.heroRankBadge:Hide()
            else
                local badgeTexture = nil
                if badgeState.index and PREMIUM.renownBadges then
                    badgeTexture = PREMIUM.renownBadges[tonumber(badgeState.index)]
                end
                self.frame.heroRankBadge:SetTexture(badgeTexture or getRenownBadgeTexture(portraitFrameCharacter))
                self.frame.heroRankBadge:Show()
            end
        end
    elseif self.frame.heroRankBadge then
        local badgeState = ns.GetRenownSelectedBadge and ns:GetRenownSelectedBadge(character) or { visible = true, index = nil }
        if badgeState.visible == false then
            self.frame.heroRankBadge:Hide()
        else
            local badgeTexture = nil
            if badgeState.index and PREMIUM.renownBadges then
                badgeTexture = PREMIUM.renownBadges[tonumber(badgeState.index)]
            end
            self.frame.heroRankBadge:SetTexture(badgeTexture or getRenownBadgeTexture(character))
            self.frame.heroRankBadge:Show()
        end
    end
    if isChronicleMode or isCompendiumMode then
        if self.frame.heroRankBadge then
            self.frame.heroRankBadge:Hide()
        end
        if self.frame.heroPortraitBackplate then
            self.frame.heroPortraitBackplate:Hide()
        end
        if self.frame.heroPortrait then
            self.frame.heroPortrait:Hide()
        end
        if self.frame.heroPortraitRing then
            self.frame.heroPortraitRing:Hide()
        end
    end
    if not ((isJournalMode and not wishlistOpen) or isMythicPlusMode or isHousingMode or isChronicleMode or isCompendiumMode) then
        self:RefreshUtility(character)
    end
    self:RefreshWishlistButton(character)

    self.frame.pvePreyCard:Hide()
    if self.frame.pveRaresCard then
        self.frame.pveRaresCard:Hide()
    end
    self.frame.pveWeeklyCard:Hide()
    if self.frame.customTaskCard then
        self.frame.customTaskCard:Hide()
    end
    if self.frame.renownCard then
        self.frame.renownCard:Hide()
    end
    if self.frame.compendiumCard then
        self.frame.compendiumCard:Hide()
    end
    self.frame.professionCooldownCard:Hide()
    self.frame.raidModeCenter:Hide()
    self.frame.wishlistCard:Hide()
    if self.frame.mythicPlusSidebar then
        self.frame.mythicPlusSidebar:Hide()
    end
    if self.frame.mythicPlusCenter then
        self.frame.mythicPlusCenter:Hide()
    end
    if self.frame.mythicPlusUtility then
        self.frame.mythicPlusUtility:Hide()
    end
    if self.frame.housingSidebar then
        self.frame.housingSidebar:Hide()
    end
    if self.frame.housingCenter then
        self.frame.housingCenter:Hide()
    end
    if self.frame.housingUtility then
        self.frame.housingUtility:Hide()
    end

    if wishlistOpen then
        self.frame.contentTitle:Hide()
        self.frame.contentSubtitle:Hide()
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Hide()
        self.frame.raidModeCenter:Hide()
        if self.frame.housingSidebar then
            self.frame.housingSidebar:Hide()
        end
        if self.frame.housingCenter then
            self.frame.housingCenter:Hide()
        end
        if self.frame.housingUtility then
            self.frame.housingUtility:Hide()
        end
        self.frame.wishlistCard:Show()
        self:RefreshWishlistCard(character)
    elseif selectedMainTab == "focus" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(L.CUSTOM_TASKS_QUESTBOARD_TITLE or "Questboard")
        self.frame.contentSubtitle:SetText(L.CUSTOM_TASKS_SUBTITLE or "Choose the Vaultloom rows you want to keep in focus for this character.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Hide()
        if self.frame.customTaskCard then
            self.frame.customTaskCard:Show()
        end
        self:RefreshCustomTasksCard(character, snapshot)
    elseif selectedMainTab == "chronicle" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(L.RENOWN_TAB_TITLE or "Chronik")
        self.frame.contentSubtitle:SetText(L.RENOWN_TAB_SUBTITLE or "Vaultloom Ruhm, badges, character progress, warband progress, and recent XP gains.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Hide()
        if self.frame.customTaskCard then
            self.frame.customTaskCard:Hide()
        end
        if ns.ScanRenownProgress then
            ns:ScanRenownProgress(character, snapshot, { force = true })
        end
        if self.frame.renownCard then
            self.frame.renownCard:ClearAllPoints()
            self.frame.renownCard:SetPoint("TOPLEFT", self.frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
            self.frame.renownCard:SetPoint("BOTTOMRIGHT", -14, 18)
            self.frame.renownCard:Show()
        end
        self:RefreshRenownCard(character)
    elseif selectedMainTab == "compendium" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(L.COMPENDIUM_TAB_TITLE or "Kompendium")
        self.frame.contentSubtitle:SetText(L.COMPENDIUM_TAB_SUBTITLE or "Midnight mounts, pets, toys, decorations, recipes, and rares in one searchable collection.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Hide()
        if self.frame.customTaskCard then
            self.frame.customTaskCard:Hide()
        end
        if self.frame.renownCard then
            self.frame.renownCard:Hide()
        end
        if self.frame.compendiumCard then
            self.frame.compendiumCard:ClearAllPoints()
            self.frame.compendiumCard:SetPoint("TOPLEFT", self.frame.contentSubtitle, "BOTTOMLEFT", 0, -16)
            self.frame.compendiumCard:SetPoint("BOTTOMRIGHT", -14, 18)
            self.frame.compendiumCard:Show()
        end
        self:RefreshCompendiumCard(character)
    elseif selectedMainTab == "vault" then
        self.frame.contentTitle:Hide()
        self.frame.contentSubtitle:Hide()
        self.frame.vaultCard:ClearAllPoints()
        if self.UsesVerticalSubTabs and self:UsesVerticalSubTabs(selectedMainTab) and self.frame.verticalSubTabRail then
            self.frame.vaultCard:SetPoint("TOPLEFT", self.frame.verticalSubTabRail, "TOPRIGHT", 14, 0)
        elseif self.UsesHorizontalSubTabs and self:UsesHorizontalSubTabs(selectedMainTab) and self.frame.subTabBar then
            self.frame.vaultCard:SetPoint("TOPLEFT", self.frame.subTabBar, "BOTTOMLEFT", 0, -16)
        else
            self.frame.vaultCard:SetPoint("TOPLEFT", self.frame.heroPlate, "BOTTOMLEFT", 14, -20)
        end
        self.frame.vaultCard:SetPoint("BOTTOMRIGHT", -14, 18)
        self:LayoutVaultSlotCards()
        self.frame.placeholderBox:Hide()
        self.frame.vaultCard:Show()
        self:RefreshVaultCard(character, snapshot)
    elseif isJournalMode then
        self.frame.contentTitle:Hide()
        self.frame.contentSubtitle:Hide()
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Hide()
        self.frame.raidModeCenter:ClearAllPoints()
        if self.UsesVerticalSubTabs and self:UsesVerticalSubTabs(selectedMainTab) and self.frame.verticalSubTabRail then
            self.frame.raidModeCenter:SetPoint("TOPLEFT", self.frame.verticalSubTabRail, "TOPRIGHT", 14, 0)
        elseif self.UsesHorizontalSubTabs and self:UsesHorizontalSubTabs(selectedMainTab) and self.frame.subTabBar then
            self.frame.raidModeCenter:SetPoint("TOPLEFT", self.frame.subTabBar, "BOTTOMLEFT", 0, -16)
        else
            self.frame.raidModeCenter:SetPoint("TOPLEFT", self.frame.heroPlate, "BOTTOMLEFT", 0, -16)
        end
        self.frame.raidModeCenter:SetPoint("BOTTOMRIGHT", -14, 18)
        self.frame.raidModeCenter:Show()
        self:RefreshRaidMode(character, journalView)
    elseif selectedMainTab == "mythicplus" and selectedSubTab == "season1" then
        self.frame.contentTitle:Hide()
        self.frame.contentSubtitle:Hide()
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Hide()
        self.frame.raidModeCenter:Hide()
        self.frame.wishlistCard:Hide()
        if self.frame.mythicPlusSidebar then
            self.frame.mythicPlusSidebar:Show()
        end
        if self.frame.mythicPlusCenter then
            self.frame.mythicPlusCenter:Show()
        end
        if self.frame.mythicPlusUtility then
            self.frame.mythicPlusUtility:Show()
        end
        self:RefreshMythicPlusCard(character, mythicPlusView)
    elseif selectedMainTab == "housing" and selectedSubTab == "endeavors" then
        self.frame.contentTitle:Hide()
        self.frame.contentSubtitle:Hide()
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Hide()
        self.frame.raidModeCenter:Hide()
        self.frame.wishlistCard:Hide()
        if self.frame.housingSidebar then
            self.frame.housingSidebar:Show()
        end
        if self.frame.housingCenter then
            self.frame.housingCenter:Show()
        end
        if self.frame.housingUtility then
            self.frame.housingUtility:Show()
        end
        self:RefreshHousingCard(character, housingView)
    elseif selectedMainTab == "pve" and selectedSubTab == "prey" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVE_PREY_SUBTITLE or "Weekly prey progress, split by difficulty, plus the linked weekly quest.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.pvePreyCard:Show()
        self:RefreshPvePreyCard(snapshot)
    elseif selectedMainTab == "pve" and selectedSubTab == "rares" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVE_RARES_CONTENT_SUBTITLE or "Midnight rare mobs, daily kill status, mount collection, and Blizzard waypoints.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        if self.frame.pveRaresCard then
            self.frame.pveRaresCard:Show()
        end
        self:RefreshPveRaresCard(snapshot)
    elseif selectedMainTab == "pve" and selectedSubTab == "weekly" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVE_WEEKLY_SUBTITLE or "Core weekly quests and event objectives for the selected character.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshPveWeeklyCard(snapshot)
    elseif selectedMainTab == "pve" and selectedSubTab == "delves" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVE_DELVES_SUBTITLE or "Delve systems and weekly caps for the selected character.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshPveDelvesCard(snapshot)
    elseif selectedMainTab == "pve" and selectedSubTab == "daily" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVE_DAILY_CONTENT_SUBTITLE or "Daily activities and reset status.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshPveDailyCard(snapshot)
    elseif selectedMainTab == "pve" and selectedSubTab == "darkmoon" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVE_DARKMOON_CONTENT_SUBTITLE or "Darkmoon Faire dailies and weekly turn-ins.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshPveDarkmoonCard(snapshot)
    elseif selectedMainTab == "pve" and selectedSubTab == "events" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVE_EVENTS_CONTENT_SUBTITLE or "Events and weekly quests.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshPveEventsCard(snapshot)
    elseif selectedMainTab == "pve" and selectedSubTab == "world" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVE_WORLD_CONTENT_SUBTITLE or "World progress and weekly caps.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshPveWorldCard(snapshot)
    elseif selectedMainTab == "pve" and selectedSubTab == "other" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.CONTENT_SUBTITLE or "This is a placeholder content area for the selected section.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshPveOtherCard(snapshot)
    elseif selectedMainTab == "pvp" and selectedSubTab == "weekly" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PVP_WEEKLY_SUBTITLE or "Weekly PvP quests from Zerella in Silvermoon for the selected character.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshPvpWeeklyCard(snapshot)
    elseif selectedMainTab == "systems" and selectedSubTab == "professions" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PROFESSIONS_SUBTITLE or "Midnight profession weeklies for the selected character.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Show()
        self:RefreshProfessionWeeklyCard(snapshot)
    elseif selectedMainTab == "systems" and selectedSubTab == "cooldowns" then
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.PROFESSION_COOLDOWNS_SUBTITLE or "Crafting cooldowns and concentration from the last profession scan.")
        self.frame.vaultCard:Hide()
        self.frame.placeholderBox:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Show()
        self:RefreshProfessionCooldownCard()
    else
        self.frame.contentTitle:Show()
        self.frame.contentSubtitle:Show()
        self.frame.contentTitle:SetText(string.format(
            L.CONTENT_TITLE_FORMAT or "%s / %s",
            mainLabel or selectedMainTab,
            subLabel or selectedSubTab
        ))
        self.frame.contentSubtitle:SetText(L.CONTENT_SUBTITLE or "This is a placeholder content area for the selected section.")
        self.frame.vaultCard:Hide()
        self.frame.pvePreyCard:Hide()
        self.frame.pveWeeklyCard:Hide()
        self.frame.professionCooldownCard:Hide()
        self.frame.placeholderBox:Show()
        self.frame.placeholderText:SetText(string.format(
            "%s\n\n%s",
            L.CONTENT_PLACEHOLDER or "",
            L.OPEN_COMMAND_HINT or ""
        ))
    end
end
