GuildHelper = GuildHelper or {}

local addon = GuildHelper

addon.NewUIMyToons = addon.NewUIMyToons or {}

local page = addon.NewUIMyToons
page.State = page.State or {}

local theme = addon.NewUITheme and addon.NewUITheme.Default or {
    Card = {
        Background = { 0.12, 0.12, 0.14, 0.98 },
        Border = { 0.34, 0.34, 0.4, 1 },
        Title = { 0.96, 0.93, 0.84, 1 },
        Value = { 0.72, 0.68, 0.58, 1 },
    },
    Panel = {
        Background = { 0.12, 0.12, 0.14, 0.98 },
        Border = { 0.34, 0.34, 0.4, 1 },
        Title = { 0.96, 0.93, 0.84, 1 },
    },
}

local LIST_COLUMNS = {
    Character = { x = 0, w = 190 },
    SpecRole = { x = 200, w = 110 },
    ItemLevel = { x = 320, w = 70 },
    Status = { x = 395, w = 73 },
}

local INTEREST_OPTIONS = { "raiding", "mythic+", "delves", "pvp", "alts", "crafting", "farming", "events", "social" }
local AVAILABILITY_DAY_OPTIONS = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }

local function trimString(value)
    return tostring(value or ""):match("^%s*(.-)%s*$") or ""
end

local function normalizeAvailabilityDays(values)
    local lookup = {}

    for _, value in ipairs(type(values) == "table" and values or {}) do
        local trimmed = trimString(value)
        if trimmed ~= "" then
            lookup[string.lower(trimmed)] = true
        end
    end

    local resolved = {}
    for _, day in ipairs(AVAILABILITY_DAY_OPTIONS) do
        if lookup[string.lower(day)] then
            resolved[#resolved + 1] = day
        end
    end

    return resolved
end

local function buildAvailabilitySummary(days, startText, endText, fallbackText)
    local dayList = normalizeAvailabilityDays(days)
    local startValue = trimString(startText)
    local endValue = trimString(endText)
    local parts = {}

    if #dayList > 0 then
        parts[#parts + 1] = table.concat(dayList, ", ")
    end

    if startValue ~= "" and endValue ~= "" then
        parts[#parts + 1] = startValue .. " - " .. endValue
    elseif startValue ~= "" then
        parts[#parts + 1] = "From " .. startValue
    elseif endValue ~= "" then
        parts[#parts + 1] = "Until " .. endValue
    end

    if #parts > 0 then
        return table.concat(parts, " | ")
    end

    return trimString(fallbackText)
end

local function resolveFactionVisual(factionText)
    local normalized = string.lower(trimString(factionText))

    if string.find(normalized, "horde", 1, true) then
        return "HomeMythicCardBackground", { 0.96, 0.28, 0.12, 1 }
    end

    if string.find(normalized, "alliance", 1, true) then
        return "HomeNewsCardBackground", { 0.52, 0.76, 1.00, 1 }
    end

    return "MyToonsCardBackground", { 0.96, 0.93, 0.84, 1 }
end

local MYTOONS_CLASS_ICON_TEXTURE = CLASS_ICON_TEXTURE or "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES"

local function resolveMyToonsClassToken(classFile, className)
    for _, value in ipairs({ classFile, className }) do
        local normalized = string.upper((trimString(value):gsub("%s+", "")))
        if normalized ~= "" and type(CLASS_ICON_TCOORDS) == "table" and CLASS_ICON_TCOORDS[normalized] then
            return normalized
        end
        for _, lookup in ipairs({ LOCALIZED_CLASS_NAMES_MALE or {}, LOCALIZED_CLASS_NAMES_FEMALE or {} }) do
            for token, localized in pairs(lookup) do
                if string.upper((trimString(localized):gsub("%s+", ""))) == normalized then
                    return token
                end
            end
        end
    end
    return ""
end

local MYTOONS_PANEL_ART = { backgroundTextureKey = "MyToonsPanelBackground", borderTextureKey = "MyToonsPanelBorder" }
local MYTOONS_CARD_ART = { backgroundTextureKey = "MyToonsCardBackground", borderTextureKey = "MyToonsCardBorder" }
local MYTOONS_BUTTON_ART = { backgroundTextureKey = "MyToonsButtonBackground", selectedBackgroundTextureKey = "MyToonsButtonSelected", borderTextureKey = "MyToonsButtonBorder" }
local MYTOONS_ROW_ART = { backgroundTextureKey = "MyToonsRowBackground", borderTextureKey = "" }
local ApplyRect
local refreshPage
local updateSelectedToonProfile

local function resolveMyToonsTexturePath(textureKeyOrPath)
    if type(textureKeyOrPath) ~= "string" or textureKeyOrPath == "" then
        return nil
    end

    local textures = addon.NewUIAssets and addon.NewUIAssets.Textures or nil
    if textures and textures[textureKeyOrPath] ~= nil then
        return textures[textureKeyOrPath] ~= "" and textures[textureKeyOrPath] or nil
    end

    return textureKeyOrPath
end

local function resolveMyToonsArtMetrics(textureKey)
    local metrics = addon.NewUIAssets and addon.NewUIAssets.Metrics or nil
    return metrics and metrics[textureKey] or nil
end

local function resolveMyToonsInsets(insetKey, fallbackInsets)
    local insets = addon.NewUIAssets and addon.NewUIAssets.Insets or nil
    return insets and insets[insetKey] or fallbackInsets or {}
end

local function resolveMyToonsAsset(textureKey, fallbackInsets, fallbackWidth, fallbackHeight)
    local metrics = resolveMyToonsArtMetrics(textureKey) or {}
    return {
        texture = resolveMyToonsTexturePath(textureKey),
        sourceWidth = tonumber(metrics.sourceWidth) or tonumber(fallbackWidth) or 1,
        sourceHeight = tonumber(metrics.sourceHeight) or tonumber(fallbackHeight) or 1,
        insets = metrics.insets or fallbackInsets or {},
        texCoord = metrics.texCoord,
    }
end

local function CreateArtFrame(parent, rect, asset)
    local frame = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(frame, parent, rect)

    local hasAuthoredArt = type(asset) == "table" and trimString(asset.texture) ~= ""
    if frame.SetBackdrop then
        frame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
            insets = {
                left = 1,
                right = 1,
                top = 1,
                bottom = 1,
            },
        })
        frame:SetBackdropColor(hasAuthoredArt and 0 or 0.08, hasAuthoredArt and 0 or 0.08, hasAuthoredArt and 0 or 0.10, hasAuthoredArt and 0 or 0.92)
        frame:SetBackdropBorderColor(hasAuthoredArt and 0 or 0.34, hasAuthoredArt and 0 or 0.34, hasAuthoredArt and 0 or 0.40, hasAuthoredArt and 0 or 1)
    elseif not hasAuthoredArt then
        local fallback = frame:CreateTexture(nil, "BACKGROUND")
        fallback:SetAllPoints(frame)
        fallback:SetColorTexture(0.10, 0.10, 0.12, 0.94)
        frame.FallbackBackground = fallback
    end

    local texture = frame:CreateTexture(nil, "BACKGROUND")
    texture:SetAllPoints(frame)
    if hasAuthoredArt then
        texture:SetTexture(asset.texture)
        texture:SetVertexColor(1, 1, 1, 0.98)
        if type(asset.texCoord) == "table" and #asset.texCoord == 4 then
            texture:SetTexCoord(asset.texCoord[1], asset.texCoord[3], asset.texCoord[2], asset.texCoord[4])
        end
    else
        texture:SetColorTexture(0.12, 0.12, 0.14, 0.98)
    end
    frame.Texture = texture

    local sourceWidth = tonumber(asset and asset.sourceWidth) or tonumber(rect and rect.w) or 1
    local sourceHeight = tonumber(asset and asset.sourceHeight) or tonumber(rect and rect.h) or 1
    local insets = asset and asset.insets or {}
    local scaleX = (tonumber(rect and rect.w) or 1) / math.max(1, sourceWidth)
    local scaleY = (tonumber(rect and rect.h) or 1) / math.max(1, sourceHeight)
    local left = math.floor(((tonumber(insets.left) or 0) * scaleX) + 0.5)
    local right = math.floor(((tonumber(insets.right) or 0) * scaleX) + 0.5)
    local top = math.floor(((tonumber(insets.top) or 0) * scaleY) + 0.5)
    local bottom = math.floor(((tonumber(insets.bottom) or 0) * scaleY) + 0.5)

    local content = CreateFrame("Frame", nil, frame)
    content:SetPoint("TOPLEFT", frame, "TOPLEFT", left, -top)
    content:SetSize(
        math.max(1, (tonumber(rect and rect.w) or 1) - left - right),
        math.max(1, (tonumber(rect and rect.h) or 1) - top - bottom)
    )
    content.Insets = {
        left = left,
        right = right,
        top = top,
        bottom = bottom,
    }

    frame.Content = content
    return frame
end

local function CreateArtContentFrame(parent, textureKey, width, height, fallbackInsets)
    local metrics = resolveMyToonsArtMetrics(textureKey)
    local sourceWidth = metrics and tonumber(metrics.sourceWidth) or tonumber(width) or 0
    local sourceHeight = metrics and tonumber(metrics.sourceHeight) or tonumber(height) or 0
    local metricInsets = metrics and metrics.insets or fallbackInsets or {}
    local scaleX = sourceWidth > 0 and (tonumber(width) or 0) / sourceWidth or 1
    local scaleY = sourceHeight > 0 and (tonumber(height) or 0) / sourceHeight or 1
    local insetLeft = math.floor(((tonumber(metricInsets.left) or 0) * scaleX) + 0.5)
    local insetRight = math.floor(((tonumber(metricInsets.right) or 0) * scaleX) + 0.5)
    local insetTop = math.floor(((tonumber(metricInsets.top) or 0) * scaleY) + 0.5)
    local insetBottom = math.floor(((tonumber(metricInsets.bottom) or 0) * scaleY) + 0.5)
    local content = CreateFrame("Frame", nil, parent)
    content:SetPoint("TOPLEFT", parent, "TOPLEFT", insetLeft, -insetTop)
    content:SetSize(math.max(1, (tonumber(width) or 0) - insetLeft - insetRight), math.max(1, (tonumber(height) or 0) - insetTop - insetBottom))
    content.Insets = {
        left = insetLeft,
        right = insetRight,
        top = insetTop,
        bottom = insetBottom,
    }
    return content
end

ApplyRect = function(frame, parent, rect)
    if not frame or not parent or not rect then
        return
    end

    frame:ClearAllPoints()
    frame:SetPoint("TOPLEFT", parent, "TOPLEFT", rect.x, rect.y)
    frame:SetSize(rect.w, rect.h)
end

local function CreateBackdrop(frame, colorKey, options)
    local palette = theme[colorKey] or theme.Panel
    local bgFile = resolveMyToonsTexturePath(options and options.backgroundTextureKey)
    local edgeFile = resolveMyToonsTexturePath(options and options.borderTextureKey)
    local hasAuthoredArt = trimString(bgFile) ~= "" or trimString(edgeFile) ~= ""

    if frame.SetBackdrop then
        frame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = edgeFile or "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
            insets = {
                left = 1,
                right = 1,
                top = 1,
                bottom = 1,
            },
        })
        frame:SetBackdropColor(hasAuthoredArt and 0 or palette.Background[1], hasAuthoredArt and 0 or palette.Background[2], hasAuthoredArt and 0 or palette.Background[3], hasAuthoredArt and 0 or palette.Background[4])
        frame:SetBackdropBorderColor(hasAuthoredArt and 0 or palette.Border[1], hasAuthoredArt and 0 or palette.Border[2], hasAuthoredArt and 0 or palette.Border[3], hasAuthoredArt and 0 or palette.Border[4])
    elseif not hasAuthoredArt then
        local background = frame:CreateTexture(nil, "BACKGROUND")
        background:SetAllPoints(frame)
        background:SetColorTexture(unpack(palette.Background))
        frame.background = background
    end

    if bgFile then
        local backgroundArt = frame:CreateTexture(nil, "BACKGROUND")
        backgroundArt:SetAllPoints(frame)
        backgroundArt:SetTexture(bgFile)
        backgroundArt:SetVertexColor(1, 1, 1, 0.98)
        local metrics = resolveMyToonsArtMetrics(options and options.backgroundTextureKey)
        if metrics and type(metrics.texCoord) == "table" and #metrics.texCoord == 4 then
            backgroundArt:SetTexCoord(metrics.texCoord[1], metrics.texCoord[3], metrics.texCoord[2], metrics.texCoord[4])
        end
        frame.BackgroundArt = backgroundArt
    end
end

local function CreateSection(parent, rect, titleText, colorKey, options)
    local asset = resolveMyToonsAsset(
        options and options.backgroundTextureKey,
        resolveMyToonsInsets("MyToonsPanel", { left = 16, right = 16, top = 12, bottom = 12 }),
        rect and rect.w,
        rect and rect.h
    )
    local section = CreateArtFrame(parent, rect, asset)

    local content = section.Content
    if trimString(titleText) ~= "" then
        local title = section.Content:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
        title:SetPoint("TOPLEFT", section.Content, "TOPLEFT", 0, 0)
        title:SetText(titleText)
        title:SetTextColor(unpack((theme.Panel and theme.Panel.Title) or { 0.96, 0.93, 0.84, 1 }))

        local body = CreateFrame("Frame", nil, section)
        body:SetPoint("TOPLEFT", section.Content, "TOPLEFT", 0, -26)
        body:SetPoint("BOTTOMRIGHT", section.Content, "BOTTOMRIGHT", 0, 0)
        section.TitleText = title
        section.Body = body
        content = body
    end

    section.Content = content
    return section
end

local function CreateInlinePanel(parent, rect, titleText, colorKey, options)
    local asset = resolveMyToonsAsset(
        options and options.backgroundTextureKey,
        resolveMyToonsInsets("MyToonsPanel", { left = 16, right = 16, top = 12, bottom = 12 }),
        rect and rect.w,
        rect and rect.h
    )
    local panel = CreateArtFrame(parent, rect, asset)

    local title = panel.Content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    title:SetPoint("TOPLEFT", panel.Content, "TOPLEFT", 0, 0)
    title:SetText(titleText)
    title:SetTextColor(unpack((theme.Panel and theme.Panel.Title) or { 0.96, 0.93, 0.84, 1 }))

    local content = CreateFrame("Frame", nil, panel)
    content:SetPoint("TOPLEFT", panel.Content, "TOPLEFT", 0, -24)
    content:SetPoint("BOTTOMRIGHT", panel.Content, "BOTTOMRIGHT", 0, 0)

    panel.TitleText = title
    panel.Body = content
    panel.Content = content
    return panel
end

local function CreatePlaceholderBar(parent, rect, textValue, options)
    local asset = resolveMyToonsAsset(
        options and options.backgroundTextureKey,
        resolveMyToonsInsets("MyToonsCard", { left = 18, right = 18, top = 12, bottom = 12 }),
        rect and rect.w,
        rect and rect.h
    )
    local bar = CreateArtFrame(parent, rect, asset)
    local content = bar.Content

    local text = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    text:SetPoint("LEFT", content, "LEFT", 0, 0)
    text:SetText(textValue)
    text:SetTextColor(0.72, 0.68, 0.58, 1)

    bar.Text = text
    return bar
end

local function CreateFilterInputBar(parent, rect, placeholderText, textValue, onCommit)
    local bar = CreateArtFrame(parent, rect, resolveMyToonsAsset("MyToonsCardBackground", { left = 18, right = 18, top = 12, bottom = 12 }, rect.w, rect.h))
    local editBox = CreateFrame("EditBox", nil, bar, "InputBoxTemplate")
    if editBox.Left then editBox.Left:SetAlpha(0) end
    if editBox.Middle then editBox.Middle:SetAlpha(0) end
    if editBox.Right then editBox.Right:SetAlpha(0) end
    editBox:SetAutoFocus(false)
    editBox:SetFontObject("GameFontHighlightSmall")
    editBox:SetPoint("TOPLEFT", bar.Content, "TOPLEFT", 10, 1)
    editBox:SetPoint("BOTTOMRIGHT", bar.Content, "BOTTOMRIGHT", -6, -1)
    editBox:SetTextInsets(0, 0, 0, 0)
    editBox:SetTextColor(0.72, 0.68, 0.58, 1)
    editBox:SetText(textValue or "")

    local placeholder = bar.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    placeholder:SetPoint("LEFT", bar.Content, "LEFT", 10, 0)
    placeholder:SetText(placeholderText)
    placeholder:SetTextColor(0.52, 0.50, 0.46, 1)
    placeholder:SetShown(trimString(textValue) == "")

    local function commit()
        if onCommit then
            onCommit(trimString(editBox:GetText()))
        end
    end

    editBox:SetScript("OnTextChanged", function(self)
        placeholder:SetShown(trimString(self:GetText()) == "" and not self:HasFocus())
    end)
    editBox:SetScript("OnEditFocusGained", function() placeholder:Hide() end)
    editBox:SetScript("OnEditFocusLost", function(self)
        placeholder:SetShown(trimString(self:GetText()) == "")
        commit()
    end)
    editBox:SetScript("OnEnterPressed", function(self)
        commit()
        self:ClearFocus()
    end)

    bar.EditBox = editBox
    bar.PlaceholderText = placeholder
    return bar
end

local function ShowDropdownMenu(anchorFrame, menuItems)
    if type(EasyMenu) ~= "function" or not anchorFrame then
        return
    end

    if not page._dropdownMenuFrame then
        page._dropdownMenuFrame = CreateFrame("Frame", "GH3NewUIMyToonsDropdownMenu", UIParent, "UIDropDownMenuTemplate")
    end

    CloseDropDownMenus()
    EasyMenu(menuItems or {}, page._dropdownMenuFrame, anchorFrame, 0, 0, "MENU", 2)
end

local function CreateFilterSelectBar(parent, rect, textValue, onClick)
    local button = CreateFrame("Button", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(button, parent, rect)
    CreateBackdrop(button, "Card", MYTOONS_CARD_ART)

    local content = CreateArtContentFrame(button, MYTOONS_CARD_ART.backgroundTextureKey, rect.w, rect.h, { left = 18, right = 18, top = 12, bottom = 12 })
    local text = button:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    text:SetPoint("LEFT", content, "LEFT", 8, 0)
    text:SetWidth(math.max(1, rect.w - 34))
    text:SetWordWrap(false)
    text:SetJustifyH("LEFT")
    text:SetText(textValue)
    text:SetTextColor(0.72, 0.68, 0.58, 1)

    local arrow = button:CreateTexture(nil, "OVERLAY")
    arrow:SetPoint("RIGHT", content, "RIGHT", -2, 0)
    arrow:SetSize(14, 14)
    arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
    arrow:SetVertexColor(0.96, 0.82, 0.46, 1)

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

    button.Content = content
    button.Text = text
    button.ArrowText = arrow
    return button
end

local function CreateFilterButton(parent, rect, options)
    local button = CreateFrame("Button", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(button, parent, rect)
    CreateBackdrop(button, "Panel", options)

    local buttonInsets = addon.NewUIAssets and addon.NewUIAssets.Insets and addon.NewUIAssets.Insets.MyToonsButton or { left = 20, right = 20, top = 10, bottom = 10 }
    local content = CreateArtContentFrame(button, options and options.backgroundTextureKey, rect.w, rect.h, buttonInsets)

    local text = button:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    text:SetPoint("CENTER", content, "CENTER", 0, 0)
    text:SetText("Filter")
    text:SetTextColor(0.72, 0.68, 0.58, 1)

    button.Content = content
    button.Text = text
    return button
end

local function CreateSubTabButton(parent, key, labelText, rect, isSelected, onClick, options)
    local button = CreateFrame("Button", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    if rect then
        button:SetPoint("TOPLEFT", parent, "TOPLEFT", rect.x or 0, rect.y or 0)
        button:SetSize(rect.w or 112, rect.h or 24)
    else
        button:SetPoint("LEFT", parent, "LEFT", 0, 0)
        button:SetSize(112, 24)
    end

    local resolvedOptions = {}
    for optionKey, optionValue in pairs(type(options) == "table" and options or {}) do
        resolvedOptions[optionKey] = optionValue
    end
    if isSelected and trimString(resolvedOptions.selectedBackgroundTextureKey) ~= "" then
        resolvedOptions.backgroundTextureKey = resolvedOptions.selectedBackgroundTextureKey
    end
    CreateBackdrop(button, "Panel", resolvedOptions)

    local buttonInsets = addon.NewUIAssets and addon.NewUIAssets.Insets and addon.NewUIAssets.Insets.MyToonsButton or { left = 20, right = 20, top = 10, bottom = 10 }
    local activeTextureKey = resolvedOptions.backgroundTextureKey
    local content = CreateArtContentFrame(button, activeTextureKey, rect and rect.w or 112, rect and rect.h or 24, buttonInsets)

    local text = button:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    text:SetPoint("CENTER", content, "CENTER", 0, 0)
    text:SetText(labelText)
    local activeColor = (theme.Shell and theme.Shell.Text) or { 0.96, 0.93, 0.84, 1 }
    local inactiveColor = (theme.Shell and theme.Shell.MutedText) or { 0.72, 0.68, 0.58, 1 }
    local color = isSelected and activeColor or inactiveColor
    text:SetTextColor(color[1], color[2], color[3], color[4])

    button:SetScript("OnClick", function()
        page.State.selectedSubTab = key
        if onClick then
            onClick()
        end
    end)

    button.Content = content
    button.Text = text
    return button
end

local CreateSelectedToonCard
local CreateDetailField
local formatUpdatedAt

local function ApplyFontStringRect(fontString, parent, rect)
    fontString:ClearAllPoints()
    fontString:SetPoint("TOPLEFT", parent, "TOPLEFT", rect.x, rect.y)
    fontString:SetWidth(rect.w)
    fontString:SetWordWrap(false)
end

local function CreateSummaryField(parent, x, y, labelText, valueText, width)
    local field = CreateFrame("Frame", nil, parent)
    field:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    field:SetSize(width, 26)

    local label = field:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    label:SetPoint("TOPLEFT", field, "TOPLEFT", 0, 0)
    label:SetText(labelText)
    label:SetTextColor(0.72, 0.68, 0.58, 1)

    local value = field:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    value:SetPoint("TOPLEFT", label, "BOTTOMLEFT", 0, -1)
    value:SetWidth(width)
    value:SetWordWrap(false)
    value:SetText(valueText)

    field.LabelText = label
    field.ValueText = value
    return field
end

local function resolveSummaryRoleIcon(roleText)
    local normalized = string.lower(trimString(roleText))
    if string.find(normalized, "tank", 1, true) then return "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Role_Tank-l.png" end
    if string.find(normalized, "heal", 1, true) then return "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Role_Healer-l.png" end
    return "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Role_DPS-l.png"
end

local function CreateSummaryAlertChip(parent, x, y, w, h, textValue, tint, iconPath)
    local chip = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    chip:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    chip:SetSize(w, h)
    if chip.SetBackdrop then
        chip:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8", edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
        chip:SetBackdropColor(tint[1] * 0.22, tint[2] * 0.22, tint[3] * 0.22, 0.95)
        chip:SetBackdropBorderColor(tint[1], tint[2], tint[3], 0.95)
    end
    local icon = chip:CreateTexture(nil, "ARTWORK")
    icon:SetPoint("LEFT", chip, "LEFT", 10, 0)
    icon:SetSize(16, 16)
    icon:SetTexture(iconPath)
    local text = chip:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    text:SetPoint("LEFT", icon, "RIGHT", 10, 0)
    text:SetWidth(w - 40)
    text:SetJustifyH("LEFT")
    text:SetWordWrap(false)
    text:SetText(textValue)
    text:SetTextColor(tint[1], tint[2], tint[3], 1)
    return chip
end

local function resolveInterestTagPalette(labelText)
    local normalized = string.lower(trimString(labelText))
    if string.find(normalized, "raid", 1, true) then return { 0.34, 0.12, 0.10, 0.94 }, { 0.84, 0.36, 0.30, 1 } end
    if string.find(normalized, "mythic", 1, true) then return { 0.18, 0.14, 0.36, 0.94 }, { 0.66, 0.54, 0.96, 1 } end
    if string.find(normalized, "delve", 1, true) or string.find(normalized, "dungeon", 1, true) then return { 0.24, 0.22, 0.14, 0.94 }, { 0.88, 0.76, 0.42, 1 } end
    if string.find(normalized, "pvp", 1, true) then return { 0.34, 0.18, 0.05, 0.94 }, { 0.96, 0.74, 0.24, 1 } end
    if string.find(normalized, "profession", 1, true) then return { 0.10, 0.26, 0.12, 0.94 }, { 0.46, 0.88, 0.36, 1 } end
    return { 0.12, 0.14, 0.18, 0.94 }, { 0.72, 0.68, 0.58, 1 }
end

local function formatDisplayLabel(textValue)
    local trimmed = trimString(textValue)
    if trimmed == "" then
        return "-"
    end
    if string.upper(trimmed) == "DPS" then
        return "DPS"
    end
    return (trimmed:gsub("(%a)([%w']*)", function(first, rest)
        return string.upper(first) .. string.lower(rest)
    end))
end

local function CreateInterestTag(parent, x, y, textValue)
    local backgroundColor, borderColor = resolveInterestTagPalette(textValue)
    local width = math.max(72, math.min(118, (#trimString(textValue) * 8) + 24))
    local chip = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    chip:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    chip:SetSize(width, 22)
    if chip.SetBackdrop then
        chip:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8", edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
        chip:SetBackdropColor(backgroundColor[1], backgroundColor[2], backgroundColor[3], backgroundColor[4])
        chip:SetBackdropBorderColor(borderColor[1], borderColor[2], borderColor[3], borderColor[4])
    end
    local text = chip:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    text:SetPoint("CENTER", chip, "CENTER", 0, 0)
    text:SetText(formatDisplayLabel(textValue))
    text:SetTextColor(borderColor[1], borderColor[2], borderColor[3], 1)
    chip.Text = text
    return chip
end

local function formatRoleLabel(roleText)
    local normalized = string.lower(trimString(roleText))
    if normalized == "dps" then return "DPS" end
    if normalized == "tank" then return "Tank" end
    if normalized == "healer" then return "Healer" end
    return trimString(roleText) ~= "" and trimString(roleText):gsub("^%l", string.upper) or "-"
end

local function formatTimePlayedDuration(totalSeconds)
    local seconds = math.max(0, math.floor(tonumber(totalSeconds) 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("%dd %dh", days, hours) end
    if hours > 0 then return string.format("%dh %dm", hours, minutes) end
    return string.format("%dm", minutes)
end

local function formatAggregateTimePlayedDuration(totalSeconds)
    local seconds = math.max(0, math.floor(tonumber(totalSeconds) or 0))
    local days = math.floor(seconds / 86400)
    local hours = math.floor((seconds % 86400) / 3600)
    return string.format("%dd %dh", days, hours)
end

local function sumSavedTimePlayedSeconds(rows)
    local total = 0

    for _, row in ipairs(type(rows) == "table" and rows or {}) do
        local record = type(row) == "table" and type(row.myToonRecord) == "table" and row.myToonRecord or nil
        local member = type(row) == "table" and type(row.member) == "table" and row.member or nil
        total = total + math.max(0, math.floor(tonumber((record and record.timePlayedSeconds) or (member and member.timePlayedSeconds) or 0) or 0))
    end

    return total
end

local function formatPreciseItemLevel(primaryValue, fallbackValue)
    local numericValue = tonumber(primaryValue)
    if numericValue == nil then
        numericValue = tonumber(fallbackValue)
    end
    if numericValue and numericValue > 0 then
        return string.format("%.2f", numericValue)
    end
    return trimString(primaryValue) ~= "" and trimString(primaryValue) or trimString(fallbackValue) ~= "" and trimString(fallbackValue) or "-"
end

local function ensureCurrentToonTimePlayedRequest(selectedRow)
    local playerId = addon.Store and addon.Store.GetPlayerName and trimString(addon.Store:GetPlayerName()) or ""
    if playerId == "" or not selectedRow or trimString(selectedRow.id) ~= playerId or type(RequestTimePlayed) ~= "function" then
        return
    end

    page.State = page.State or {}
    if type(page.State.timePlayed) == "table" and page.State.timePlayed.toonId == playerId then
        return
    end

    page._requestedTimePlayedRow = selectedRow

    if not page._timePlayedFrame then
        page._timePlayedFrame = CreateFrame("Frame")
        page._timePlayedFrame:SetScript("OnEvent", function(self, event, totalTime, levelTime)
            if event ~= "TIME_PLAYED_MSG" then return end
            self:UnregisterEvent("TIME_PLAYED_MSG")
            local resolvedTotal = math.max(0, math.floor(tonumber(totalTime) or 0))
            page.State = page.State or {}
            page.State.timePlayed = {
                toonId = addon.Store and addon.Store.GetPlayerName and trimString(addon.Store:GetPlayerName()) or "",
                total = resolvedTotal,
                level = tonumber(levelTime) or 0,
            }
            page.State.requestedTimePlayedFor = nil

            local selectedTimePlayedRow = page._requestedTimePlayedRow
            local savedTotal = tonumber(selectedTimePlayedRow and selectedTimePlayedRow.myToonRecord and selectedTimePlayedRow.myToonRecord.timePlayedSeconds) or 0
            local memberTotal = tonumber(selectedTimePlayedRow and selectedTimePlayedRow.member and selectedTimePlayedRow.member.timePlayedSeconds) or 0

            if selectedTimePlayedRow and resolvedTotal ~= savedTotal and resolvedTotal ~= memberTotal and addon.RosterService and addon.RosterService.UpdateMember then
                updateSelectedToonProfile(selectedTimePlayedRow, { timePlayedSeconds = resolvedTotal }, page._lastParent, page._lastSnapshot)
                return
            end

            if page._lastParent then
                refreshPage(page._lastParent, page._lastSnapshot)
            end
        end)
    end

    if page.State.requestedTimePlayedFor ~= playerId then
        page.State.requestedTimePlayedFor = playerId
        page._timePlayedFrame:RegisterEvent("TIME_PLAYED_MSG")
        RequestTimePlayed()
    end
end

local function CreateSummaryBlock(parent, x, y, width, labelText, primaryText, secondaryText, iconPath, primaryColor, useLargeFont)
    local block = CreateFrame("Frame", nil, parent)
    block:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    block:SetSize(width, 54)

    local label = block:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    label:SetPoint("TOPLEFT", block, "TOPLEFT", 0, 0)
    label:SetText(labelText)
    label:SetTextColor(0.96, 0.82, 0.46, 1)

    local leftInset = 0
    local icon = nil
    if trimString(iconPath) ~= "" then
        icon = block:CreateTexture(nil, "ARTWORK")
        icon:SetPoint("TOPLEFT", block, "TOPLEFT", 0, -18)
        icon:SetSize(28, 28)
        icon:SetTexture(iconPath)
        leftInset = 36
    end

    local primary = block:CreateFontString(nil, "OVERLAY", useLargeFont and "GameFontNormalLarge" or "GameFontHighlight")
    primary:SetPoint("TOPLEFT", block, "TOPLEFT", leftInset, -18)
    primary:SetWidth(math.max(1, width - leftInset))
    primary:SetWordWrap(false)
    primary:SetJustifyH("LEFT")
    primary:SetText(primaryText)
    primary:SetTextColor((primaryColor or { 0.96, 0.93, 0.84, 1 })[1], (primaryColor or { 0.96, 0.93, 0.84, 1 })[2], (primaryColor or { 0.96, 0.93, 0.84, 1 })[3], (primaryColor or { 0.96, 0.93, 0.84, 1 })[4] or 1)

    local secondary = nil
    if trimString(secondaryText) ~= "" then
        secondary = block:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        secondary:SetPoint("TOPLEFT", primary, "BOTTOMLEFT", 0, -2)
        secondary:SetWidth(math.max(1, width - leftInset))
        secondary:SetWordWrap(false)
        secondary:SetJustifyH("LEFT")
        secondary:SetText(secondaryText)
        secondary:SetTextColor(0.96, 0.93, 0.84, 1)
    end

    block.LabelText = label
    block.Icon = icon
    block.PrimaryText = primary
    block.SecondaryText = secondary
    return block
end

local function GetClassColor(className)
    local normalized = string.lower(trimString(className))
    local colors = {
        deathknight = { 0.77, 0.12, 0.23, 1 },
        demonhunter = { 0.64, 0.19, 0.79, 1 },
        druid = { 1.0, 0.49, 0.04, 1 },
        evoker = { 0.2, 0.58, 0.5, 1 },
        hunter = { 0.67, 0.83, 0.45, 1 },
        mage = { 0.25, 0.78, 0.92, 1 },
        monk = { 0.0, 1.0, 0.6, 1 },
        paladin = { 0.96, 0.55, 0.73, 1 },
        priest = { 0.9, 0.9, 0.9, 1 },
        rogue = { 1.0, 0.96, 0.41, 1 },
        shaman = { 0.0, 0.44, 0.87, 1 },
        warlock = { 0.53, 0.53, 0.93, 1 },
        warrior = { 0.78, 0.61, 0.43, 1 },
    }
    return unpack(colors[normalized] or { 0.96, 0.82, 0.46, 1 })
end

local function ResolveStatusVisual(statusText)
    local normalized = string.lower(trimString(statusText))

    if string.find(normalized, "ready", 1, true) or string.find(normalized, "complete", 1, true) or string.find(normalized, "configured", 1, true) or string.find(normalized, "available", 1, true) then
        return { 0.11, 0.32, 0.14, 0.96 }, { 0.42, 0.92, 0.36, 1 }
    end

    if string.find(normalized, "missing", 1, true) or string.find(normalized, "issues", 1, true) then
        return { 0.36, 0.09, 0.07, 0.96 }, { 0.96, 0.40, 0.28, 1 }
    end

    if string.find(normalized, "needs", 1, true) or string.find(normalized, "attention", 1, true) or string.find(normalized, "low", 1, true) then
        return { 0.34, 0.18, 0.05, 0.96 }, { 0.96, 0.74, 0.24, 1 }
    end

    return { 0.11, 0.15, 0.24, 0.96 }, { 0.62, 0.82, 1, 1 }
end

local function CreatePanelValueRow(parent, offsetY, labelText, valueText, valueColor, options)
    local rowOptions = type(options) == "table" and options or {}
    local subtitleText = trimString(rowOptions.subtitle)
    local rowHeight = tonumber(rowOptions.rowHeight) or (subtitleText ~= "" and 34 or 24)
    local rowWidth = math.max(1, math.floor(tonumber(parent:GetWidth()) or 0))
    local row = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, offsetY)
    row:SetSize(rowWidth, rowHeight)
    CreateBackdrop(row, "Card", MYTOONS_ROW_ART)

    local content = CreateArtContentFrame(row, MYTOONS_ROW_ART.backgroundTextureKey, rowWidth, rowHeight, { left = 8, right = 8, top = 4, bottom = 4 })
    local accent = row:CreateTexture(nil, "ARTWORK")
    accent:SetPoint("LEFT", content, "LEFT", 0, 0)
    accent:SetSize(8, 8)
    accent:SetColorTexture(valueColor[1], valueColor[2], valueColor[3], valueColor[4] or 1)

    local label = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    label:SetPoint("LEFT", accent, "RIGHT", 8, 0)
    label:SetWidth(subtitleText ~= "" and math.max(1, rowWidth - 36) or 200)
    label:SetWordWrap(false)
    label:SetJustifyH("LEFT")
    label:SetText(labelText)
    label:SetTextColor(0.96, 0.93, 0.84, 1)

    local value = nil
    if trimString(valueText) ~= "" then
        value = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        value:SetPoint("RIGHT", content, "RIGHT", 0, 0)
        value:SetWidth(140)
        value:SetWordWrap(false)
        value:SetJustifyH("RIGHT")
        value:SetText(valueText)
        value:SetTextColor(valueColor[1], valueColor[2], valueColor[3], valueColor[4] or 1)
    end

    local subtitle = nil
    if subtitleText ~= "" then
        subtitle = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        subtitle:SetPoint("TOPLEFT", label, "BOTTOMLEFT", 0, -1)
        subtitle:SetWidth(math.max(1, rowWidth - 40))
        subtitle:SetJustifyH("LEFT")
        subtitle:SetText(subtitleText)
        subtitle:SetTextColor(0.72, 0.68, 0.58, 1)
    end

    row.Content = content
    row.LabelText = label
    row.ValueText = value
    row.SubtitleText = subtitle
    row.Accent = accent
    return row
end

local function ResolveSelectedToonMeta(selectedRow)
    local member = type(selectedRow) == "table" and selectedRow.member or nil

    local function pick(...)
        for index = 1, select("#", ...) do
            local value = trimString(select(index, ...))
            if value ~= "" then
                return value
            end
        end
        return "-"
    end

    return {
        faction = pick(selectedRow and selectedRow.faction, member and member.faction, member and member.side),
        guild = pick(selectedRow and selectedRow.guildName, member and member.guildName, member and member.guild),
        rank = pick(selectedRow and selectedRow.rankName, member and member.rankName, member and member.rank, member and member.guildRank),
        realm = pick(selectedRow and selectedRow.realm),
        specRole = pick(selectedRow and selectedRow.spec, selectedRow and selectedRow.role),
        itemLevel = formatPreciseItemLevel(member and member.itemLevel, selectedRow and selectedRow.itemLevel),
        mainCharacter = pick(selectedRow and selectedRow.mainCharacter),
        lastUpdated = formatUpdatedAt(selectedRow and selectedRow.updatedAt),
        className = pick(selectedRow and selectedRow.className),
    }
end

local function formatStoredReadinessValue(entry, fallbackText)
    if type(entry) == "table" then
        local label = trimString(entry.label)
        if label ~= "" then
            return label
        end

        local status = trimString(entry.status)
        if status ~= "" then
            return status
        end

        return fallbackText
    end

    local value = trimString(entry)
    return value ~= "" and value or fallbackText
end

local function buildAvailabilityDisplay(record, member)
    return buildAvailabilitySummary(
        type(record) == "table" and record.availabilityDays or type(member) == "table" and member.availabilityDays,
        type(record) == "table" and record.availabilityStart or type(member) == "table" and member.availabilityStart,
        type(record) == "table" and record.availabilityEnd or type(member) == "table" and member.availabilityEnd,
        type(record) == "table" and record.availability or type(member) == "table" and member.availability
    )
end

local function BuildReadinessRows(selectedRow)
    local readinessSummary = type(selectedRow) == "table" and type(selectedRow.readiness) == "table" and selectedRow.readiness or nil
    local readinessSnapshot = type(selectedRow) == "table" and type(selectedRow.readinessSnapshot) == "table" and selectedRow.readinessSnapshot or nil

    if type(readinessSnapshot) ~= "table" then
        if type(readinessSummary) == "table" then
            local rows = {}
            local function addRow(labelText, valueText)
                local fillColor, textColor = ResolveStatusVisual(valueText)
                rows[#rows + 1] = {
                    label = labelText,
                    value = valueText,
                    color = textColor,
                    fill = fillColor,
                }
            end

            addRow("Gems", formatStoredReadinessValue(readinessSummary.gems, "Unknown"))
            addRow("Enchants", formatStoredReadinessValue(readinessSummary.enchants, "Unknown"))
            addRow("Consumables", formatStoredReadinessValue(readinessSummary.consumables, "Unknown"))
            if tonumber(readinessSummary.durability) ~= nil then
                addRow("Durability", string.format("%d%%", math.floor((tonumber(readinessSummary.durability) or 0) + 0.5)))
            else
                addRow("Durability", formatStoredReadinessValue(readinessSummary.durability, "Unknown"))
            end
            addRow("Talents", formatStoredReadinessValue(readinessSummary.talents, "Unknown"))
            addRow("Weekly Vault", formatStoredReadinessValue(readinessSummary.weeklyVault, "Unknown"))

            return rows, tonumber(readinessSummary.readinessPercent), trimString(readinessSummary.statusText)
        end

        return {
            {
                label = "No readiness data captured",
                value = "Save Toon",
                color = { 0.62, 0.82, 1, 1 },
                subtitle = "Use Edit Profile > Save Toon on the active character to store a local scan.",
            },
        }, nil, nil
    end

    local rows = {}
    local function addRow(labelText, valueText)
        local fillColor, textColor = ResolveStatusVisual(valueText)
        rows[#rows + 1] = {
            label = labelText,
            value = valueText,
            color = textColor,
            fill = fillColor,
        }
    end

    local gemMissingCount = tonumber(readinessSnapshot.gems and readinessSnapshot.gems.missing) or 0
    local enchantMissingCount = tonumber(readinessSnapshot.enchants and readinessSnapshot.enchants.missing) or 0
    addRow("Gems", readinessSnapshot.gems and (readinessSnapshot.gems.status == "issues" and string.format("%d Missing", math.max(1, gemMissingCount)) or (readinessSnapshot.gems.status == "ok" or readinessSnapshot.gems.status == "ready") and "Ready" or trimString(readinessSnapshot.gems.status)) or "Unknown")
    addRow("Enchants", readinessSnapshot.enchants and (readinessSnapshot.enchants.status == "issues" and string.format("%d Missing", math.max(1, enchantMissingCount)) or (readinessSnapshot.enchants.status == "ok" or readinessSnapshot.enchants.status == "ready") and "Ready" or trimString(readinessSnapshot.enchants.status)) or "Unknown")
    addRow("Consumables", readinessSnapshot.consumables and ((readinessSnapshot.consumables.status == "ok" or readinessSnapshot.consumables.status == "ready") and "Ready" or readinessSnapshot.consumables.status == "issues" and "Missing" or trimString(readinessSnapshot.consumables.status)) or "Unknown")

    if type(readinessSnapshot.durability) == "table" and tonumber(readinessSnapshot.durability.minPercent) ~= nil then
        addRow("Durability", string.format("%d%%", math.floor((tonumber(readinessSnapshot.durability.minPercent) or 0) + 0.5)))
    else
        addRow("Durability", type(readinessSnapshot.durability) == "table" and trimString(readinessSnapshot.durability.status) ~= "" and readinessSnapshot.durability.status or "Unknown")
    end

    addRow("Talents", formatStoredReadinessValue(readinessSnapshot.talents, "Unknown"))
    addRow("Weekly Vault", formatStoredReadinessValue(readinessSnapshot.weeklyVault, "Unknown"))

    return rows, tonumber(readinessSummary and readinessSummary.readinessPercent), trimString(readinessSummary and readinessSummary.statusText)
end

local function BuildGearAlertRows(selectedRow)
    local readinessSummary = type(selectedRow) == "table" and type(selectedRow.readiness) == "table" and selectedRow.readiness or nil
    local readinessSnapshot = type(selectedRow) == "table" and type(selectedRow.readinessSnapshot) == "table" and selectedRow.readinessSnapshot or nil

    if type(readinessSnapshot) ~= "table" then
        if type(readinessSummary) == "table" then
            local summaryValue = trimString(readinessSummary.statusText) ~= "" and trimString(readinessSummary.statusText) or "Summary Only"
            local _, textColor = ResolveStatusVisual(summaryValue)

            return {
                {
                    label = "Latest readiness",
                    value = summaryValue,
                    color = textColor,
                    subtitle = "Detailed local scan is not available for this toon.",
                },
            }
        end

        return {
            {
                label = "No gear alerts captured",
                value = "Save Toon",
                color = { 0.62, 0.82, 1, 1 },
                subtitle = "The current character needs a local readiness scan first.",
            },
        }
    end

    local alerts = {}
    local function addAlert(labelText, valueText, subtitleText)
        local _, textColor = ResolveStatusVisual(labelText .. " " .. trimString(valueText))
        alerts[#alerts + 1] = {
            label = labelText,
            value = valueText or "",
            color = textColor,
            subtitle = subtitleText or "",
        }
    end

    if readinessSnapshot.gems and readinessSnapshot.gems.status == "issues" then
        addAlert("Gems need attention", "Missing", "Socketed gear still needs gems.")
    end

    if readinessSnapshot.enchants and readinessSnapshot.enchants.status == "issues" then
        addAlert("Enchants need attention", "Missing", "One or more enchants are not applied.")
    end

    if readinessSnapshot.consumables and readinessSnapshot.consumables.status == "issues" then
        addAlert("Consumables missing", "Not Ready", "Current flask or food requirements are not met.")
    end

    if type(readinessSnapshot.durability) == "table" and readinessSnapshot.durability.status == "issues" then
        local minPercent = tonumber(readinessSnapshot.durability.minPercent)
        addAlert("Durability low", minPercent and string.format("%d%%", math.floor(minPercent + 0.5)) or "Low", "Multiple items need repair.")
    end

    if #alerts == 0 then
        alerts[1] = {
            label = "No active gear alerts",
            value = "Clear",
            color = { 0.42, 0.92, 0.36, 1 },
            subtitle = "The current readiness scan is clear.",
        }
    end

    return alerts
end

local function cloneStringList(values)
    local copy = {}
    for _, value in ipairs(type(values) == "table" and values or {}) do
        local trimmed = trimString(value)
        if trimmed ~= "" then
            copy[#copy + 1] = trimmed
        end
    end
    return copy
end

local function hasListValue(values, expected)
    local normalizedExpected = string.lower(trimString(expected))
    for _, value in ipairs(type(values) == "table" and values or {}) do
        if string.lower(trimString(value)) == normalizedExpected then
            return true
        end
    end
    return false
end

updateSelectedToonProfile = function(selectedRow, updates, parent, snapshot)
    if type(selectedRow) ~= "table" or trimString(selectedRow.id) == "" or type(addon.RosterService) ~= "table" or type(addon.RosterService.UpdateMember) ~= "function" then
        return
    end

    local member = type(selectedRow.member) == "table" and selectedRow.member or {}
    local record = type(selectedRow.myToonRecord) == "table" and selectedRow.myToonRecord or {}
    local payload = {
        spec = trimString(selectedRow.spec or member.spec),
        itemLevel = tonumber(member.itemLevel or selectedRow.itemLevel) or 0,
        primaryRole = trimString(record.primaryRole or member.primaryRole or selectedRow.role),
        meleeOrRanged = trimString(member.meleeOrRanged or record.meleeOrRanged),
        secondaryRoles = cloneStringList(record.secondaryRoles or member.secondaryRoles),
        mainCharacter = trimString(record.mainCharacter or selectedRow.mainCharacter or member.mainCharacter),
        guildJoinDate = trimString(record.guildJoinDate or member.guildJoinDate),
        birthdate = trimString(record.birthdate or member.birthdate),
        battleTag = trimString(record.battleTag or member.battleTag),
        note = trimString(record.note or member.note),
        professions = cloneStringList(record.professions or member.professions),
        interests = cloneStringList(record.interests or member.interests),
    }

    for key, value in pairs(type(updates) == "table" and updates or {}) do
        payload[key] = value
    end

    if pcall(addon.RosterService.UpdateMember, addon.RosterService, selectedRow.id, payload) then
        page.selectedToonId = selectedRow.id
        refreshPage(parent, snapshot)
    end
end

local function RenderStats(parent, rightSection, selectedRow, rightContentWidth)
    local statsLayout = addon.NewUILayout and addon.NewUILayout.MyToons and addon.NewUILayout.MyToons.Stats or {}
    local summaryRect = statsLayout.Summary or { x = 0, y = 0, w = 780, h = 214 }
    local readinessRect = statsLayout.Readiness or { x = 0, y = -232, w = 382, h = 198 }
    local gearRect = statsLayout.GearAlerts or { x = 398, y = -232, w = 382, h = 198 }
    local bottomRect = statsLayout.BottomPanel or { x = 24, y = -566, w = 1336, h = 154 }
    local rolesRect = statsLayout.Roles or { x = 0, y = 0, w = 648, h = 130 }
    local notesRect = statsLayout.Notes or { x = 664, y = 0, w = 648, h = 130 }
    local placeholderColor = (theme.Card and theme.Card.Value) or { 0.72, 0.68, 0.58, 1 }
    local readinessSnapshot = selectedRow and selectedRow.readinessSnapshot or nil
    local readinessSummary = selectedRow and selectedRow.readiness or nil
    local member = type(selectedRow) == "table" and type(selectedRow.member) == "table" and selectedRow.member or {}
    local record = type(selectedRow) == "table" and type(selectedRow.myToonRecord) == "table" and selectedRow.myToonRecord or {}

    local function normalizeList(values)
        local resolved = {}
        local seen = {}

        for _, value in ipairs(type(values) == "table" and values or {}) do
            local trimmed = trimString(value)
            if trimmed ~= "" and not seen[trimmed] then
                seen[trimmed] = true
                resolved[#resolved + 1] = trimmed
            end
        end

        return resolved
    end

    local function createSelectableChip(host, x, y, width, labelText, isSelected, onClick, fillColor, borderColor)
        local chip = CreateFrame(onClick and "Button" or "Frame", nil, host, BackdropTemplateMixin and "BackdropTemplate" or nil)
        chip:SetPoint("TOPLEFT", host, "TOPLEFT", x, y)
        chip:SetSize(width, 20)
        if chip.SetBackdrop then
            chip:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8", edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
            chip:SetBackdropColor(fillColor[1], fillColor[2], fillColor[3], isSelected and 0.94 or 0.34)
            chip:SetBackdropBorderColor(borderColor[1], borderColor[2], borderColor[3], isSelected and 1 or 0.55)
        end

        local text = chip:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        text:SetPoint("CENTER", chip, "CENTER", 0, 0)
        text:SetText(formatDisplayLabel(labelText))
        text:SetTextColor(borderColor[1], borderColor[2], borderColor[3], isSelected and 1 or 0.72)

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

        chip.Text = text
        return chip
    end

    local detailCard = CreateSelectedToonCard(rightSection.Content, summaryRect, selectedRow, MYTOONS_PANEL_ART)

    local readinessPanel = CreateInlinePanel(rightSection.Content, readinessRect, "Readiness Checklist", "Panel", MYTOONS_PANEL_ART)
    local readinessRows, readinessPercent, readinessLabel = BuildReadinessRows(selectedRow)
    for index, rowData in ipairs(readinessRows) do
        CreatePanelValueRow(readinessPanel.Content, -((index - 1) * 22), rowData.label, rowData.value, rowData.color, {
            rowHeight = 20,
            subtitle = rowData.subtitle,
        })
    end
    if readinessPercent ~= nil then
        local scoreColorFill, scoreColorText = ResolveStatusVisual(readinessLabel ~= "" and readinessLabel or string.format("%d%%", math.floor(readinessPercent + 0.5)))
        local scoreY = -(#readinessRows * 22) - 6
        local scoreLabelText = readinessPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        scoreLabelText:SetPoint("TOPLEFT", readinessPanel.Content, "TOPLEFT", 0, scoreY)
        scoreLabelText:SetText("Checklist Score")
        scoreLabelText:SetTextColor(0.96, 0.82, 0.46, 1)

        local scoreValue = readinessPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        scoreValue:SetPoint("LEFT", scoreLabelText, "RIGHT", 18, 0)
        scoreValue:SetText(string.format("%d%%", math.floor(readinessPercent + 0.5)))
        scoreValue:SetTextColor(scoreColorText[1], scoreColorText[2], scoreColorText[3], scoreColorText[4] or 1)
    end

    local gearPanel = CreateInlinePanel(rightSection.Content, gearRect, "Gear Alerts", "Panel", MYTOONS_PANEL_ART)
    local gearRows = BuildGearAlertRows(selectedRow)
    for index, rowData in ipairs(gearRows) do
        CreatePanelValueRow(gearPanel.Content, -((index - 1) * 34), rowData.label, rowData.value, rowData.color, {
            rowHeight = 30,
            subtitle = rowData.subtitle,
        })
    end

    local bottomPanel = CreateFrame("Frame", nil, parent)
    ApplyRect(bottomPanel, parent, bottomRect)

    local rolesPanel = CreateInlinePanel(bottomPanel, rolesRect, "Roles & Interests", "Panel", MYTOONS_PANEL_ART)
    local primaryRole = trimString(record.primaryRole or member.primaryRole or selectedRow and selectedRow.role or "")
    local secondaryRoles = normalizeList(record.secondaryRoles or member.secondaryRoles)
    local interests = normalizeList(record.interests or member.interests)
    local roleOptions = addon.RosterService and type(addon.RosterService.GetRoleOptions) == "function" and addon.RosterService:GetRoleOptions(member) or { "tank", "healer", "dps" }
    local interestOptions = cloneStringList(INTEREST_OPTIONS)
    for _, interest in ipairs(interests) do
        if not hasListValue(interestOptions, interest) then
            interestOptions[#interestOptions + 1] = interest
        end
    end

    local primaryLabel = rolesPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    primaryLabel:SetPoint("TOPLEFT", rolesPanel.Content, "TOPLEFT", 0, 0)
    primaryLabel:SetText("Primary Role")
    primaryLabel:SetTextColor(0.96, 0.82, 0.46, 1)

    for index, roleOption in ipairs(roleOptions) do
        createSelectableChip(rolesPanel.Content, (index - 1) * 74, -22, 68, roleOption, string.lower(primaryRole) == string.lower(roleOption), nil, { 0.22, 0.18, 0.08 }, { 0.96, 0.82, 0.46 })
    end

    local secondaryLabel = rolesPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    secondaryLabel:SetPoint("TOPLEFT", rolesPanel.Content, "TOPLEFT", 0, -52)
    secondaryLabel:SetText("Secondary Roles")
    secondaryLabel:SetTextColor(0.96, 0.82, 0.46, 1)

    for index, roleOption in ipairs(roleOptions) do
        createSelectableChip(rolesPanel.Content, (index - 1) * 74, -74, 68, roleOption, hasListValue(secondaryRoles, roleOption), nil, { 0.18, 0.18, 0.22 }, { 0.74, 0.74, 0.82 })
    end

    local rolesDivider = rolesPanel.Content:CreateTexture(nil, "ARTWORK")
    rolesDivider:SetPoint("TOPLEFT", rolesPanel.Content, "TOPLEFT", 236, 0)
    rolesDivider:SetSize(1, math.max(1, rolesRect.h - 42))
    rolesDivider:SetColorTexture(0.44, 0.34, 0.16, 0.45)

    local interestsLabel = rolesPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    interestsLabel:SetPoint("TOPLEFT", rolesPanel.Content, "TOPLEFT", 254, 0)
    interestsLabel:SetText("Content Interests")
    interestsLabel:SetTextColor(0.96, 0.82, 0.46, 1)

    for index, interestOption in ipairs(interestOptions) do
        local rowIndex = math.floor((index - 1) / 3)
        local columnIndex = (index - 1) % 3
        local fillColor, borderColor = resolveInterestTagPalette(interestOption)
        createSelectableChip(rolesPanel.Content, 254 + (columnIndex * 90), -22 - (rowIndex * 26), 82, interestOption, hasListValue(interests, interestOption), nil, fillColor, borderColor)
    end

    local interestRowCount = math.max(1, math.ceil(#interestOptions / 3))

    local availabilityLabel = rolesPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    availabilityLabel:SetPoint("TOPLEFT", rolesPanel.Content, "TOPLEFT", 254, -22 - (interestRowCount * 26) - 4)
    availabilityLabel:SetText("Availability")
    availabilityLabel:SetTextColor(0.96, 0.82, 0.46, 1)

    local availabilityValue = rolesPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    availabilityValue:SetPoint("TOPLEFT", availabilityLabel, "BOTTOMLEFT", 0, -4)
    availabilityValue:SetWidth(math.max(1, rolesRect.w - 266))
    availabilityValue:SetJustifyH("LEFT")
    availabilityValue:SetWordWrap(true)
    availabilityValue:SetText(buildAvailabilityDisplay(record, member) ~= "" and buildAvailabilityDisplay(record, member) or "No availability saved")
    availabilityValue:SetTextColor(0.62, 0.82, 1, 1)

    local notesPanel = CreateInlinePanel(bottomPanel, notesRect, "Notes & Profile", "Panel", MYTOONS_PANEL_ART)
    local selectedToonId = trimString(selectedRow and selectedRow.id)
    local currentPlayerId = addon.Store and addon.Store.GetPlayerName and trimString(addon.Store:GetPlayerName()) or ""
    local savedTimePlayedSeconds = tonumber(record.timePlayedSeconds or member.timePlayedSeconds) or 0
    local timePlayedText = savedTimePlayedSeconds > 0 and formatTimePlayedDuration(savedTimePlayedSeconds) or "-"
    if selectedToonId ~= "" and selectedToonId == currentPlayerId then
        ensureCurrentToonTimePlayedRequest(selectedRow)
        if type(page.State and page.State.timePlayed) == "table" and page.State.timePlayed.toonId == currentPlayerId then
            timePlayedText = formatTimePlayedDuration(page.State.timePlayed.total)
        else
            timePlayedText = "Loading..."
        end
    end

    CreateSummaryBlock(notesPanel.Content, 0, 0, 210, "Main Character", trimString(record.mainCharacter or selectedRow and selectedRow.mainCharacter or "") ~= "" and trimString(record.mainCharacter or selectedRow and selectedRow.mainCharacter or "") or "-", "", "", { 0.96, 0.93, 0.84, 1 }, false)
    CreateSummaryBlock(notesPanel.Content, 224, 0, 126, "Birthday", trimString(record.birthdate or member.birthdate or "") ~= "" and trimString(record.birthdate or member.birthdate or "") or "-", "", "", { 0.96, 0.93, 0.84, 1 }, false)
    CreateSummaryBlock(notesPanel.Content, 0, -42, 210, "BattleTag", trimString(record.battleTag or member.battleTag or "") ~= "" and trimString(record.battleTag or member.battleTag or "") or "-", "", "", { 0.96, 0.93, 0.84, 1 }, false)
    CreateSummaryBlock(notesPanel.Content, 224, -42, 126, "Time Played", timePlayedText, "", "", { 0.62, 0.82, 1, 1 }, false)

    local noteLabel = notesPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    noteLabel:SetPoint("TOPLEFT", notesPanel.Content, "TOPLEFT", 366, 0)
    noteLabel:SetText("Note")
    noteLabel:SetTextColor(0.96, 0.82, 0.46, 1)

    local noteText = notesPanel.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    noteText:SetPoint("TOPLEFT", noteLabel, "BOTTOMLEFT", 0, -4)
    noteText:SetWidth(math.max(1, notesRect.w - 380))
    noteText:SetJustifyH("LEFT")
    noteText:SetJustifyV("TOP")
    noteText:SetText(trimString(record.note or member.note or "") ~= "" and trimString(record.note or member.note or "") or "No profile note saved.")
    noteText:SetTextColor(placeholderColor[1], placeholderColor[2], placeholderColor[3], placeholderColor[4])

    return detailCard
end

local function RenderEditProfilePlaceholder(rightSection, rightContentWidth)
    local rightEmpty = rightSection.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    rightEmpty:SetPoint("TOPLEFT", rightSection.Content, "TOPLEFT", 0, 0)
    rightEmpty:SetWidth(rightContentWidth)
    rightEmpty:SetJustifyH("LEFT")
    rightEmpty:SetText("Edit Profile placeholder remains for Phase 7A. Stats data is live on the left.")
    return rightEmpty
end

local function CreateListRow(parent, offsetY, rowData, isSelected, onClick)
    local rowWidth = math.max(1, math.floor(tonumber(parent:GetWidth()) or 0))
    local rowHeight = 40
    local row = CreateArtFrame(parent, { x = 0, y = offsetY, w = rowWidth, h = rowHeight }, resolveMyToonsAsset("MyToonsRowBackground", { left = 8, right = 8, top = 4, bottom = 4 }, rowWidth, rowHeight))
    local content = row.Content
    local classColor = { GetClassColor(rowData and rowData.className) }
    local classToken = resolveMyToonsClassToken(rowData and rowData.classFile, rowData and rowData.className)
    local _, statusTextColor = ResolveStatusVisual(rowData and rowData.status or "")

    if row.Texture then
        row.Texture:SetVertexColor(1, 1, 1, isSelected and 1 or 0.92)
    end
    if row.SetBackdrop then
        row:SetBackdropBorderColor(isSelected and 0.62 or 0.34, isSelected and 0.48 or 0.34, isSelected and 0.16 or 0.40, 1)
    end

    local iconFrame = CreateFrame("Frame", nil, row, BackdropTemplateMixin and "BackdropTemplate" or nil)
    iconFrame:SetPoint("LEFT", content, "LEFT", 0, 0)
    iconFrame:SetSize(28, 28)
    if iconFrame.SetBackdrop then
        iconFrame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 },
        })
        iconFrame:SetBackdropColor(0.09, 0.09, 0.11, 0.98)
        iconFrame:SetBackdropBorderColor(classColor[1], classColor[2], classColor[3], classColor[4])
    end

    local iconTexture = iconFrame:CreateTexture(nil, "OVERLAY")
    iconTexture:SetPoint("TOPLEFT", iconFrame, "TOPLEFT", 2, -2)
    iconTexture:SetPoint("BOTTOMRIGHT", iconFrame, "BOTTOMRIGHT", -2, 2)
    if classToken ~= "" and type(CLASS_ICON_TCOORDS) == "table" and CLASS_ICON_TCOORDS[classToken] then
        local coords = CLASS_ICON_TCOORDS[classToken]
        iconTexture:SetTexture(MYTOONS_CLASS_ICON_TEXTURE)
        iconTexture:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
    else
        iconTexture:SetColorTexture(classColor[1], classColor[2], classColor[3], 0.22)
    end

    local name = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    name:SetPoint("TOPLEFT", content, "TOPLEFT", 38, -1)
    name:SetWidth(132)
    name:SetWordWrap(false)
    name:SetJustifyH("LEFT")
    name:SetText(trimString(rowData and rowData.name) ~= "" and rowData.name or "-")
    name:SetTextColor(classColor[1], classColor[2], classColor[3], classColor[4])

    local realm = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    realm:SetPoint("TOPLEFT", name, "BOTTOMLEFT", 0, -1)
    realm:SetWidth(132)
    realm:SetWordWrap(false)
    realm:SetJustifyH("LEFT")
    realm:SetText(trimString(rowData and rowData.realm) ~= "" and rowData.realm or "-")
    realm:SetTextColor(0.72, 0.68, 0.58, 1)

    local role = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    role:SetPoint("TOPLEFT", content, "TOPLEFT", 188, -1)
    role:SetWidth(96)
    role:SetWordWrap(false)
    role:SetJustifyH("LEFT")
    role:SetText(trimString(rowData and rowData.role) ~= "" and rowData.role or "-")

    local className = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    className:SetPoint("TOPLEFT", role, "BOTTOMLEFT", 0, -2)
    className:SetWidth(96)
    className:SetWordWrap(false)
    className:SetJustifyH("LEFT")
    className:SetText(trimString(rowData and rowData.className) ~= "" and rowData.className or "-")
    className:SetTextColor(0.72, 0.68, 0.58, 1)

    local itemLevel = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    itemLevel:SetPoint("TOPRIGHT", content, "TOPRIGHT", -116, -1)
    itemLevel:SetWidth(70)
    itemLevel:SetWordWrap(false)
    itemLevel:SetJustifyH("RIGHT")
    itemLevel:SetText(trimString(rowData and rowData.itemLevel) ~= "" and rowData.itemLevel or "-")
    itemLevel:SetTextColor(0.96, 0.93, 0.84, 1)

    local status = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    status:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, -11)
    status:SetWidth(112)
    status:SetWordWrap(false)
    status:SetJustifyH("RIGHT")
    status:SetText(trimString(rowData and rowData.status) ~= "" and rowData.status or "-")
    status:SetTextColor(statusTextColor[1], statusTextColor[2], statusTextColor[3], statusTextColor[4] or 1)

    if onClick then
        row:EnableMouse(true)
        row:SetScript("OnMouseUp", function()
            onClick()
        end)
    end

    row.Content = content
    row.IconFrame = iconFrame
    row.IconTexture = iconTexture
    row.CharacterText = name
    row.RealmText = realm
    row.RoleText = role
    row.ClassText = className
    row.ItemLevelText = itemLevel
    row.StatusText = status
    return row
end

local function getSelectedSubTab(snapshot)
    local myToonsSnapshot = type(snapshot) == "table" and (snapshot.myToons or snapshot.mytoons or snapshot) or {}
    local selected = trimString(myToonsSnapshot.selectedSubTab or myToonsSnapshot.subTab or myToonsSnapshot.activeSubTab or myToonsSnapshot.tab)

    if selected == "edit" or selected == "editprofile" or selected == "edit-profile" then
        return "edit"
    end

    return "stats"
end

local function hasProfileData(record)
    if type(record) ~= "table" or record.deleted then
        return false
    end

    local fields = {
        "spec",
        "itemLevel",
        "primaryRole",
        "meleeOrRanged",
        "secondaryRoles",
        "mainCharacter",
        "guildJoinDate",
        "birthdate",
        "battleTag",
        "professions",
        "interests",
        "note",
    }

    for _, fieldName in ipairs(fields) do
        local value = record[fieldName]

        if type(value) == "table" then
            if next(value) ~= nil then
                return true
            end
        elseif trimString(value) ~= "" then
            return true
        end
    end

    return false
end

formatUpdatedAt = function(value)
    local updatedAt = tonumber(value) or 0

    if updatedAt <= 0 then
        return "-"
    end

    return date("%Y-%m-%d %H:%M:%S", updatedAt)
end

CreateSelectedToonCard = function(parent, rect, selectedRow, options)
    local card = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(card, parent, rect)
    CreateBackdrop(card, "Card", options)

    local cardInsets = addon.NewUIAssets and addon.NewUIAssets.Insets and addon.NewUIAssets.Insets.MyToonsCard or { left = 18, right = 18, top = 12, bottom = 12 }
    local content = CreateArtContentFrame(card, options and options.backgroundTextureKey, rect.w, rect.h, cardInsets)

    local meta = ResolveSelectedToonMeta(selectedRow)
    local classToken = resolveMyToonsClassToken(selectedRow and selectedRow.classFile, meta.className)
    local myToonRecord = type(selectedRow) == "table" and type(selectedRow.myToonRecord) == "table" and selectedRow.myToonRecord or nil
    local playerToonId = addon.Store and addon.Store.GetPlayerName and trimString(addon.Store:GetPlayerName()) or ""
    local isCurrentPlayer = playerToonId ~= "" and selectedRow and (trimString(selectedRow.id) == playerToonId or trimString(selectedRow.name) == playerToonId)
    local factionCardAsset, factionTextColor = resolveFactionVisual(meta.faction)

    local portrait = CreateArtFrame(content, { x = 0, y = -5, w = 96, h = 96 }, resolveMyToonsAsset(factionCardAsset, { left = 18, right = 18, top = 12, bottom = 12 }, 96, 96))

    local portraitTexture = portrait:CreateTexture(nil, "OVERLAY")
    portraitTexture:SetPoint("TOPLEFT", portrait.Content, "TOPLEFT", 0, 0)
    portraitTexture:SetPoint("BOTTOMRIGHT", portrait.Content, "BOTTOMRIGHT", 0, 0)
    if isCurrentPlayer and type(SetPortraitTexture) == "function" then
        SetPortraitTexture(portraitTexture, "player")
        portraitTexture:SetTexCoord(0.07, 0.93, 0.07, 0.93)
    elseif classToken ~= "" and type(CLASS_ICON_TCOORDS) == "table" and CLASS_ICON_TCOORDS[classToken] then
        local coords = CLASS_ICON_TCOORDS[classToken]
        portraitTexture:SetTexture(MYTOONS_CLASS_ICON_TEXTURE)
        portraitTexture:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
    else
        portraitTexture:SetColorTexture(0.72, 0.68, 0.58, 0.18)
    end

    local roleBadge = CreateArtFrame(portrait, { x = 62, y = -62, w = 30, h = 30 }, resolveMyToonsAsset("MyToonsCardBackground", { left = 18, right = 18, top = 12, bottom = 12 }, 30, 30))
    local roleBadgeTexture = roleBadge:CreateTexture(nil, "OVERLAY")
    roleBadgeTexture:SetPoint("TOPLEFT", roleBadge.Content, "TOPLEFT", 0, 0)
    roleBadgeTexture:SetPoint("BOTTOMRIGHT", roleBadge.Content, "BOTTOMRIGHT", 0, 0)
    roleBadgeTexture:SetTexture(resolveSummaryRoleIcon(meta.specRole))

    local contentWidth = math.max(1, math.floor(tonumber(content:GetWidth()) or 0))
    local summaryLeft = 118
    local alertWidth = 168
    local alertX = math.max(0, contentWidth - alertWidth)
    local detailWidth = math.max(220, alertX - summaryLeft - 18)

    local header = nil

    local name = card:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    name:SetPoint("TOPLEFT", content, "TOPLEFT", summaryLeft, -8)
    name:SetWidth(detailWidth)
    name:SetWordWrap(false)
    name:SetJustifyH("LEFT")
    name:SetText(trimString(selectedRow and selectedRow.name) ~= "" and selectedRow.name or "No toon selected")
    name:SetTextColor(0.96, 0.82, 0.46, 1)

    local subtitle = card:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    subtitle:SetPoint("TOPLEFT", content, "TOPLEFT", summaryLeft, -40)
    subtitle:SetWidth(detailWidth)
    subtitle:SetWordWrap(false)
    subtitle:SetJustifyH("LEFT")
    subtitle:SetText(meta.realm)
    subtitle:SetTextColor(0.96, 0.93, 0.84, 1)

    local faction = card:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    faction:SetPoint("TOPLEFT", subtitle, "BOTTOMLEFT", 0, -8)
    faction:SetWidth(detailWidth)
    faction:SetWordWrap(false)
    faction:SetJustifyH("LEFT")
    faction:SetText(meta.faction)
    faction:SetTextColor(factionTextColor[1], factionTextColor[2], factionTextColor[3], factionTextColor[4] or 1)

    local specText = trimString(selectedRow and selectedRow.spec) ~= "" and trimString(selectedRow.spec) or meta.specRole
    local roleText = trimString(selectedRow and selectedRow.role)
    local specRoleField = CreateSummaryBlock(content, 0, -108, 112, "Spec / Role", specText, roleText, resolveSummaryRoleIcon(meta.specRole), { 0.96, 0.93, 0.84, 1 }, false)
    local itemLevelField = CreateSummaryBlock(content, 126, -108, 108, "Item Level", meta.itemLevel, "", "", { 0.96, 0.82, 0.46, 1 }, true)
    local guildField = CreateSummaryBlock(content, 252, -108, 176, "Guild", meta.guild, "", "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Roster-l.png", { 0.96, 0.93, 0.84, 1 }, false)
    local classField = CreateSummaryBlock(content, 446, -108, math.max(70, alertX - 454), "Class", meta.className, "", MYTOONS_CLASS_ICON_TEXTURE, { 0.96, 0.93, 0.84, 1 }, false)
    if classField.Icon and classToken ~= "" and type(CLASS_ICON_TCOORDS) == "table" and CLASS_ICON_TCOORDS[classToken] then
        local coords = CLASS_ICON_TCOORDS[classToken]
        classField.Icon:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
    end

    local footerLine = card:CreateTexture(nil, "ARTWORK")
    footerLine:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -166)
    footerLine:SetSize(math.max(1, alertX - 16), 1)
    footerLine:SetColorTexture(0.44, 0.34, 0.16, 0.45)

    local updatedField = card:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    updatedField:SetPoint("TOPLEFT", content, "TOPLEFT", 18, -182)
    updatedField:SetWidth(math.max(1, alertX - 24))
    updatedField:SetWordWrap(false)
    updatedField:SetText("Last Updated: " .. meta.lastUpdated)
    updatedField:SetTextColor(0.96, 0.93, 0.84, 1)

    local mainField = nil

    for _, x in ipairs({ 116, 242, 436 }) do
        local divider = card:CreateTexture(nil, "ARTWORK")
        divider:SetPoint("TOPLEFT", content, "TOPLEFT", x, -108)
        divider:SetSize(1, 54)
        divider:SetColorTexture(0.44, 0.34, 0.16, 0.45)
    end

    local alerts = {
        { text = "Ready", color = { 0.42, 0.92, 0.36 }, icon = "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Ready-l.png" },
    }
    local readinessSnapshot = type(selectedRow) == "table" and type(selectedRow.readinessSnapshot) == "table" and selectedRow.readinessSnapshot or nil
    if readinessSnapshot and readinessSnapshot.gems and readinessSnapshot.gems.status == "issues" then alerts[#alerts + 1] = { text = "Missing Gem", color = { 0.96, 0.74, 0.24 }, icon = "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_NeedGems-l.png" } end
    if readinessSnapshot and readinessSnapshot.enchants and readinessSnapshot.enchants.status == "issues" then alerts[#alerts + 1] = { text = "Missing Enchant", color = { 0.96, 0.40, 0.28 }, icon = "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Warning-l.png" } end
    if readinessSnapshot and readinessSnapshot.consumables and readinessSnapshot.consumables.status == "issues" then alerts[#alerts + 1] = { text = "Missing Consumables", color = { 0.96, 0.40, 0.28 }, icon = "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Warning-l.png" } end
    if type(readinessSnapshot and readinessSnapshot.durability) == "table" and readinessSnapshot.durability.status == "issues" then alerts[#alerts + 1] = { text = "Low Durability", color = { 0.96, 0.74, 0.24 }, icon = "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Warning-l.png" } end
    if hasProfileData(myToonRecord) then alerts[#alerts + 1] = { text = "Profile Complete", color = { 0.42, 0.68, 1 }, icon = "Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Roster-l.png" } end
    for index = 1, math.min(#alerts, 4) do
        local alert = alerts[index]
        CreateSummaryAlertChip(content, alertX, -2 - ((index - 1) * 40), alertWidth, 34, alert.text, alert.color, alert.icon)
    end

    card.Content = content
    card.HeaderText = header
    card.PortraitFrame = portrait
    card.PortraitTexture = portraitTexture
    card.RoleBadge = roleBadge
    card.NameText = name
    card.SubtitleText = subtitle
    card.FactionText = faction
    card.SpecRoleField = specRoleField
    card.ItemLevelField = itemLevelField
    card.GuildField = guildField
    card.MainField = mainField
    card.ClassField = classField
    card.UpdatedField = updatedField
    return card
end

CreateDetailField = function(parent, x, y, labelText, valueText, width)
    local field = CreateFrame("Frame", nil, parent)
    field:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    field:SetSize(width, 34)

    local label = field:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    label:SetPoint("TOPLEFT", field, "TOPLEFT", 0, 0)
    label:SetText(labelText)
    label:SetTextColor(0.72, 0.68, 0.58, 1)

    local value = field:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    value:SetPoint("TOPLEFT", label, "BOTTOMLEFT", 0, -2)
    value:SetWidth(width)
    value:SetWordWrap(false)
    value:SetText(valueText)

    field.LabelText = label
    field.ValueText = value
    return field
end

local function buildMyToonsRows()
    local resolved = {
        rows = nil,
        noData = false,
    }

    if not addon.RosterService or type(addon.RosterService.ListMyToons) ~= "function" then
        resolved.noData = true
        return resolved
    end

    local okMembers, members = pcall(addon.RosterService.ListMyToons, addon.RosterService)

    if not okMembers or type(members) ~= "table" or #members == 0 then
        resolved.noData = true
        return resolved
    end

    local rows = {}

    for _, member in ipairs(members) do
        if type(member) == "table" then
            local memberId = trimString(member.id or member.fullName or member.name or "")
            local displayName = trimString(member.fullName or member.displayName or member.name or member.id or memberId)
            local specText = trimString(member.primaryRole or member.spec or "")
            local itemLevel = tonumber(member.itemLevel) or 0
            local myToonRecord = nil

            if memberId ~= "" and addon.RosterService and type(addon.RosterService.GetMyToonRecord) == "function" then
                local okRecord, record = pcall(addon.RosterService.GetMyToonRecord, addon.RosterService, memberId)

                if okRecord and type(record) == "table" then
                    myToonRecord = record
                end
            end

            local status = hasProfileData(myToonRecord) and "Profile Data" or "Missing Profile"
            local source = "ListMyToons"
            local resolvedName = displayName ~= "" and displayName or "-"
            local resolvedRole = specText ~= "" and specText or "-"
            local resolvedItemLevel = itemLevel > 0 and tostring(math.floor(itemLevel)) or "-"

            rows[#rows + 1] = {
                id = memberId ~= "" and memberId or resolvedName,
                name = resolvedName,
                realm = trimString(member.realm or member.guildName or ""),
                classFile = trimString(member.classFile or member.class or (myToonRecord and myToonRecord.classFile) or ""),
                className = trimString(member.className or member.class or (myToonRecord and myToonRecord.className) or member.classFile or ""),
                spec = trimString(member.spec or ""),
                role = resolvedRole,
                itemLevel = resolvedItemLevel,
                mainCharacter = trimString(member.mainCharacter or ""),
                updatedAt = tonumber(member.updatedAt) or 0,
                updatedBy = trimString(member.updatedBy or ""),
                member = member,
                myToonRecord = myToonRecord,
                readiness = type(member.readiness) == "table" and member.readiness or nil,
                readinessSnapshot = type(myToonRecord) == "table" and type(myToonRecord.readiness) == "table" and myToonRecord.readiness or nil,
                status = trimString(member.readiness and member.readiness.statusText) ~= "" and member.readiness.statusText or status,
                source = source,
                isLive = true,
            }
        end
    end

    resolved.rows = rows
    return resolved
end

local function collectRealmOptions(rows)
    local options = { "All Realms" }
    local seen = { ["All Realms"] = true }

    for _, row in ipairs(type(rows) == "table" and rows or {}) do
        local realm = trimString(row and row.realm)
        if realm ~= "" and not seen[realm] then
            seen[realm] = true
            options[#options + 1] = realm
        end
    end

    table.sort(options, function(left, right)
        if left == "All Realms" then
            return true
        end
        if right == "All Realms" then
            return false
        end
        return left < right
    end)

    return options
end

local function filterMyToonsRows(rows, searchQuery, selectedRealm)
    local filtered = {}
    local normalizedQuery = string.lower(trimString(searchQuery))
    local activeRealm = trimString(selectedRealm)

    for _, row in ipairs(type(rows) == "table" and rows or {}) do
        local matchesRealm = activeRealm == "" or activeRealm == "All Realms" or trimString(row and row.realm) == activeRealm
        local haystack = string.lower(table.concat({
            trimString(row and row.name),
            trimString(row and row.realm),
            trimString(row and row.className),
            trimString(row and row.role),
            trimString(row and row.spec),
        }, " "))
        local matchesQuery = normalizedQuery == "" or string.find(haystack, normalizedQuery, 1, true) ~= nil

        if matchesRealm and matchesQuery then
            filtered[#filtered + 1] = row
        end
    end

    return filtered
end

local function getSelectedToonId(snapshot, rows)
    local myToonsSnapshot = type(snapshot) == "table" and (snapshot.myToons or snapshot.mytoons or snapshot) or {}
    local selected = trimString(myToonsSnapshot.selectedToonId or myToonsSnapshot.selectedId or myToonsSnapshot.toonId or page.selectedToonId)

    if selected ~= "" then
        return selected
    end

    local playerToonId = addon.Store and addon.Store.GetPlayerName and trimString(addon.Store:GetPlayerName()) or ""
    if playerToonId ~= "" and type(rows) == "table" then
        for _, row in ipairs(rows) do
            if row and (trimString(row.id) == playerToonId or trimString(row.name) == playerToonId) then
                return row.id or row.name
            end
        end
    end

    if type(rows) == "table" and type(rows[1]) == "table" then
        return rows[1].id or rows[1].name or nil
    end

    return nil
end

refreshPage = function(parent, snapshot)
    if page._rootFrame then
        page._rootFrame:Hide()
        page._rootFrame = nil
    end

    return page:Render(parent, snapshot)
end

function page:SelectSubTab(subTab, toonId)
    page.State = page.State or {}
    page.State.selectedSubTab = subTab == "edit" and "edit" or "stats"

    if trimString(toonId) ~= "" then
        page.selectedToonId = toonId
    elseif subTab ~= "edit" then
        page.selectedToonId = nil
    end

    if not page._lastParent then
        return nil
    end

    return refreshPage(page._lastParent, page._lastSnapshot)
end

function page:RefreshSelectedToon(toonId)
    if trimString(toonId) ~= "" then
        page.selectedToonId = toonId
    end

    if not page._lastParent then
        return nil
    end

    return refreshPage(page._lastParent, page._lastSnapshot)
end

function page:Render(parent, snapshot)
    if not parent then
        return nil
    end

    page._lastParent = parent
    page._lastSnapshot = snapshot

    page.State = page.State or {}
    page.State.selectedSubTab = page.State.selectedSubTab or getSelectedSubTab(snapshot) or "stats"
    local selectedSubTab = page.State.selectedSubTab or "stats"
    local rosterSnapshot = buildMyToonsRows()
    page.State.searchQuery = trimString(page.State.searchQuery)
    page.State.selectedRealm = trimString(page.State.selectedRealm)
    local realmOptions = collectRealmOptions(rosterSnapshot.rows)
    if page.State.selectedRealm ~= "" and page.State.selectedRealm ~= "All Realms" then
        local knownRealm = false
        for _, realmName in ipairs(realmOptions) do
            if realmName == page.State.selectedRealm then
                knownRealm = true
                break
            end
        end
        if not knownRealm then
            page.State.selectedRealm = "All Realms"
        end
    end
    local filteredRows = filterMyToonsRows(rosterSnapshot.rows, page.State.searchQuery, page.State.selectedRealm)
    local selectedToonId = getSelectedToonId(snapshot, filteredRows)
    local selectedRow = nil

    if type(filteredRows) == "table" then
        for _, row in ipairs(filteredRows) do
            if row and (row.id == selectedToonId or row.name == selectedToonId) then
                selectedRow = row
                break
            end
        end
    end

    if not selectedRow and type(filteredRows) == "table" and type(filteredRows[1]) == "table" then
        selectedRow = filteredRows[1]
        selectedToonId = selectedRow.id or selectedRow.name
    end

    page.selectedToonId = selectedToonId

    local layout = addon.NewUILayout and addon.NewUILayout.MyToons or nil
    local headerRect = (layout and layout.Header) or {}
    local tabsRect = (layout and layout.Tabs) or {}
    local pageRect = (layout and layout.Page) or { x = 0, y = 0, w = tonumber(parent:GetWidth()) or 1380, h = tonumber(parent:GetHeight()) or 742 }
    local statsLayout = (layout and layout.Stats) or {}
    local leftPanelRect = statsLayout.LeftPanel or { x = 24, y = -130, w = 500, h = 620 }
    local rightPanelRect = statsLayout.RightPanel or { x = 544, y = -130, w = 812, h = 620 }

    local root = CreateFrame("Frame", nil, parent)
    ApplyRect(root, parent, pageRect)
    page._rootFrame = root

    local statsTab = CreateSubTabButton(root, "stats", "Stats", tabsRect.Stats or { x = 24, y = -92, w = 120, h = 28 }, selectedSubTab == "stats", function()
        page:SelectSubTab("stats", page.selectedToonId)
    end, MYTOONS_BUTTON_ART)

    local editTab = CreateSubTabButton(root, "edit", "Edit Profile", tabsRect.Edit or { x = 154, y = -92, w = 140, h = 28 }, selectedSubTab == "edit", function()
        page:SelectSubTab("edit", page.selectedToonId)
    end, MYTOONS_BUTTON_ART)

    if selectedSubTab == "edit" then
        if addon.NewUIMyToonsEditProfile and addon.NewUIMyToonsEditProfile.Render then
            page.EditProfileRoot = addon.NewUIMyToonsEditProfile:Render(root, selectedRow, {
                showStats = function(toonId)
                    return page:SelectSubTab("stats", toonId)
                end,
                showEdit = function(toonId)
                    return page:SelectSubTab("edit", toonId)
                end,
                refresh = function(toonId)
                    return page:RefreshSelectedToon(toonId)
                end,
            })
        end
        return root
    end

    local leftSection = CreateSection(root, leftPanelRect, "Toon List", "Panel", MYTOONS_PANEL_ART)
    local rightSection = CreateSection(root, rightPanelRect, "", "Panel", MYTOONS_PANEL_ART)
    local leftContentWidth = math.max(1, math.floor(tonumber(leftSection.Content:GetWidth()) or 0))
    local rightContentWidth = math.max(1, math.floor(tonumber(rightSection.Content:GetWidth()) or 0))

    local searchRect = {
        x = ((statsLayout.Search or { x = 16 }).x or 16) + 20,
        y = (statsLayout.Search or { y = -18 }).y,
        w = math.max(248, ((statsLayout.Search or { w = 280 }).w or 280) - 12),
        h = (statsLayout.Search or { h = 28 }).h,
    }
    local realmRect = {
        x = ((statsLayout.RealmFilter or { x = 306 }).x or 306) + 15,
        y = (statsLayout.RealmFilter or { y = -18 }).y,
        w = math.max(146, ((statsLayout.RealmFilter or { w = 140 }).w or 140) + 15),
        h = (statsLayout.RealmFilter or { h = 28 }).h,
    }

    local searchRow = CreateFilterInputBar(leftSection.Content, searchRect, "Search toons...", page.State.searchQuery, function(value)
        page.State.searchQuery = value
        refreshPage(parent, snapshot)
    end)
    local realmLabel = page.State.selectedRealm ~= "" and page.State.selectedRealm or "All Realms"
    local openRealmMenu = function()
        local menuItems = {}
        for index, realmName in ipairs(realmOptions) do
            menuItems[index] = {
                text = realmName,
                checked = realmName == realmLabel,
                func = function()
                    page.State.selectedRealm = realmName
                    refreshPage(parent, snapshot)
                end,
            }
        end
        ShowDropdownMenu(realmFilterRow, menuItems)
    end
    local realmFilterRow = CreateFilterSelectBar(leftSection.Content, realmRect, realmLabel, openRealmMenu)
    local filterButton = nil

    if leftSection.TitleText then
        local totalTimePlayedText = leftSection:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        totalTimePlayedText:SetPoint("RIGHT", leftSection.TitleText, "RIGHT", 278, 0)
        totalTimePlayedText:SetJustifyH("RIGHT")
        totalTimePlayedText:SetText("Total time played " .. formatAggregateTimePlayedDuration(sumSavedTimePlayedSeconds(rosterSnapshot.rows)))
        totalTimePlayedText:SetTextColor(0.62, 0.82, 1, 1)
        leftSection.TotalTimePlayedText = totalTimePlayedText
    end

    local headerRow = CreateFrame("Frame", nil, leftSection.Content)
    ApplyRect(headerRow, leftSection.Content, statsLayout.ListHeader or { x = 16, y = -58, w = 468, h = 24 })

    local headerRule = headerRow:CreateTexture(nil, "ARTWORK")
    headerRule:SetPoint("BOTTOMLEFT", headerRow, "BOTTOMLEFT", 0, 0)
    headerRule:SetPoint("BOTTOMRIGHT", headerRow, "BOTTOMRIGHT", 0, 0)
    headerRule:SetHeight(1)
    headerRule:SetColorTexture(0.58, 0.44, 0.16, 0.72)

    local headerOffset = -35

    local headerCharacter = headerRow:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerCharacter:SetPoint("LEFT", headerRow, "LEFT", LIST_COLUMNS.Character.x + headerOffset, 0)
    headerCharacter:SetWidth(LIST_COLUMNS.Character.w)
    headerCharacter:SetWordWrap(false)
    headerCharacter:SetText("Character")
    headerCharacter:SetTextColor(0.96, 0.82, 0.46, 1)

    local headerRole = headerRow:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerRole:SetPoint("LEFT", headerRow, "LEFT", LIST_COLUMNS.SpecRole.x + headerOffset, 0)
    headerRole:SetWidth(LIST_COLUMNS.SpecRole.w)
    headerRole:SetWordWrap(false)
    headerRole:SetText("Spec / Role")
    headerRole:SetTextColor(0.96, 0.82, 0.46, 1)

    local headerItemLevel = headerRow:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerItemLevel:SetPoint("LEFT", headerRow, "LEFT", LIST_COLUMNS.ItemLevel.x + headerOffset, 0)
    headerItemLevel:SetWidth(LIST_COLUMNS.ItemLevel.w)
    headerItemLevel:SetWordWrap(false)
    headerItemLevel:SetJustifyH("RIGHT")
    headerItemLevel:SetText("Item Level")
    headerItemLevel:SetTextColor(0.96, 0.82, 0.46, 1)

    local headerStatus = headerRow:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerStatus:SetPoint("LEFT", headerRow, "LEFT", LIST_COLUMNS.Status.x + headerOffset, 0)
    headerStatus:SetWidth(LIST_COLUMNS.Status.w)
    headerStatus:SetWordWrap(false)
    headerStatus:SetJustifyH("RIGHT")
    headerStatus:SetText("Status")
    headerStatus:SetTextColor(0.96, 0.82, 0.46, 1)

    local rowsViewport = CreateFrame("ScrollFrame", nil, leftSection.Content, "UIPanelScrollFrameTemplate")
    local listRowsRect = statsLayout.ListRows or { x = 16, y = -86, w = 468, h = 420 }
    ApplyRect(rowsViewport, leftSection.Content, { x = listRowsRect.x, y = listRowsRect.y, w = listRowsRect.w, h = math.max(60, listRowsRect.h - 20) })
    local rowsHost = CreateFrame("Frame", nil, rowsViewport)
    rowsHost:SetPoint("TOPLEFT", rowsViewport, "TOPLEFT", 0, 0)
    rowsHost:SetSize(math.max(1, (listRowsRect.w or 468) - 24), math.max(1, #filteredRows * 40))
    rowsViewport:SetScrollChild(rowsHost)

    local footerText = leftSection.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    ApplyFontStringRect(footerText, leftSection.Content, statsLayout.ListFooter or { x = 16, y = -570, w = 468, h = 24 })
    footerText:SetTextColor(0.72, 0.68, 0.58, 1)

    if rosterSnapshot.noData then
        local emptyText = leftSection.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        emptyText:SetPoint("TOPLEFT", rowsHost, "TOPLEFT", 0, 0)
        emptyText:SetWidth(leftContentWidth)
        emptyText:SetJustifyH("LEFT")
        emptyText:SetText("No toon data available")
        page.EmptyText = emptyText
        footerText:SetText("Showing 0 toons")
    else
        local rows = filteredRows or {}

        for index, rowData in ipairs(rows) do
            local isSelected = rowData.id == selectedToonId or rowData.name == selectedToonId
            local offsetY = -((index - 1) * 40)

            page["ToonRow" .. index] = CreateListRow(rowsHost, offsetY, rowData, isSelected, function()
                page.selectedToonId = rowData.id or rowData.name
                refreshPage(parent, snapshot)
            end)
        end

        footerText:SetText(string.format("Showing %d of %d toons", #rows, #(rosterSnapshot.rows or {})))
    end

    page.FooterText = footerText

    if not selectedRow then
        local rightEmpty = rightSection.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        rightEmpty:SetPoint("TOPLEFT", rightSection.Content, "TOPLEFT", 0, 0)
        rightEmpty:SetWidth(rightContentWidth)
        rightEmpty:SetJustifyH("LEFT")
        rightEmpty:SetText("No toon data available")
        page.RightEmptyText = rightEmpty
    else
        local okStats, detailOrErr = pcall(function()
            return RenderStats(root, rightSection, selectedRow, rightContentWidth)
        end)

        if okStats then
            page.DetailCard = detailOrErr
        else
            local err = detailOrErr

            local fallback = rightSection.Content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
            fallback:SetPoint("TOPLEFT", rightSection.Content, "TOPLEFT", 0, 0)
            fallback:SetWidth(rightContentWidth)
            fallback:SetJustifyH("LEFT")
            fallback:SetText("Stats content failed to render: " .. tostring(err))
            page.RightStatsFallback = fallback
        end
    end

    root.SearchRow = searchRow
    root.RealmFilterRow = realmFilterRow
    root.FilterButton = filterButton
    root.HeaderRow = headerRow
    root.RowsViewport = rowsViewport
    root.RowsHost = rowsHost
    root.StatsTab = statsTab
    root.EditTab = editTab
    root.LeftSection = leftSection
    root.RightSection = rightSection
    root.SelectedRow = selectedRow

    return root
end

return page