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

local UIData = ns.UIData or {}
local PREMIUM = UIData.PREMIUM or {}
local THEME = UIData.THEME or {}
local STATUS_COLORS = UIData.STATUS_COLORS or {}
local UI_LAYOUT = UIData.UI_LAYOUT or {}
local WEEKLY_STATUS_ROW_HEIGHT = UIData.WEEKLY_STATUS_ROW_HEIGHT or 34
local WEEKLY_STATUS_ROW_GAP = UIData.WEEKLY_STATUS_ROW_GAP or 8

local UIMythicPlusView = ns.UIMythicPlusView or {}
local buildMythicPlusSeasonOneView = UIMythicPlusView.BuildSeasonOneView or function()
    return {
        available = false,
        message = L.MYTHIC_PLUS_UNAVAILABLE or "Mythic+ data is not available yet.",
    }
end
local getMythicPlusScoreColor = UIMythicPlusView.GetScoreColor or function()
    local color = THEME.parchment or { 0.93, 0.89, 0.77 }
    return color[1], color[2], color[3]
end

local UIMythicPlusCard = {}
ns.UIMythicPlusCard = UIMythicPlusCard

local BACKDROP_TEMPLATE = BackdropTemplateMixin and "BackdropTemplate" or nil
local MYTHIC_PLUS_CENTER_DUNGEONS = "dungeons"
local MYTHIC_PLUS_CENTER_WARBAND_KEYS = "warbandKeys"

local function getMythicPlusCenterMode()
    local ui = ns.db and ns.db.ui
    if type(ui) == "table" and ui.mythicPlusCenterMode == MYTHIC_PLUS_CENTER_WARBAND_KEYS then
        return MYTHIC_PLUS_CENTER_WARBAND_KEYS
    end
    return MYTHIC_PLUS_CENTER_DUNGEONS
end

local function setMythicPlusCenterMode(mode)
    if not ns.db or type(ns.db.ui) ~= "table" then
        return
    end

    ns.db.ui.mythicPlusCenterMode = mode == MYTHIC_PLUS_CENTER_WARBAND_KEYS
        and MYTHIC_PLUS_CENTER_WARBAND_KEYS
        or MYTHIC_PLUS_CENTER_DUNGEONS

    if ns.UI and ns.UI.RefreshMythicPlusCard then
        local character = ns.GetSelectedCharacter and ns:GetSelectedCharacter() or nil
        ns.UI:RefreshMythicPlusCard(character)
    elseif ns.UI and ns.UI.Refresh then
        ns.UI:Refresh()
    end
end

local function formatMythicPlusTimeAgo(timestamp)
    timestamp = tonumber(timestamp) or 0
    if timestamp <= 0 then
        return L.UNKNOWN or "Unknown"
    end

    local elapsed = math.max(0, (time and time() or timestamp) - timestamp)
    if elapsed < 60 then
        return L.TIME_JUST_NOW or "just now"
    elseif elapsed < 3600 then
        return string.format(L.TIME_MIN or "%d m", math.floor(elapsed / 60))
    elseif elapsed < 86400 then
        return string.format(L.TIME_HOUR_MIN or "%d h %d m", math.floor(elapsed / 3600), math.floor((elapsed % 3600) / 60))
    end
    return string.format(L.TIME_DAY_HOUR or "%d d %d h", math.floor(elapsed / 86400), math.floor((elapsed % 86400) / 3600))
end

local function getMythicPlusKeyLevelColor(level)
    level = tonumber(level) or 0
    if level >= 15 then
        return 1.00, 0.55, 0.18
    elseif level >= 10 then
        return 0.73, 0.44, 0.98
    elseif level >= 7 then
        return 0.24, 0.72, 1.00
    elseif level > 0 then
        return 0.34, 0.86, 0.52
    end
    return THEME.parchment[1], THEME.parchment[2], THEME.parchment[3]
end

local function getSnapshotKeyInfo(snapshot)
    if type(snapshot) ~= "table" then
        return 0, nil
    end

    local currentKey = type(snapshot.currentKey) == "table" and snapshot.currentKey or nil
    local level = tonumber(currentKey and currentKey.level) or 0
    local name = currentKey and currentKey.name or nil
    local summaryKey = snapshot.summary and snapshot.summary.key or nil

    if level <= 0 and type(summaryKey) == "table" then
        local value = tostring(summaryKey.value or "")
        level = tonumber(value:match("%+%s*(%d+)")) or 0
    end
    if (not name or name == "") and type(summaryKey) == "table" then
        name = summaryKey.meta
    end

    return level, name
end

local function getSnapshotScore(snapshot)
    local scoreEntry = snapshot and snapshot.summary and snapshot.summary.score or nil
    return tonumber(scoreEntry and (scoreEntry.score or scoreEntry.value)) or 0
end

local function buildWarbandKeyEntries(activeView)
    local characters = ns.GetLiveVaultloomCharacters and ns:GetLiveVaultloomCharacters() or {}
    local entries = {}
    local selected = ns.GetSelectedCharacter and ns:GetSelectedCharacter() or nil
    local selectedKey = selected and selected.key

    for _, character in ipairs(characters) do
        if type(character) == "table" and type(character.key) == "string" and character.key ~= "" then
            local snapshot
            if ns.IsCurrentCharacterKey and ns:IsCurrentCharacterKey(character.key) then
                snapshot = buildMythicPlusSeasonOneView(character)
                if not (snapshot and snapshot.available) then
                    snapshot = ns.GetStoredMythicPlusSnapshot and ns:GetStoredMythicPlusSnapshot(character) or nil
                end
            elseif activeView and selectedKey == character.key then
                snapshot = activeView
            else
                snapshot = ns.GetStoredMythicPlusSnapshot and ns:GetStoredMythicPlusSnapshot(character) or nil
            end

            local keyLevel, keyName = getSnapshotKeyInfo(snapshot)
            local score = getSnapshotScore(snapshot)
            entries[#entries + 1] = {
                character = character,
                snapshot = snapshot,
                keyLevel = keyLevel,
                keyName = keyName,
                score = score,
                updatedAt = tonumber(snapshot and snapshot.updatedAt) or 0,
                selected = selectedKey == character.key,
                current = ns.IsCurrentCharacterKey and ns:IsCurrentCharacterKey(character.key),
            }
        end
    end

    table.sort(entries, function(a, b)
        if (a.keyLevel or 0) ~= (b.keyLevel or 0) then
            return (a.keyLevel or 0) > (b.keyLevel or 0)
        end
        if (a.score or 0) ~= (b.score or 0) then
            return (a.score or 0) > (b.score or 0)
        end
        return tostring(a.character and a.character.name or "") < tostring(b.character and b.character.name or "")
    end)

    return entries
end

local function createMythicPlusStatCard(parent, helpers)
    local createSectionFrame = helpers.createSectionFrame
    local createLabel = helpers.createLabel

    local card = createSectionFrame(parent, "cardInset")
    card:SetHeight(76)

    card.label = createLabel(card, "GameFontHighlightSmall", "LEFT")
    card.label:SetPoint("TOPLEFT", 12, -12)
    card.label:SetPoint("TOPRIGHT", -12, -12)

    card.value = createLabel(card, "GameFontNormalLarge", "LEFT")
    card.value:SetPoint("TOPLEFT", card.label, "BOTTOMLEFT", 0, -6)
    card.value:SetPoint("TOPRIGHT", -12, 0)

    card.meta = createLabel(card, "GameFontDisableSmall", "LEFT")
    card.meta:SetPoint("TOPLEFT", card.value, "BOTTOMLEFT", 0, -4)
    card.meta:SetPoint("TOPRIGHT", -12, 0)
    if card.meta.SetWordWrap then
        card.meta:SetWordWrap(false)
    end

    return card
end

local function applyMythicPlusStatCard(card, entry)
    if not card then
        return
    end

    entry = type(entry) == "table" and entry or {}
    card.label:SetText(entry.label or "")
    card.value:SetText(entry.value or "")
    card.meta:SetText(entry.meta or "")

    local valueR, valueG, valueB = THEME.gold[1], THEME.gold[2], THEME.gold[3]
    if entry.score then
        valueR, valueG, valueB = getMythicPlusScoreColor(entry.score)
    end
    card.value:SetTextColor(valueR, valueG, valueB, 1)
end

local function createMythicPlusModeSidebar(parent, helpers)
    local createLabel = helpers.createLabel
    local createMiniActionButton = helpers.createMiniActionButton
    local createSectionFrame = helpers.createSectionFrame
    local createUtilityHeader = helpers.createUtilityHeader
    local createWeeklyStatusRow = helpers.createWeeklyStatusRow

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

    panel.title = createLabel(panel, "GameFontNormalLarge", "LEFT")
    panel.title:SetPoint("TOPLEFT", 16, -16)
    panel.title:SetText(L.MAIN_TAB_MYTHICPLUS or "Mythic+")

    panel.subtitle = createLabel(panel, "GameFontHighlightSmall", "LEFT")
    panel.subtitle:SetPoint("TOPLEFT", panel.title, "BOTTOMLEFT", 0, -8)
    panel.subtitle:SetPoint("TOPRIGHT", -16, 0)
    panel.subtitle:SetWordWrap(true)
    panel.subtitle:SetText("")
    panel.subtitle:Hide()

    panel.emptyText = createLabel(panel, "GameFontDisable", "LEFT")
    panel.emptyText:SetPoint("TOPLEFT", panel.title, "BOTTOMLEFT", 0, -14)
    panel.emptyText:SetPoint("BOTTOMRIGHT", -16, 16)
    panel.emptyText:SetJustifyV("TOP")
    panel.emptyText:SetWordWrap(true)
    panel.emptyText:Hide()

    panel.keyHeader = createUtilityHeader(panel, L.MYTHIC_PLUS_SUMMARY_KEY or "Current Key")
    panel.keyHeader:SetPoint("TOPLEFT", panel.title, "BOTTOMLEFT", 0, -28)
    panel.keyHeader:SetPoint("TOPRIGHT", -16, 0)

    panel.keyPanel = createSectionFrame(panel, "cardInset")
    panel.keyPanel:SetPoint("TOPLEFT", panel.keyHeader, "BOTTOMLEFT", 0, -8)
    panel.keyPanel:SetPoint("TOPRIGHT", panel.keyHeader, "TOPRIGHT", 0, -8)
    panel.keyPanel:SetHeight(UI_LAYOUT.mythicPlusKeyPanelHeight)

    panel.keyValue = createLabel(panel.keyPanel, "GameFontNormalLarge", "LEFT")
    panel.keyValue:SetPoint("TOPLEFT", 14, -14)
    panel.keyValue:SetPoint("TOPRIGHT", -14, -14)

    panel.keyMeta = createLabel(panel.keyPanel, "GameFontHighlightSmall", "LEFT")
    panel.keyMeta:SetPoint("TOPLEFT", panel.keyValue, "BOTTOMLEFT", 0, -8)
    panel.keyMeta:SetPoint("TOPRIGHT", -14, 0)
    panel.keyMeta:SetWordWrap(true)

    panel.weekLabel = createLabel(panel.keyPanel, "GameFontDisableSmall", "LEFT")
    panel.weekLabel:SetPoint("TOPLEFT", panel.keyMeta, "BOTTOMLEFT", 0, -14)
    panel.weekLabel:SetText(L.MYTHIC_PLUS_THIS_WEEK or "This week")

    panel.weekValue = createLabel(panel.keyPanel, "GameFontHighlightSmall", "RIGHT")
    panel.weekValue:SetPoint("TOPLEFT", 120, -86)
    panel.weekValue:SetPoint("TOPRIGHT", -14, -86)

    panel.vaultLabel = createLabel(panel.keyPanel, "GameFontDisableSmall", "LEFT")
    panel.vaultLabel:SetPoint("TOPLEFT", panel.weekLabel, "BOTTOMLEFT", 0, -14)
    panel.vaultLabel:SetText(L.MYTHIC_PLUS_SUMMARY_VAULT or "Great Vault")

    panel.vaultValue = createLabel(panel.keyPanel, "GameFontHighlightSmall", "RIGHT")
    panel.vaultValue:SetPoint("TOPLEFT", 120, -112)
    panel.vaultValue:SetPoint("TOPRIGHT", -14, -112)

    panel.warbandKeysButton = createMiniActionButton(panel, L.MYTHIC_PLUS_WARBAND_KEYS or "Warband Keys", 236)
    panel.warbandKeysButton:SetPoint("TOPLEFT", panel.keyPanel, "BOTTOMLEFT", 0, -12)
    panel.warbandKeysButton:SetPoint("TOPRIGHT", panel.keyPanel, "BOTTOMRIGHT", 0, -12)
    panel.warbandKeysButton:SetHeight(28)
    panel.warbandKeysButton:SetScript("OnClick", function()
        local nextMode = getMythicPlusCenterMode() == MYTHIC_PLUS_CENTER_WARBAND_KEYS
            and MYTHIC_PLUS_CENTER_DUNGEONS
            or MYTHIC_PLUS_CENTER_WARBAND_KEYS
        setMythicPlusCenterMode(nextMode)
    end)
    panel.warbandKeysButton:SetScript("OnEnter", function(selfButton)
        GameTooltip:SetOwner(selfButton, "ANCHOR_CURSOR_RIGHT")
        GameTooltip:AddLine(L.MYTHIC_PLUS_WARBAND_KEYS or "Warband Keys", THEME.gold[1], THEME.gold[2], THEME.gold[3], true)
        GameTooltip:AddLine(L.MYTHIC_PLUS_WARBAND_KEYS_TOOLTIP or "Shows saved keystones for your Warband. Log into each character once to capture its key.", 0.86, 0.86, 0.82, true)
        GameTooltip:Show()
    end)
    panel.warbandKeysButton:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    panel.affixHeader = createUtilityHeader(panel, L.MYTHIC_PLUS_SECTION_AFFIXES or "Current Affixes")
    panel.affixHeader:SetPoint("TOPLEFT", panel.warbandKeysButton, "BOTTOMLEFT", 0, -16)
    panel.affixHeader:SetPoint("TOPRIGHT", -16, 0)

    panel.affixPanel = createSectionFrame(panel, "cardInset")
    panel.affixPanel:SetPoint("TOPLEFT", panel.affixHeader, "BOTTOMLEFT", 0, -8)
    panel.affixPanel:SetPoint("BOTTOMRIGHT", -16, 16)

    panel.affixRows = {}
    for index = 1, 4 do
        local row = createWeeklyStatusRow(panel.affixPanel)
        if index == 1 then
            row:SetPoint("TOPLEFT", 14, -14)
            row:SetPoint("TOPRIGHT", -14, -14)
        else
            row:SetPoint("TOPLEFT", panel.affixRows[index - 1], "BOTTOMLEFT", 0, -8)
            row:SetPoint("TOPRIGHT", panel.affixRows[index - 1], "BOTTOMRIGHT", 0, -8)
        end
        row:Hide()
        panel.affixRows[index] = row
    end

    return panel
end

local function createMythicPlusModeCenter(parent, helpers)
    local createLabel = helpers.createLabel
    local createSectionFrame = helpers.createSectionFrame
    local styleScrollFrame = helpers.styleScrollFrame

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

    card.emptyText = createLabel(card, "GameFontDisableLarge", "LEFT")
    card.emptyText:SetPoint("TOPLEFT", 0, 0)
    card.emptyText:SetPoint("BOTTOMRIGHT", 0, 0)
    card.emptyText:SetJustifyV("TOP")
    card.emptyText:SetWordWrap(true)
    card.emptyText:Hide()

    card.summaryPanel = createSectionFrame(card, "card")
    card.summaryPanel:SetPoint("TOPLEFT", 0, 0)
    card.summaryPanel:SetPoint("TOPRIGHT", 0, 0)
    card.summaryPanel:SetHeight(UI_LAYOUT.mythicPlusSummaryPanelHeight)

    card.summaryPanel.title = createLabel(card.summaryPanel, "GameFontNormalLarge", "LEFT")
    card.summaryPanel.title:SetPoint("TOPLEFT", 14, -14)
    card.summaryPanel.title:SetText(L.MYTHIC_PLUS_TITLE or "Mythic+ Season 1")

    card.summaryPanel.subtitle = createLabel(card.summaryPanel, "GameFontHighlightSmall", "LEFT")
    card.summaryPanel.subtitle:SetPoint("TOPLEFT", card.summaryPanel.title, "BOTTOMLEFT", 0, -6)
    card.summaryPanel.subtitle:SetPoint("TOPRIGHT", -14, 0)
    card.summaryPanel.subtitle:SetWordWrap(true)
    card.summaryPanel.subtitle:SetText(L.MYTHIC_PLUS_CENTER_SUBTITLE or "Season 1 dungeon pool with your best season and weekly runs.")

    card.statCards = {}
    for index = 1, 3 do
        local statCard = createMythicPlusStatCard(card.summaryPanel, helpers)
        statCard:Hide()
        card.statCards[index] = statCard
    end

    card.dungeonsPanel = createSectionFrame(card, "cardInset")
    card.dungeonsPanel:SetPoint("TOPLEFT", card.summaryPanel, "BOTTOMLEFT", 0, -14)
    card.dungeonsPanel:SetPoint("BOTTOMRIGHT", 0, 0)
    card.dungeonsPanel:Hide()

    card.dungeonsPanel.title = createLabel(card.dungeonsPanel, "GameFontNormalLarge", "LEFT")
    card.dungeonsPanel.title:SetPoint("TOPLEFT", 14, -14)
    card.dungeonsPanel.title:SetText(L.MYTHIC_PLUS_SECTION_DUNGEONS or "Season Dungeons")

    card.dungeonsPanel.subtitle = createLabel(card.dungeonsPanel, "GameFontHighlightSmall", "LEFT")
    card.dungeonsPanel.subtitle:SetPoint("TOPLEFT", card.dungeonsPanel.title, "BOTTOMLEFT", 0, -6)
    card.dungeonsPanel.subtitle:SetPoint("TOPRIGHT", -14, 0)
    card.dungeonsPanel.subtitle:SetWordWrap(true)
    card.dungeonsPanel.subtitle:SetText(L.MYTHIC_PLUS_SECTION_DUNGEONS_SUBTITLE or "Best season and weekly runs for the active pool.")

    card.dungeonsPanel.scroll = CreateFrame("ScrollFrame", nil, card.dungeonsPanel, "UIPanelScrollFrameTemplate")
    card.dungeonsPanel.scroll:SetPoint("TOPLEFT", card.dungeonsPanel.subtitle, "BOTTOMLEFT", 0, -12)
    card.dungeonsPanel.scroll:SetPoint("BOTTOMRIGHT", -24, 12)

    card.dungeonsPanel.scrollChild = CreateFrame("Frame", nil, card.dungeonsPanel.scroll)
    card.dungeonsPanel.scrollChild:SetSize(520, 10)
    card.dungeonsPanel.scroll:SetScrollChild(card.dungeonsPanel.scrollChild)
    styleScrollFrame(card.dungeonsPanel.scroll)

    card.dungeonsPanel.empty = createLabel(card.dungeonsPanel.scrollChild, "GameFontDisable", "LEFT")
    card.dungeonsPanel.empty:SetPoint("TOPLEFT", 4, -4)
    card.dungeonsPanel.empty:SetPoint("TOPRIGHT", -4, -4)
    card.dungeonsPanel.empty:SetJustifyV("TOP")
    card.dungeonsPanel.empty:SetWordWrap(true)
    card.dungeonsPanel.empty:SetText(L.MYTHIC_PLUS_DUNGEONS_EMPTY or "No Season 1 dungeons found yet.")

    card.dungeonRows = {}

    card.warbandKeysPanel = createSectionFrame(card, "cardInset")
    card.warbandKeysPanel:SetPoint("TOPLEFT", card.summaryPanel, "BOTTOMLEFT", 0, -14)
    card.warbandKeysPanel:SetPoint("BOTTOMRIGHT", 0, 0)
    card.warbandKeysPanel:Hide()

    card.warbandKeysPanel.title = createLabel(card.warbandKeysPanel, "GameFontNormalLarge", "LEFT")
    card.warbandKeysPanel.title:SetPoint("TOPLEFT", 14, -14)
    card.warbandKeysPanel.title:SetText(L.MYTHIC_PLUS_WARBAND_KEYS_TITLE or "Warband Keys")

    card.warbandKeysPanel.subtitle = createLabel(card.warbandKeysPanel, "GameFontHighlightSmall", "LEFT")
    card.warbandKeysPanel.subtitle:SetPoint("TOPLEFT", card.warbandKeysPanel.title, "BOTTOMLEFT", 0, -6)
    card.warbandKeysPanel.subtitle:SetPoint("TOPRIGHT", -14, 0)
    card.warbandKeysPanel.subtitle:SetWordWrap(true)
    card.warbandKeysPanel.subtitle:SetText(L.MYTHIC_PLUS_WARBAND_KEYS_SUBTITLE or "Saved keystones for your visible characters.")

    card.warbandKeysPanel.scroll = CreateFrame("ScrollFrame", nil, card.warbandKeysPanel, "UIPanelScrollFrameTemplate")
    card.warbandKeysPanel.scroll:SetPoint("TOPLEFT", card.warbandKeysPanel.subtitle, "BOTTOMLEFT", 0, -12)
    card.warbandKeysPanel.scroll:SetPoint("BOTTOMRIGHT", -24, 12)

    card.warbandKeysPanel.scrollChild = CreateFrame("Frame", nil, card.warbandKeysPanel.scroll)
    card.warbandKeysPanel.scrollChild:SetSize(520, 10)
    card.warbandKeysPanel.scroll:SetScrollChild(card.warbandKeysPanel.scrollChild)
    styleScrollFrame(card.warbandKeysPanel.scroll)

    card.warbandKeysPanel.empty = createLabel(card.warbandKeysPanel.scrollChild, "GameFontDisable", "LEFT")
    card.warbandKeysPanel.empty:SetPoint("TOPLEFT", 4, -4)
    card.warbandKeysPanel.empty:SetPoint("TOPRIGHT", -4, -4)
    card.warbandKeysPanel.empty:SetJustifyV("TOP")
    card.warbandKeysPanel.empty:SetWordWrap(true)
    card.warbandKeysPanel.empty:SetText(L.MYTHIC_PLUS_WARBAND_KEYS_EMPTY or "No saved keys yet. Log into each character once to capture its current keystone.")

    card.warbandKeyRows = {}
    return card
end

local function createMythicPlusModeUtility(parent, helpers)
    local createLabel = helpers.createLabel
    local createSectionFrame = helpers.createSectionFrame
    local createUtilityHeader = helpers.createUtilityHeader
    local styleScrollFrame = helpers.styleScrollFrame

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

    panel.title = createLabel(panel, "GameFontNormalLarge", "LEFT")
    panel.title:SetPoint("TOPLEFT", 16, -16)
    panel.title:SetText(L.MYTHIC_PLUS_UTILITY_TITLE or "Runs & goals")

    panel.subtitle = createLabel(panel, "GameFontHighlightSmall", "LEFT")
    panel.subtitle:SetPoint("TOPLEFT", panel.title, "BOTTOMLEFT", 0, -8)
    panel.subtitle:SetPoint("TOPRIGHT", -16, 0)
    panel.subtitle:SetWordWrap(true)
    panel.subtitle:SetText(L.MYTHIC_PLUS_UTILITY_SUBTITLE or "Latest pushes and seasonal milestones for the active character.")

    panel.emptyText = createLabel(panel, "GameFontDisable", "LEFT")
    panel.emptyText:SetPoint("TOPLEFT", panel.subtitle, "BOTTOMLEFT", 0, -14)
    panel.emptyText:SetPoint("BOTTOMRIGHT", -16, 16)
    panel.emptyText:SetJustifyV("TOP")
    panel.emptyText:SetWordWrap(true)
    panel.emptyText:Hide()

    panel.recentHeader = createUtilityHeader(panel, L.MYTHIC_PLUS_SECTION_RECENT or "Recent Runs")
    panel.recentHeader:SetPoint("TOPLEFT", panel.subtitle, "BOTTOMLEFT", 0, -14)
    panel.recentHeader:SetPoint("TOPRIGHT", -16, 0)

    panel.recentPanel = createSectionFrame(panel, "cardInset")
    panel.recentPanel:SetPoint("TOPLEFT", panel.recentHeader, "BOTTOMLEFT", 0, -8)
    panel.recentPanel:SetPoint("TOPRIGHT", panel.recentHeader, "TOPRIGHT", 0, -8)
    panel.recentPanel:SetHeight(230)

    panel.recentPanel.subtitle = createLabel(panel.recentPanel, "GameFontHighlightSmall", "LEFT")
    panel.recentPanel.subtitle:SetPoint("TOPLEFT", 14, -14)
    panel.recentPanel.subtitle:SetPoint("TOPRIGHT", -14, -14)
    panel.recentPanel.subtitle:SetWordWrap(true)
    panel.recentPanel.subtitle:SetText(L.MYTHIC_PLUS_SECTION_RECENT_SUBTITLE or "Latest recorded runs from this season.")

    panel.recentPanel.scroll = CreateFrame("ScrollFrame", nil, panel.recentPanel, "UIPanelScrollFrameTemplate")
    panel.recentPanel.scroll:SetPoint("TOPLEFT", panel.recentPanel.subtitle, "BOTTOMLEFT", 0, -12)
    panel.recentPanel.scroll:SetPoint("BOTTOMRIGHT", -24, 12)

    panel.recentPanel.scrollChild = CreateFrame("Frame", nil, panel.recentPanel.scroll)
    panel.recentPanel.scrollChild:SetSize(250, 10)
    panel.recentPanel.scroll:SetScrollChild(panel.recentPanel.scrollChild)
    styleScrollFrame(panel.recentPanel.scroll)

    panel.recentPanel.empty = createLabel(panel.recentPanel.scrollChild, "GameFontDisable", "LEFT")
    panel.recentPanel.empty:SetPoint("TOPLEFT", 4, -4)
    panel.recentPanel.empty:SetPoint("TOPRIGHT", -4, -4)
    panel.recentPanel.empty:SetJustifyV("TOP")
    panel.recentPanel.empty:SetWordWrap(true)
    panel.recentPanel.empty:SetText(L.MYTHIC_PLUS_RECENT_EMPTY or "No recent Mythic+ runs recorded for this season yet.")

    panel.goalsHeader = createUtilityHeader(panel, L.MYTHIC_PLUS_SECTION_REWARDS or "Goals")
    panel.goalsHeader:SetPoint("TOPLEFT", panel.recentPanel, "BOTTOMLEFT", 0, -16)
    panel.goalsHeader:SetPoint("TOPRIGHT", -16, 0)

    panel.goalsPanel = createSectionFrame(panel, "cardInset")
    panel.goalsPanel:SetPoint("TOPLEFT", panel.goalsHeader, "BOTTOMLEFT", 0, -8)
    panel.goalsPanel:SetPoint("BOTTOMRIGHT", -16, 16)

    panel.goalsPanel.subtitle = createLabel(panel.goalsPanel, "GameFontHighlightSmall", "LEFT")
    panel.goalsPanel.subtitle:SetPoint("TOPLEFT", 14, -14)
    panel.goalsPanel.subtitle:SetPoint("TOPRIGHT", -14, -14)
    panel.goalsPanel.subtitle:SetWordWrap(true)
    panel.goalsPanel.subtitle:SetText(L.MYTHIC_PLUS_SECTION_REWARDS_SUBTITLE or "Season milestones and rewards.")

    panel.goalsPanel.scroll = CreateFrame("ScrollFrame", nil, panel.goalsPanel, "UIPanelScrollFrameTemplate")
    panel.goalsPanel.scroll:SetPoint("TOPLEFT", panel.goalsPanel.subtitle, "BOTTOMLEFT", 0, -12)
    panel.goalsPanel.scroll:SetPoint("BOTTOMRIGHT", -24, 12)

    panel.goalsPanel.scrollChild = CreateFrame("Frame", nil, panel.goalsPanel.scroll)
    panel.goalsPanel.scrollChild:SetSize(250, 10)
    panel.goalsPanel.scroll:SetScrollChild(panel.goalsPanel.scrollChild)
    styleScrollFrame(panel.goalsPanel.scroll)

    panel.recentRows = {}
    panel.goalRows = {}
    return panel
end

local function createMythicPlusDungeonRow(parent, helpers)
    local createLabel = helpers.createLabel
    local setTextureColor = helpers.setTextureColor

    local row = CreateFrame("Frame", nil, parent, BACKDROP_TEMPLATE)
    row:SetHeight(76)

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

    row.bg = row:CreateTexture(nil, "BACKGROUND")
    row.bg:SetAllPoints()
    row.bg:SetTexture(PREMIUM.row)
    row.bg:SetTexCoord(0.022, 0.978, 0, 1)
    setTextureColor(row.bg, { 1, 1, 1 }, 0.94)

    row.fill = row:CreateTexture(nil, "BACKGROUND", nil, 1)
    row.fill:SetAllPoints()
    row.fill:SetTexture("Interface\\Buttons\\WHITE8X8")
    row.fill:SetColorTexture(THEME.metalInset[1], THEME.metalInset[2], THEME.metalInset[3], 0.03)

    row.hover = row:CreateTexture(nil, "BACKGROUND", nil, 2)
    row.hover:SetAllPoints()
    row.hover:SetTexture("Interface\\Buttons\\WHITE8X8")
    row.hover:SetColorTexture(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.08)
    row.hover:SetAlpha(0)

    row.statusLine = row:CreateTexture(nil, "ARTWORK")
    row.statusLine:SetPoint("TOPLEFT", 4, -5)
    row.statusLine:SetPoint("BOTTOMLEFT", 4, 5)
    row.statusLine:SetWidth(3)
    row.statusLine:SetColorTexture(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.9)

    row.iconBackplate = row:CreateTexture(nil, "ARTWORK")
    row.iconBackplate:SetSize(42, 42)
    row.iconBackplate:SetPoint("LEFT", 12, 0)
    row.iconBackplate:SetTexture(PREMIUM.classBackplate)

    row.icon = row:CreateTexture(nil, "OVERLAY")
    row.icon:SetSize(36, 36)
    row.icon:SetPoint("CENTER", row.iconBackplate, "CENTER", 0, 0)
    row.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    row.icon:SetTexture(PREMIUM.minimapLogo)
    if row.icon.AddMaskTexture then
        row.iconMask = row:CreateMaskTexture()
        row.iconMask:SetTexture("Interface\\CHARACTERFRAME\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
        row.iconMask:SetAllPoints(row.icon)
        row.icon:AddMaskTexture(row.iconMask)
    end

    row.iconRing = row:CreateTexture(nil, "OVERLAY", nil, 1)
    row.iconRing:SetSize(42, 42)
    row.iconRing:SetPoint("CENTER", row.iconBackplate, "CENTER", 0, 0)
    row.iconRing:SetTexture(PREMIUM.classRing)

    row.title = createLabel(row, "GameFontNormal", "LEFT")
    row.title:SetPoint("TOPLEFT", row.iconBackplate, "TOPRIGHT", 12, -8)
    row.title:SetPoint("TOPRIGHT", -90, -8)
    if row.title.SetWordWrap then
        row.title:SetWordWrap(false)
    end

    row.best = createLabel(row, "GameFontHighlightSmall", "LEFT")
    row.best:SetPoint("TOPLEFT", row.title, "BOTTOMLEFT", 0, -6)
    row.best:SetPoint("TOPRIGHT", -90, 0)
    row.best:SetTextColor(THEME.parchment[1], THEME.parchment[2], THEME.parchment[3], 0.94)
    if row.best.SetWordWrap then
        row.best:SetWordWrap(false)
    end

    row.meta = createLabel(row, "GameFontDisableSmall", "LEFT")
    row.meta:SetPoint("TOPLEFT", row.best, "BOTTOMLEFT", 0, -5)
    row.meta:SetPoint("TOPRIGHT", -90, 0)
    row.meta:SetTextColor(THEME.parchment[1], THEME.parchment[2], THEME.parchment[3], 0.72)
    if row.meta.SetWordWrap then
        row.meta:SetWordWrap(false)
    end

    row.score = createLabel(row, "GameFontHighlight", "RIGHT")
    row.score:SetPoint("TOPRIGHT", -12, -10)
    row.score:SetWidth(78)

    row.status = createLabel(row, "GameFontHighlightSmall", "RIGHT")
    row.status:SetPoint("TOPRIGHT", row.score, "BOTTOMRIGHT", 0, -6)
    row.status:SetPoint("LEFT", row.title, "RIGHT", 10, 0)

    row.hitbox = CreateFrame("Button", nil, row)
    row.hitbox:SetAllPoints()
    row.hitbox:SetFrameLevel((row:GetFrameLevel() or 1) + 1)
    row.hitbox:SetScript("OnEnter", function()
        row.hover:SetAlpha(1)
        if row.tooltipTitle or (row.tooltipLines and #row.tooltipLines > 0) then
            GameTooltip:SetOwner(row.hitbox, "ANCHOR_CURSOR_RIGHT")
            if row.tooltipTitle and row.tooltipTitle ~= "" then
                GameTooltip:AddLine(row.tooltipTitle, 1, 0.82, 0.18, true)
            end
            for _, line in ipairs(row.tooltipLines or {}) do
                if type(line) == "string" and line ~= "" then
                    GameTooltip:AddLine(line, 0.92, 0.92, 0.92, true)
                end
            end
            GameTooltip:Show()
        end
    end)
    row.hitbox:SetScript("OnLeave", function()
        row.hover:SetAlpha(0)
        GameTooltip:Hide()
    end)

    return row
end

local function applyMythicPlusDungeonRow(row, entry)
    if not row then
        return
    end

    if not entry then
        row:Hide()
        return
    end

    local statusKey = entry.bestStatus == "timed" and "complete"
        or (entry.bestStatus == "overtime" and "turnin")
        or "missing"
    local statusColor = STATUS_COLORS[statusKey] or STATUS_COLORS.missing
    local scoreR, scoreG, scoreB = getMythicPlusScoreColor(entry.score)

    row.title:SetText(entry.name or "")
    row.best:SetText(entry.bestText or "")
    row.meta:SetText(entry.weeklyText and entry.splitText and (entry.weeklyText .. "  |  " .. entry.splitText) or (entry.weeklyText or entry.splitText or ""))
    row.score:SetText((tonumber(entry.score) or 0) > 0 and tostring(math.floor((tonumber(entry.score) or 0) + 0.5)) or "--")
    row.score:SetTextColor(scoreR, scoreG, scoreB, 1)
    row.status:SetText(entry.bestStatus == "timed" and (L.MYTHIC_PLUS_TIMED or "Timed")
        or (entry.bestStatus == "overtime" and (L.MYTHIC_PLUS_OVERTIME or "Over time"))
        or "--")
    row.status:SetTextColor(statusColor[1], statusColor[2], statusColor[3], 1)
    row.statusLine:SetColorTexture(statusColor[1], statusColor[2], statusColor[3], 0.95)
    row.tooltipTitle = entry.tooltipTitle
    row.tooltipLines = entry.tooltipLines

    if entry.texture then
        row.icon:SetTexture(entry.texture)
    else
        row.icon:SetTexture(PREMIUM.minimapLogo)
    end

    row:Show()
end

local function createMythicPlusWarbandKeyRow(parent, helpers)
    return createMythicPlusDungeonRow(parent, helpers)
end

local function applyMythicPlusWarbandKeyRow(row, entry, helpers)
    if not row then
        return
    end
    if type(entry) ~= "table" or type(entry.character) ~= "table" then
        row:Hide()
        return
    end

    local character = entry.character
    local keyLevel = tonumber(entry.keyLevel) or 0
    local score = tonumber(entry.score) or 0
    local keyName = type(entry.keyName) == "string" and entry.keyName ~= "" and entry.keyName or nil
    local className = character.className or L.UNKNOWN or "Unknown"
    local level = tonumber(character.level) or 0
    local itemLevel = tonumber(character.itemLevel) or 0
    local keyR, keyG, keyB = getMythicPlusKeyLevelColor(keyLevel)
    local classR, classG, classB = helpers.getClassColor(character.classFile or character.classToken or character.classFilename)

    row.title:SetText(character.name or character.key or (L.UNKNOWN or "Unknown"))
    row.title:SetTextColor(classR, classG, classB, 1)

    if keyLevel > 0 then
        row.best:SetText(string.format("%s  +%d", keyName or (L.UNKNOWN or "Unknown"), keyLevel))
    elseif entry.snapshot then
        row.best:SetText(L.MYTHIC_PLUS_KEY_NONE or "No keystone")
    else
        row.best:SetText(L.MYTHIC_PLUS_WARBAND_KEYS_NO_SNAPSHOT or "No saved snapshot")
    end

    local metaParts = {}
    if className and className ~= "" then
        metaParts[#metaParts + 1] = className
    end
    if level > 0 then
        metaParts[#metaParts + 1] = string.format(L.SIDEBAR_ROW_LEVEL or "Level %d", level)
    end
    if itemLevel > 0 then
        metaParts[#metaParts + 1] = string.format(L.SIDEBAR_ROW_ILVL or "ilvl %d", itemLevel)
    end
    if (tonumber(entry.updatedAt) or 0) > 0 then
        metaParts[#metaParts + 1] = string.format(L.MYTHIC_PLUS_WARBAND_KEYS_CAPTURED or "Captured %s", formatMythicPlusTimeAgo(entry.updatedAt))
    end
    row.meta:SetText(table.concat(metaParts, "  |  "))

    row.score:SetText(keyLevel > 0 and string.format("+%d", keyLevel) or "--")
    row.score:SetTextColor(keyR, keyG, keyB, 1)
    row.status:SetText(score > 0 and string.format("%s %d", L.MYTHIC_PLUS_SUMMARY_SCORE or "Score", math.floor(score + 0.5)) or "--")
    if score > 0 then
        local scoreR, scoreG, scoreB = getMythicPlusScoreColor(score)
        row.status:SetTextColor(scoreR, scoreG, scoreB, 1)
    else
        row.status:SetTextColor(THEME.parchment[1], THEME.parchment[2], THEME.parchment[3], 0.72)
    end
    row.statusLine:SetColorTexture(keyR, keyG, keyB, keyLevel > 0 and 0.95 or 0.42)

    if row.fill then
        if entry.selected then
            row.fill:SetColorTexture(THEME.selected[1], THEME.selected[2], THEME.selected[3], 0.09)
        elseif entry.current then
            row.fill:SetColorTexture(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.05)
        else
            row.fill:SetColorTexture(THEME.metalInset[1], THEME.metalInset[2], THEME.metalInset[3], 0.03)
        end
    end
    if row.SetBackdropBorderColor then
        if entry.selected then
            row:SetBackdropBorderColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 0.72)
        else
            row:SetBackdropBorderColor(THEME.goldDim[1], THEME.goldDim[2], THEME.goldDim[3], 0.32)
        end
    end

    if helpers.setClassIconTexture then
        helpers.setClassIconTexture(row.icon, character.classFile or character.classToken or character.classFilename)
    end

    row.tooltipTitle = character.name or character.key or (L.UNKNOWN or "Unknown")
    row.tooltipLines = {
        keyLevel > 0 and string.format("%s: %s +%d", L.MYTHIC_PLUS_SUMMARY_KEY or "Current Key", keyName or (L.UNKNOWN or "Unknown"), keyLevel) or (L.MYTHIC_PLUS_KEY_NONE or "No keystone"),
    }
    if score > 0 then
        row.tooltipLines[#row.tooltipLines + 1] = string.format("%s: %d", L.MYTHIC_PLUS_SUMMARY_SCORE or "Score", math.floor(score + 0.5))
    end
    if (tonumber(entry.updatedAt) or 0) > 0 then
        row.tooltipLines[#row.tooltipLines + 1] = string.format(L.MYTHIC_PLUS_WARBAND_KEYS_CAPTURED or "Captured %s", formatMythicPlusTimeAgo(entry.updatedAt))
    end
    row.tooltipLines[#row.tooltipLines + 1] = L.MYTHIC_PLUS_WARBAND_KEYS_TOOLTIP or "Shows saved keystones for your Warband. Log into each character once to capture its key."
    if row.hitbox then
        row.hitbox:SetScript("OnClick", function()
            if ns.SetSelectedCharacterKey and character.key then
                ns:SetSelectedCharacterKey(character.key)
            end
        end)
    end

    row:Show()
end

local function layoutMythicPlusCard(ui)
    if not ui.frame then
        return
    end

    local center = ui.frame.mythicPlusCenter
    if center and center.summaryPanel then
        local gap = 12
        local statHeight = UI_LAYOUT.mythicPlusStatCardHeight
        local innerWidth = math.max(420, (center.summaryPanel:GetWidth() or 0) - 28)
        local statWidth = math.floor((innerWidth - (gap * 2)) / 3)
        statWidth = math.max(136, statWidth)

        local statsAnchor = (center.summaryPanel.subtitle and center.summaryPanel.subtitle:IsShown())
            and center.summaryPanel.subtitle
            or center.summaryPanel.title
        local statsOffset = (center.summaryPanel.subtitle and center.summaryPanel.subtitle:IsShown()) and -12 or -18

        for index, statCard in ipairs(center.statCards or {}) do
            statCard:ClearAllPoints()
            statCard:SetSize(statWidth, statHeight)
            if index == 1 then
                statCard:SetPoint("TOPLEFT", statsAnchor, "BOTTOMLEFT", 0, statsOffset)
            else
                statCard:SetPoint("TOPLEFT", center.statCards[index - 1], "TOPRIGHT", gap, 0)
            end
        end

        if center.dungeonsPanel and center.dungeonsPanel.scrollChild then
            local width = math.max(100, math.floor((center.dungeonsPanel.scroll:GetWidth() or (center.dungeonsPanel:GetWidth() - 34))))
            center.dungeonsPanel.scrollChild:SetWidth(width)
        end
        if center.warbandKeysPanel and center.warbandKeysPanel.scrollChild then
            local width = math.max(100, math.floor((center.warbandKeysPanel.scroll:GetWidth() or (center.warbandKeysPanel:GetWidth() - 34))))
            center.warbandKeysPanel.scrollChild:SetWidth(width)
        end
    end

    local utility = ui.frame.mythicPlusUtility
    if utility and utility.recentPanel and utility.goalsPanel then
        local availableHeight = math.max(360, (utility:GetHeight() or 0) - 220)
        local recentHeight = math.max(210, math.floor(availableHeight * 0.46))
        utility.recentPanel:SetHeight(recentHeight)

        if utility.recentPanel.scrollChild then
            local recentWidth = math.max(100, math.floor((utility.recentPanel.scroll:GetWidth() or (utility.recentPanel:GetWidth() - 34))))
            utility.recentPanel.scrollChild:SetWidth(recentWidth)
        end
        if utility.goalsPanel.scrollChild then
            local goalsWidth = math.max(100, math.floor((utility.goalsPanel.scroll:GetWidth() or (utility.goalsPanel:GetWidth() - 34))))
            utility.goalsPanel.scrollChild:SetWidth(goalsWidth)
        end
    end
end

local function ensureMythicPlusDungeonRows(ui, count, helpers)
    if not ui.frame or not ui.frame.mythicPlusCenter then
        return
    end

    local center = ui.frame.mythicPlusCenter
    while #center.dungeonRows < count do
        local row = createMythicPlusDungeonRow(center.dungeonsPanel.scrollChild, helpers)
        local index = #center.dungeonRows + 1
        if index == 1 then
            row:SetPoint("TOPLEFT", 0, 0)
            row:SetPoint("TOPRIGHT", 0, 0)
        else
            row:SetPoint("TOPLEFT", center.dungeonRows[index - 1], "BOTTOMLEFT", 0, -10)
            row:SetPoint("TOPRIGHT", center.dungeonRows[index - 1], "BOTTOMRIGHT", 0, -10)
        end
        center.dungeonRows[index] = row
    end
end

local function ensureMythicPlusWarbandKeyRows(ui, count, helpers)
    if not ui.frame or not ui.frame.mythicPlusCenter then
        return
    end

    local center = ui.frame.mythicPlusCenter
    while #center.warbandKeyRows < count do
        local row = createMythicPlusWarbandKeyRow(center.warbandKeysPanel.scrollChild, helpers)
        local index = #center.warbandKeyRows + 1
        if index == 1 then
            row:SetPoint("TOPLEFT", 0, 0)
            row:SetPoint("TOPRIGHT", 0, 0)
        else
            row:SetPoint("TOPLEFT", center.warbandKeyRows[index - 1], "BOTTOMLEFT", 0, -10)
            row:SetPoint("TOPRIGHT", center.warbandKeyRows[index - 1], "BOTTOMRIGHT", 0, -10)
        end
        center.warbandKeyRows[index] = row
    end
end

local function ensureMythicPlusRecentRows(ui, count, helpers)
    if not ui.frame or not ui.frame.mythicPlusUtility then
        return
    end

    local utility = ui.frame.mythicPlusUtility
    while #utility.recentRows < count do
        local row = helpers.createWeeklyStatusRow(utility.recentPanel.scrollChild)
        local index = #utility.recentRows + 1
        if index == 1 then
            row:SetPoint("TOPLEFT", 0, 0)
            row:SetPoint("TOPRIGHT", 0, 0)
        else
            row:SetPoint("TOPLEFT", utility.recentRows[index - 1], "BOTTOMLEFT", 0, -8)
            row:SetPoint("TOPRIGHT", utility.recentRows[index - 1], "BOTTOMRIGHT", 0, -8)
        end
        row:Hide()
        utility.recentRows[index] = row
    end
end

local function ensureMythicPlusGoalRows(ui, count, helpers)
    if not ui.frame or not ui.frame.mythicPlusUtility then
        return
    end

    local utility = ui.frame.mythicPlusUtility
    while #utility.goalRows < count do
        local row = helpers.createWeeklyStatusRow(utility.goalsPanel.scrollChild)
        local index = #utility.goalRows + 1
        if index == 1 then
            row:SetPoint("TOPLEFT", 0, 0)
            row:SetPoint("TOPRIGHT", 0, 0)
        else
            row:SetPoint("TOPLEFT", utility.goalRows[index - 1], "BOTTOMLEFT", 0, -8)
            row:SetPoint("TOPRIGHT", utility.goalRows[index - 1], "BOTTOMRIGHT", 0, -8)
        end
        row:Hide()
        utility.goalRows[index] = row
    end
end

local function refreshMythicPlusCard(ui, character, view, helpers)
    if not ui.frame or not ui.frame.mythicPlusSidebar or not ui.frame.mythicPlusCenter or not ui.frame.mythicPlusUtility then
        return
    end

    local sidebar = ui.frame.mythicPlusSidebar
    local center = ui.frame.mythicPlusCenter
    local utility = ui.frame.mythicPlusUtility
    layoutMythicPlusCard(ui)
    local centerMode = getMythicPlusCenterMode()

    view = view or buildMythicPlusSeasonOneView(character)
    if not (view and view.available) then
        local message = (view and view.message) or (L.MYTHIC_PLUS_UNAVAILABLE or "Mythic+ data is not available yet.")
        sidebar.emptyText:SetText(message)
        sidebar.emptyText:ClearAllPoints()
        sidebar.emptyText:SetPoint("TOPLEFT", sidebar.warbandKeysButton or sidebar.title, sidebar.warbandKeysButton and "BOTTOMLEFT" or "BOTTOMLEFT", 0, -14)
        sidebar.emptyText:SetPoint("BOTTOMRIGHT", -16, 16)
        sidebar.emptyText:Show()
        sidebar.keyHeader:Hide()
        sidebar.keyPanel:Hide()
        if sidebar.warbandKeysButton then
            sidebar.warbandKeysButton:ClearAllPoints()
            sidebar.warbandKeysButton:SetPoint("TOPLEFT", sidebar.title, "BOTTOMLEFT", 0, -18)
            sidebar.warbandKeysButton:SetPoint("TOPRIGHT", -16, 0)
            sidebar.warbandKeysButton:Show()
            if sidebar.warbandKeysButton.label then
                sidebar.warbandKeysButton.label:SetText(centerMode == MYTHIC_PLUS_CENTER_WARBAND_KEYS
                    and (L.MYTHIC_PLUS_SEASON_DUNGEONS or "Season Dungeons")
                    or (L.MYTHIC_PLUS_WARBAND_KEYS or "Warband Keys"))
            end
            helpers.setMiniActionButtonActive(sidebar.warbandKeysButton, centerMode == MYTHIC_PLUS_CENTER_WARBAND_KEYS)
        end
        sidebar.affixHeader:Hide()
        sidebar.affixPanel:Hide()

        center.emptyText:SetText(message)
        center.summaryPanel:Hide()
        center.dungeonsPanel:Hide()
        if center.warbandKeysPanel then
            center.warbandKeysPanel:SetShown(centerMode == MYTHIC_PLUS_CENTER_WARBAND_KEYS)
        end
        center.emptyText:SetShown(centerMode ~= MYTHIC_PLUS_CENTER_WARBAND_KEYS)
        for _, statCard in ipairs(center.statCards or {}) do
            statCard:Hide()
        end
        if centerMode == MYTHIC_PLUS_CENTER_WARBAND_KEYS and center.warbandKeysPanel then
            local keyEntries = buildWarbandKeyEntries(nil)
            ensureMythicPlusWarbandKeyRows(ui, #keyEntries, helpers)
            for index, row in ipairs(center.warbandKeyRows or {}) do
                applyMythicPlusWarbandKeyRow(row, keyEntries[index], helpers)
            end
            center.warbandKeysPanel.scrollChild:SetHeight(math.max(10, (#keyEntries * 86) - 10))
            center.warbandKeysPanel.empty:SetShown(#keyEntries == 0)
        end

        utility.emptyText:SetText(message)
        utility.emptyText:Show()
        utility.recentHeader:Hide()
        utility.recentPanel:Hide()
        utility.goalsHeader:Hide()
        utility.goalsPanel:Hide()
        return
    end

    sidebar.title:SetText(L.MAIN_TAB_MYTHICPLUS or "Mythic+")
    sidebar.subtitle:SetText("")
    sidebar.subtitle:Hide()
    sidebar.emptyText:Hide()
    sidebar.keyHeader:Show()
    sidebar.keyPanel:Show()
    sidebar.warbandKeysButton:ClearAllPoints()
    sidebar.warbandKeysButton:SetPoint("TOPLEFT", sidebar.keyPanel, "BOTTOMLEFT", 0, -12)
    sidebar.warbandKeysButton:SetPoint("TOPRIGHT", sidebar.keyPanel, "BOTTOMRIGHT", 0, -12)
    sidebar.warbandKeysButton:Show()
    sidebar.affixHeader:Show()
    sidebar.affixPanel:Show()

    center.emptyText:Hide()
    center.summaryPanel:Show()
    center.dungeonsPanel:SetShown(centerMode ~= MYTHIC_PLUS_CENTER_WARBAND_KEYS)
    if center.warbandKeysPanel then
        center.warbandKeysPanel:SetShown(centerMode == MYTHIC_PLUS_CENTER_WARBAND_KEYS)
    end
    center.summaryPanel.title:SetText(L.MYTHIC_PLUS_TITLE or "Mythic+ Season 1")
    center.summaryPanel.subtitle:SetText(L.MYTHIC_PLUS_CENTER_SUBTITLE or "Season 1 dungeon pool with your best season and weekly runs.")
    center.warbandKeysPanel.title:SetText(L.MYTHIC_PLUS_WARBAND_KEYS_TITLE or "Warband Keys")
    center.warbandKeysPanel.subtitle:SetText(L.MYTHIC_PLUS_WARBAND_KEYS_SUBTITLE or "Saved keystones for your visible characters.")

    if sidebar.warbandKeysButton and sidebar.warbandKeysButton.label then
        sidebar.warbandKeysButton.label:SetText(centerMode == MYTHIC_PLUS_CENTER_WARBAND_KEYS
            and (L.MYTHIC_PLUS_SEASON_DUNGEONS or "Season Dungeons")
            or (L.MYTHIC_PLUS_WARBAND_KEYS or "Warband Keys"))
        helpers.setMiniActionButtonActive(sidebar.warbandKeysButton, centerMode == MYTHIC_PLUS_CENTER_WARBAND_KEYS)
    end

    utility.title:SetText(L.MYTHIC_PLUS_UTILITY_TITLE or "Runs & goals")
    utility.subtitle:SetText(L.MYTHIC_PLUS_UTILITY_SUBTITLE or "Latest pushes and seasonal milestones for the active character.")
    utility.emptyText:Hide()
    utility.recentHeader:Show()
    utility.recentPanel:Show()
    utility.goalsHeader:Show()
    utility.goalsPanel:Show()

    local keyEntry = view.summary and view.summary.key or {}
    local vaultEntry = view.summary and view.summary.vault or {}
    sidebar.keyValue:SetText(keyEntry.value or (L.MYTHIC_PLUS_KEY_NONE or "No keystone"))
    sidebar.keyValue:SetTextColor(THEME.gold[1], THEME.gold[2], THEME.gold[3], 1)
    sidebar.keyMeta:SetText(keyEntry.meta or "")
    sidebar.weekValue:SetText(
        (tonumber(view.currentWeekBestLevel) or 0) > 0
            and string.format("+%d", tonumber(view.currentWeekBestLevel) or 0)
            or "--"
    )
    sidebar.vaultValue:SetText(vaultEntry.value or (L.MYTHIC_PLUS_VAULT_NONE or "No run yet"))

    local affixEntries = type(view.affixes) == "table" and view.affixes or {}
    if #affixEntries == 0 then
        affixEntries = {
            {
                label = L.MYTHIC_PLUS_UNAVAILABLE or "Mythic+ data is not available yet.",
                text = "--",
                status = "missing",
            },
        }
    end
    for index, row in ipairs(sidebar.affixRows or {}) do
        helpers.setStatusRowWithoutBadge(row, affixEntries[index])
    end

    for index, statCard in ipairs(center.statCards or {}) do
        local entry
        if index == 1 then
            entry = view.summary and view.summary.score
        elseif index == 2 then
            entry = view.summary and view.summary.vault
        else
            entry = view.summary and view.summary.portals
        end

        applyMythicPlusStatCard(statCard, entry)
        statCard:Show()
    end

    local dungeonEntries = type(view.dungeons) == "table" and view.dungeons or {}
    ensureMythicPlusDungeonRows(ui, #dungeonEntries, helpers)
    for index, row in ipairs(center.dungeonRows or {}) do
        applyMythicPlusDungeonRow(row, dungeonEntries[index])
    end
    center.dungeonsPanel.scrollChild:SetHeight(math.max(10, (#dungeonEntries * 86) - 10))
    center.dungeonsPanel.empty:SetShown(#dungeonEntries == 0)

    local keyEntries = buildWarbandKeyEntries(view)
    ensureMythicPlusWarbandKeyRows(ui, #keyEntries, helpers)
    for index, row in ipairs(center.warbandKeyRows or {}) do
        applyMythicPlusWarbandKeyRow(row, keyEntries[index], helpers)
    end
    center.warbandKeysPanel.scrollChild:SetHeight(math.max(10, (#keyEntries * 86) - 10))
    center.warbandKeysPanel.empty:SetShown(#keyEntries == 0)

    local rewardEntries = type(view.rewards) == "table" and view.rewards or {}
    ensureMythicPlusGoalRows(ui, #rewardEntries, helpers)
    for index, row in ipairs(utility.goalRows or {}) do
        helpers.setStatusRowWithoutBadge(row, rewardEntries[index])
    end
    utility.goalsPanel.scrollChild:SetHeight(math.max(10, (#rewardEntries * (WEEKLY_STATUS_ROW_HEIGHT + WEEKLY_STATUS_ROW_GAP)) - WEEKLY_STATUS_ROW_GAP))

    local recentEntries = type(view.recentRuns) == "table" and view.recentRuns or {}
    ensureMythicPlusRecentRows(ui, #recentEntries, helpers)
    for index, row in ipairs(utility.recentRows or {}) do
        helpers.setStatusRowWithoutBadge(row, recentEntries[index])
    end
    utility.recentPanel.scrollChild:SetHeight(math.max(10, (#recentEntries * (WEEKLY_STATUS_ROW_HEIGHT + WEEKLY_STATUS_ROW_GAP)) - WEEKLY_STATUS_ROW_GAP))
    utility.recentPanel.empty:SetShown(#recentEntries == 0)
end

UIMythicPlusCard.CreateStatCard = createMythicPlusStatCard
UIMythicPlusCard.ApplyStatCard = applyMythicPlusStatCard
UIMythicPlusCard.CreateModeSidebar = createMythicPlusModeSidebar
UIMythicPlusCard.CreateModeCenter = createMythicPlusModeCenter
UIMythicPlusCard.CreateModeUtility = createMythicPlusModeUtility
UIMythicPlusCard.CreateDungeonRow = createMythicPlusDungeonRow
UIMythicPlusCard.ApplyDungeonRow = applyMythicPlusDungeonRow
UIMythicPlusCard.Layout = layoutMythicPlusCard
UIMythicPlusCard.EnsureDungeonRows = ensureMythicPlusDungeonRows
UIMythicPlusCard.EnsureWarbandKeyRows = ensureMythicPlusWarbandKeyRows
UIMythicPlusCard.EnsureRecentRows = ensureMythicPlusRecentRows
UIMythicPlusCard.EnsureGoalRows = ensureMythicPlusGoalRows
UIMythicPlusCard.Refresh = refreshMythicPlusCard
