-- LidCouncil_Editor_RefreshSections.lua
-- Split from Editor.lua to isolate editor section wiring and refresh logic.

local API = rawget(_G, "LIDCOUNCIL_API")
local INTERNAL_API = type(API) == "table" and type(API.__internal) == "table" and API.__internal or nil
if type(INTERNAL_API) ~= "table" then
  error("LidCouncil missing internal API registry.")
end

INTERNAL_API.LIDCOUNCIL_WireEditorRefreshSections = function(editor, deps)
  local RequestEditorWorkflowRefresh = type(INTERNAL_API.LIDCOUNCIL_RequestEditorWorkflowRefresh_Impl) == "function"
    and INTERNAL_API.LIDCOUNCIL_RequestEditorWorkflowRefresh_Impl
    or nil
  local GetEditorThemePalette = type(INTERNAL_API.LIDCOUNCIL_GetEditorThemePalette_Impl) == "function"
    and INTERNAL_API.LIDCOUNCIL_GetEditorThemePalette_Impl
    or nil
  local RootDB = deps.RootDB
  local DB = deps.DB
  local GetDebugStore = deps.GetDebugStore
  local ReportEditorRuntimeError = deps.ReportEditorRuntimeError
  local GetPanelsUnlockedState = deps.GetPanelsUnlockedState
  local EnsureGradientTables = deps.EnsureGradientTables
  local SetDropdownSelection = deps.SetDropdownSelection
  local DisplayProfileName = deps.DisplayProfileName
  local GetEnabledLootProfileName = deps.GetEnabledLootProfileName
  local GetCharacterKey = deps.GetCharacterKey
  local ADDON_VERSION = deps.ADDON_VERSION
  local WOW_CLIENT_VERSION = deps.WOW_CLIENT_VERSION
  local LISTS_PER_PAGE = deps.LISTS_PER_PAGE
  local GetSortedPanelNames = deps.GetSortedPanelNames
  local GetListPageForValue = deps.GetListPageForValue
  local ClampListPage = deps.ClampListPage
  local MakeListButton = deps.MakeListButton
  local MakeSimpleListButton = deps.MakeSimpleListButton
  local MakeProfileButton = deps.MakeProfileButton
  local SelectPanel = deps.SelectPanel
  local UpdatePagerControls = deps.UpdatePagerControls
  local ListBackgroundCategories = deps.ListBackgroundCategories
  local FindCategoryIndex = deps.FindCategoryIndex
  local FindBackgroundCategoryGroupIndex = deps.FindBackgroundCategoryGroupIndex
  local EnsureBackgroundCategoryGroups = deps.EnsureBackgroundCategoryGroups
  local BuildVisibleBackgroundCategoryRows = deps.BuildVisibleBackgroundCategoryRows
  local GetBackgroundCategoryFocusKey = deps.GetBackgroundCategoryFocusKey
  local ToggleBackgroundCategoryGroupCollapsed = deps.ToggleBackgroundCategoryGroupCollapsed
  local ConfigureBackgroundCategoryGroupButton = deps.ConfigureBackgroundCategoryGroupButton
  local BeginBackgroundCategoryGroupDrag = deps.BeginBackgroundCategoryGroupDrag
  local BeginBackgroundCategoryDrag = deps.BeginBackgroundCategoryDrag
  local SelectBackground = deps.SelectBackground
  local GetSortedBackgroundNames = deps.GetSortedBackgroundNames
  local ListBackgrounds = deps.ListBackgrounds
  local FindProfileGroupIndex = deps.FindProfileGroupIndex
  local EnsureProfileGroups = deps.EnsureProfileGroups
  local BuildVisibleProfileRows = deps.BuildVisibleProfileRows
  local GetProfileFocusKey = deps.GetProfileFocusKey
  local ToggleProfileGroupCollapsed = deps.ToggleProfileGroupCollapsed
  local ConfigureProfileGroupButton = deps.ConfigureProfileGroupButton
  local BeginProfileGroupDrag = deps.BeginProfileGroupDrag
  local InvalidateSortedProfileNames = deps.InvalidateSortedProfileNames
  local InvalidateSortedPanelNames = deps.InvalidateSortedPanelNames
  local GetBackgroundPath = deps.GetBackgroundPath
  local CreatePurpleOutline = deps.CreatePurpleOutline

  local function GetDeveloperAccentHex()
    local palette = GetEditorThemePalette and GetEditorThemePalette() or nil
    local color = type(palette) == "table" and type(palette.aboutTitle) == "table"
      and palette.aboutTitle
      or { 0.6588, 0.4784, 0.9098, 1.0 }
    local function ToHex(component)
      local value = tonumber(component) or 1
      value = math.max(0, math.min(255, math.floor((value * 255) + 0.5)))
      return string.format("%02x", value)
    end
    return ToHex(color[1]) .. ToHex(color[2]) .. ToHex(color[3])
  end

  local function ApplyDeveloperAccentMarkup(text)
    return tostring(text or ""):gsub("|cffa87ae8", "|cff" .. GetDeveloperAccentHex())
  end

  local function RequestFullWorkflowRefresh(targetEditor, opts)
    opts = type(opts) == "table" and opts or {}
    local refreshOpts = {
      refreshHiddenPages = opts.refreshHiddenPages ~= false,
      refreshAbout = opts.refreshAbout ~= false,
      refreshPages = {
        Profiles = true,
        Panels = true,
        LiveBoard = true,
        Backgrounds = true,
        LootPresets = true,
      },
    }
    if type(RequestEditorWorkflowRefresh) == "function" then
      return RequestEditorWorkflowRefresh(targetEditor, refreshOpts)
    end
    if type(targetEditor) == "table" and type(targetEditor.RequestWorkflowRefresh) == "function" then
      return targetEditor.RequestWorkflowRefresh(targetEditor, refreshOpts)
    end
    return false
  end

  local function GetSelectedPanelName()
    return deps.GetSelectedPanelName()
  end

  local function GetSelectedBackgroundName()
    return deps.GetSelectedBackgroundName()
  end

  local function SetSelectedBackgroundName(value)
    deps.SetSelectedBackgroundName(value)
  end

  local function GetSelectedBackgroundCategory()
    return deps.GetSelectedBackgroundCategory()
  end

  local function SetSelectedBackgroundCategory(value)
    deps.SetSelectedBackgroundCategory(value)
  end

  local function GetSelectedBackgroundCategoryGroup()
    return deps.GetSelectedBackgroundCategoryGroup()
  end

  local function SetSelectedBackgroundCategoryGroup(value)
    deps.SetSelectedBackgroundCategoryGroup(value)
  end

  function editor:UpdateBgPreviewLayout()
    if not self.bgPreviewFrame or not self.bgPreviewTex then
      return
    end
    local path = self.bgPreviewTex.__path
    if not path or path == "" then
      self.bgPreviewTex:SetAllPoints(true)
      return
    end
    local frameW = self.bgPreviewFrame:GetWidth() or 0
    local frameH = self.bgPreviewFrame:GetHeight() or 0
    if frameW <= 1 or frameH <= 1 then
      return
    end

    local texW, texH
    if C_Texture and C_Texture.GetFileIDFromPath and C_Texture.GetTextureInfo then
      local fileID = C_Texture.GetFileIDFromPath(path)
      if fileID then
        local w, h = C_Texture.GetTextureInfo(fileID)
        if type(w) == "table" then
          texW = w.width
          texH = w.height
        else
          texW = w
          texH = h
        end
      end
    end

    if not texW or not texH or texW <= 0 or texH <= 0 then
      self.bgPreviewTex:ClearAllPoints()
      self.bgPreviewTex:SetAllPoints(true)
      return
    end

    local scaleW = frameW / texW
    local scaleH = frameH / texH
    local scale = math.min(scaleW, scaleH)
    local drawW = math.floor(texW * scale)
    local drawH = math.floor(texH * scale)

    self.bgPreviewTex:ClearAllPoints()
    self.bgPreviewTex:SetPoint("CENTER", 0, 0)
    self.bgPreviewTex:SetSize(drawW, drawH)
  end

  function editor:RefreshAbout()
    if not self.aboutMeta then
      return
    end
    local root = RootDB()
    local debugStore = GetDebugStore()

    self.aboutMeta:SetText(ApplyDeveloperAccentMarkup(
      "|cffa87ae8Title:|r LidCouncil\n" ..
      "|cffa87ae8Version:|r " .. ADDON_VERSION .. "\n" ..
      "|cffa87ae8Author:|r Lidian\n" ..
      "|cffa87ae8WoW Client:|r " .. WOW_CLIENT_VERSION
    ))
    if self.aboutDesc then
      self.aboutDesc:SetText(ApplyDeveloperAccentMarkup([[|cffa87ae8About the creator:|r
My Name is Lidian and I'am playing World of Warcraft since mid-end Classic. The real hook came with Legion when M+ has been added to the game and brought a challenge next to raiding. Altough my interest in the game is focusing on M+ and Raid, I have an affection for a unique personal UI. 


We all know, each raid evening is different and the definition of happiness is written for every individual itself. 
Some find happiness in Progress. Some in Loot. Some in just spending time with their friends and just having a fun evening. In my experience, no matter which one of these are the major factor to a single raider - it is always a combination of all these factors. Only the ratio shifts.

I have found a rather respectful and exemplary guild to Raid with. Our progress is quite quick in Normal and Heroic while the mood is almost 100% of the time really enjoyful. But still – we faced the issue of dissatisfaction almost every time because of the Loot distribution. While it only affects a momentum – those situations still echoes for a while.

From what I observed is, that trying to memorize each raid and each item is impossible. This leads to unfair distribution and assignment of items. It also creates false feels regarding the question – who has already got a lot from the past raids – and who was left out. Making a list and scoresystem surely is the only way to collect and analyse proper data, but it’s a hell of an effort.

I remember back in the days that a DKP System was very popular – but many hated it. So anything close to one part of the DKP System, which includes the auctioneering part, was a major part of the bad reputation this system got.

Eventually i sat down and tried to build a personal scoresystem. I created this within an Excel which then was shared and updated manually while raiding. I’am not saying this list is perfect - but a major reduction of discussions about unfair loot distribution was noticeable. I believe a key factor is simply because the list provides full transparency to everyone and eliminates any "guessing" or "feeling". I surely recommend to use a list, in case you are struggeling with disatisfaction in your raid -  wether it’s LidCouncil or something else.

With this as a backstory, I believe the reason to create this AddOn has become obvious. It’s purpose is to automatize the loot list in order to reduce the manual work on the Excel file. While the AddOn will be publicly shared on CurseForge – i tried to make sure to create an AddOn which can be used by everyone. 


I hope you enjoy this AddOn as much as I do.]]))
    end

    local hasErrors = debugStore and debugStore.errors and #debugStore.errors > 0
    if not hasErrors then
      self.aboutStatusIcon:SetTexture("Interface/Buttons/UI-CheckBox-Check")
      self.aboutStatusIcon:SetVertexColor(0.2, 0.9, 0.2, 1)
      self.aboutStatusText:SetText("No errors found, yeayyyy :)")
      self.aboutStatusText:SetTextColor(0.2, 0.9, 0.2, 1)
    else
      self.aboutStatusIcon:SetTexture("Interface/RAIDFRAME/ReadyCheck-NotReady")
      self.aboutStatusIcon:SetVertexColor(1, 0.2, 0.2, 1)
      self.aboutStatusText:SetText("Errors detected. See details below.")
      self.aboutStatusText:SetTextColor(1, 0.2, 0.2, 1)
    end

    local infoLines = {}
    local okIcon = "|TInterface/RAIDFRAME/ReadyCheck-Ready:14:14:0:0|t"
    local failIcon = "|TInterface/RAIDFRAME/ReadyCheck-NotReady:14:14:0:0|t"
    local function iconFor(msg)
      local l = string.lower(tostring(msg or ""))
      if l:find("fail", 1, true) or l:find("error", 1, true) or l:find("missing", 1, true) then
        return failIcon
      end
      return okIcon
    end
    if debugStore and debugStore.info then
      for _, msg in ipairs(debugStore.info) do
        infoLines[#infoLines + 1] = iconFor(msg) .. " " .. tostring(msg)
      end
    end
    if #infoLines == 0 then
      infoLines[1] = okIcon .. " No status messages yet."
    end
    self.aboutDetails:SetText(table.concat(infoLines, "\n"))

    local errCount = (debugStore and debugStore.errors and #debugStore.errors) or 0
    if errCount == 0 then
      self.aboutErrors:SetText("No errors captured.")
    else
      self.aboutErrors:SetText("Errors captured: " .. tostring(errCount))
    end

    local silentCount = (debugStore and debugStore.silent and #debugStore.silent) or 0
    if self.aboutSilent then
      local devMode = (root and root.ui and root.ui.developerMode) == true
      if devMode then
        if silentCount == 0 then
          self.aboutSilent:SetText("No silent errors captured.\n\nNote: Silent-error tracing is active only while Developer Mode is enabled.")
        else
          self.aboutSilent:SetText("Silent errors captured: " .. tostring(silentCount) .. "\n\nNote: Silent-error tracing is active only while Developer Mode is enabled.")
        end
      else
        self.aboutSilent:SetText("Developer Mode is OFF.\nSilent-error tracing is currently disabled.")
      end
    end
    if self.aboutSection == "RaidTrace" and self.RefreshRaidTestTraceSection then
      self:RefreshRaidTestTraceSection()
    end
  end

  editor.AcquirePooledButton = function(_, pool, index, creator)
    local button = pool[index]
    if not button then
      button = creator()
      pool[index] = button
    end
    return button
  end

  editor.PreparePooledListButton = function(_, button, text, y, onClick, opts)
    if not button then
      return nil
    end
    opts = opts or {}
    button:ClearAllPoints()
    button:SetPoint("TOPLEFT", opts.x or 0, y or 0)
    button:SetSize(opts.width or 200, opts.height or 20)
    button:SetText(text or "")
    button:SetScript("OnClick", onClick)
    button:Show()

    local fs = button:GetFontString()
    if fs then
      fs:SetTextColor(1, 1, 1, 1)
    end

    if button.__glow then button.__glow:Hide() end
    if button.__selectedBorder then button.__selectedBorder:Hide() end
    if button.__lockIcon then button.__lockIcon:Hide() end
    if button.__activeIcon then button.__activeIcon:Hide() end
    if button.__activeBorder then button.__activeBorder:Hide() end
    if button.__profileDragDropBorder then button.__profileDragDropBorder:Hide() end
    if button.__profileDragSourceBorder then button.__profileDragSourceBorder:Hide() end
    if button.__bgDragDropBorder then button.__bgDragDropBorder:Hide() end
    if button.__bgDragSourceBorder then button.__bgDragSourceBorder:Hide() end
    if button.__bgCategoryGroupDragDropBorder then button.__bgCategoryGroupDragDropBorder:Hide() end
    if button.__bgCategoryGroupDragSourceBorder then button.__bgCategoryGroupDragSourceBorder:Hide() end

    return button
  end

  editor.RestoreProfileCurrentLabel = function(self)
    if not self or not self.profileLabel then
      return
    end
    local enabledProfileName = type(GetEnabledLootProfileName) == "function" and tostring(GetEnabledLootProfileName() or "") or ""
    local profileText = enabledProfileName ~= ""
      and DisplayProfileName(enabledProfileName)
      or "No profile activated :("
    self.profileLabel:SetText("Current Profile: " .. profileText)
  end

  editor.RestoreBackgroundsCurrentLabel = function(self)
    if not self or not self.backgroundsCurrentLabel then
      return
    end
    local curCat = GetSelectedBackgroundCategory() or "(none)"
    self.backgroundsCurrentLabel:SetText("Current Category: " .. curCat)
  end

  local RefreshPanelsSection
  local RefreshPanelPropertiesSection
  local RefreshBackgroundsSection
  local RefreshBackgroundListDetailsSection
  local RefreshProfilesSection
  local RefreshProfileDetailsSection

  local function RefreshPanelsListSection(self, db)
    local selectedPanelName = GetSelectedPanelName()
    local order = GetSortedPanelNames(db)
    local panelTotalPages = math.max(1, math.ceil(#order / LISTS_PER_PAGE))
    local selectedPanelPage = GetListPageForValue(order, selectedPanelName, LISTS_PER_PAGE)
    if selectedPanelName ~= self.__lastPagedSelectedPanelName and selectedPanelPage then
      self.panelListPage = selectedPanelPage
    end
    self.panelListPage = ClampListPage(self.panelListPage or 1, panelTotalPages)
    self.__lastPagedSelectedPanelName = selectedPanelName
    local panelStartIndex = ((self.panelListPage - 1) * LISTS_PER_PAGE) + 1
    local panelEndIndex = math.min(#order, panelStartIndex + LISTS_PER_PAGE - 1)

    self.listButtonPool = self.listButtonPool or {}
    self.listButtons = self.listButtons or {}
    for _, b in ipairs(self.listButtonPool) do b:Hide() end
    wipe(self.listButtons)

    local y = 0
    local usedButtons = 0
    for i = panelStartIndex, panelEndIndex do
      local name = order[i]
      usedButtons = usedButtons + 1
      local b = self:AcquirePooledButton(self.listButtonPool, usedButtons, function()
        return MakeListButton(self.listContent, name, y, function() SelectPanel(name) end)
      end)
      self:PreparePooledListButton(b, name, y, function() SelectPanel(name) end)
      local p = db.panels[name]
      if name == selectedPanelName then
        b:GetFontString():SetTextColor(0.6, 0.9, 1.0)
      end
      if b.__glow then b.__glow:SetShown(name == selectedPanelName) end
      if b.__selectedBorder then
        b.__selectedBorder:SetShown(name == selectedPanelName and GetPanelsUnlockedState() and p and not p.locked)
      end
      if b.__lockIcon then
        if p and p.locked then
          b.__lockIcon:SetTexture("Interface/Buttons/LockButton-Locked-Up")
          b.__lockIcon:SetVertexColor(1.0, 0.25, 0.25, 1)
        else
          b.__lockIcon:SetTexture("Interface/Buttons/LockButton-Unlocked-Up")
          b.__lockIcon:SetVertexColor(0.2, 1.0, 0.2, 1)
        end
        b.__lockIcon:Show()
      end
      self.listButtons[#self.listButtons + 1] = b
      y = y - 22
    end
    for i = usedButtons + 1, #self.listButtonPool do
      self.listButtonPool[i]:Hide()
    end
    self.listContent:SetHeight(math.max(1, -y))
    if self.listScroll and self.listScroll.UpdateScrollChildRect then
      self.listScroll:UpdateScrollChildRect()
    end
    if self.listScroll then self.listScroll:SetVerticalScroll(0) end
    UpdatePagerControls(self.panelPrevBtn, self.panelNextBtn, self.panelPageText, self.panelListPage, panelTotalPages)
    if self.delBtn then
      self.delBtn:SetEnabled(selectedPanelName ~= nil and selectedPanelName ~= "")
    end
  end

  local function RefreshPanelPropertiesState(self, root, db)
    local selectedPanelName = GetSelectedPanelName()
    if selectedPanelName and db.panels[selectedPanelName] then
      if self.propsScroll then self.propsScroll:Show() end
      if self.noPanelScroll then self.noPanelScroll:Hide() end
      if self.noPanelFrame then self.noPanelFrame:Hide() end
      local p = db.panels[selectedPanelName]
      p.color = p.color or { 0.05, 0.05, 0.07, 0.8 }
      self.selLabel:SetText("Selected: " .. selectedPanelName)

      self.wSlider:SetValueSilent(p.w or 300)
      self.hSlider:SetValueSilent(p.h or 120)

      local pos = db.positions[selectedPanelName]
      self.xSlider:SetValueSilent(pos and pos.x or 0)
      self.ySlider:SetValueSilent(pos and pos.y or 0)

      if self.borderDrop and self.borderOptions then
        local bval = p.border or "tooltip"
        local btext = bval
        for _, opt in ipairs(self.borderOptions) do
          if opt.value == bval then
            btext = opt.text
            break
          end
        end
        UIDropDownMenu_SetSelectedValue(self.borderDrop, bval)
        UIDropDownMenu_SetText(self.borderDrop, btext)
      end
      if self.lockCheck then
        self.lockCheck:SetChecked(p.locked and true or false)
      end
      do
        local showEditMode = not (p.locked and true or false)
        local editMode = ((root.ui and root.ui.panelEditMode) == "PREVIEW") and "PREVIEW" or "EDITOR"
        if self.editModeBox then self.editModeBox:SetShown(showEditMode) end
        if self.editModeLabel then self.editModeLabel:SetShown(showEditMode) end
        if self.editModeEditorCheck then
          self.editModeEditorCheck:SetShown(showEditMode)
          self.editModeEditorCheck:SetChecked(editMode == "EDITOR")
        end
        if self.editModePreviewCheck then
          self.editModePreviewCheck:SetShown(showEditMode)
          self.editModePreviewCheck:SetChecked(editMode == "PREVIEW")
        end
      end

      if p.gradient then
        EnsureGradientTables(p)
        self.alphaSlider:SetValue(p.gradient.c1[4] or 0.8)
      else
        self.alphaSlider:SetValue((p.color and p.color[4]) or 0.8)
      end
      if self.UpdateColorDesignUI then self:UpdateColorDesignUI() end
    else
      if self.propsScroll then self.propsScroll:Hide() end
      if self.noPanelScroll then self.noPanelScroll:Show() end
      if self.noPanelFrame then self.noPanelFrame:Show() end
      if self.LayoutNoPanel then self:LayoutNoPanel() end
      self.selLabel:SetText("Selected: (none)")
      if self.lockCheck then
        self.lockCheck:SetChecked(false)
      end
      if self.editModeBox then self.editModeBox:Hide() end
      if self.editModeLabel then self.editModeLabel:Hide() end
      if self.editModeEditorCheck then self.editModeEditorCheck:Hide() end
      if self.editModePreviewCheck then self.editModePreviewCheck:Hide() end
      if self.UpdateColorDesignUI then self:UpdateColorDesignUI() end
    end
  end

  RefreshPanelsSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      local root = RootDB()
      local db = DB()
      self.__refreshing = true
      RefreshPanelsListSection(self, db)
      RefreshPanelPropertiesState(self, root, db)
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
    end, function(err)
      ReportEditorRuntimeError("Editor Panels Refresh", err)
    end)
    if not ok then
      self.__refreshing = false
    end
  end

  editor.RefreshPanelsListSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      local db = DB()
      self.__refreshing = true
      RefreshPanelsListSection(self, db)
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
    end, function(err)
      ReportEditorRuntimeError("Editor Panel List Refresh", err)
    end)
    if not ok then
      self.__refreshing = false
    end
  end

  RefreshPanelPropertiesSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      local root = RootDB()
      local db = DB()
      self.__refreshing = true
      RefreshPanelPropertiesState(self, root, db)
      self.__refreshing = false
    end, function(err)
      ReportEditorRuntimeError("Editor Panel Properties Refresh", err)
    end)
    if not ok then
      self.__refreshing = false
    end
  end

  local function RefreshBackgroundsSectionState(self)
    local selectedBackgroundCategory = GetSelectedBackgroundCategory()
    local selectedBackgroundName = GetSelectedBackgroundName()
    local selectedBackgroundCategoryGroup = GetSelectedBackgroundCategoryGroup()

    if self.backgroundsCurrentLabel then
      local curCat = selectedBackgroundCategory or "(none)"
      self.backgroundsCurrentLabel:SetText("Current Category: " .. curCat)
    end
    if selectedBackgroundCategory and not FindCategoryIndex(ListBackgroundCategories(), selectedBackgroundCategory) then
      selectedBackgroundCategory = nil
      selectedBackgroundName = nil
      SetSelectedBackgroundCategory(nil)
      SetSelectedBackgroundName(nil)
    end
    if selectedBackgroundCategoryGroup and not FindBackgroundCategoryGroupIndex(EnsureBackgroundCategoryGroups(), selectedBackgroundCategoryGroup) then
      selectedBackgroundCategoryGroup = nil
      SetSelectedBackgroundCategoryGroup(nil)
    end
    if (not self.__skipBackgroundCategoryListRebuild) and self.bgCatButtons then
      self.bgCatGroupButtonPool = self.bgCatGroupButtonPool or {}
      self.bgCatItemButtonPool = self.bgCatItemButtonPool or {}
      self.bgCatButtons = self.bgCatButtons or {}
      self.bgCatGroupButtons = self.bgCatGroupButtons or {}
      self.bgCatDropButtons = self.bgCatDropButtons or {}
      for _, b in ipairs(self.bgCatGroupButtonPool) do b:Hide() end
      for _, b in ipairs(self.bgCatItemButtonPool) do b:Hide() end
      wipe(self.bgCatButtons)
      wipe(self.bgCatGroupButtons)
      wipe(self.bgCatDropButtons)
      local catRows = BuildVisibleBackgroundCategoryRows(selectedBackgroundCategory)
      if not selectedBackgroundCategory and not selectedBackgroundCategoryGroup and catRows[1] then
        local firstGroupName = nil
        for _, row in ipairs(catRows) do
          if row and row.type == "category" then
            selectedBackgroundCategory = row.name
            SetSelectedBackgroundCategory(row.name)
            break
          elseif row and row.type == "group" and not firstGroupName then
            firstGroupName = row.name
          end
        end
        if not selectedBackgroundCategory and firstGroupName then
          selectedBackgroundCategoryGroup = firstGroupName
          SetSelectedBackgroundCategoryGroup(firstGroupName)
        end
      end
      local catFocusKey = GetBackgroundCategoryFocusKey(selectedBackgroundCategory, selectedBackgroundCategoryGroup)
      local bgCatTotalPages = math.max(1, math.ceil(#catRows / LISTS_PER_PAGE))
      local selectedCatPage = GetListPageForValue(catRows, catFocusKey, LISTS_PER_PAGE, function(row)
        return row and row.key
      end)
      if catFocusKey ~= self.__lastPagedBackgroundCategoryKey and selectedCatPage then
        self.bgCatPage = selectedCatPage
      end
      self.bgCatPage = ClampListPage(self.bgCatPage or 1, bgCatTotalPages)
      self.__lastPagedBackgroundCategoryKey = catFocusKey
      local bgCatStartIndex = ((self.bgCatPage - 1) * LISTS_PER_PAGE) + 1
      local bgCatEndIndex = math.min(#catRows, bgCatStartIndex + LISTS_PER_PAGE - 1)
      local yc = 0
      local usedGroupButtons = 0
      local usedItemButtons = 0
      for i = bgCatStartIndex, bgCatEndIndex do
        local row = catRows[i]
        if row and row.type == "group" then
          local label = string.format("%s (%d)", row.name or "Group", row.childCount or 0)
          usedGroupButtons = usedGroupButtons + 1
          local b = self:AcquirePooledButton(self.bgCatGroupButtonPool, usedGroupButtons, function()
            return MakeSimpleListButton(self.bgCatContent, label, yc, function()
              SetSelectedBackgroundCategoryGroup(row.name)
              SetSelectedBackgroundCategory(nil)
              SetSelectedBackgroundName(nil)
              RefreshBackgroundsSection(self)
            end)
          end)
          self:PreparePooledListButton(b, label, yc, function()
            SetSelectedBackgroundCategoryGroup(row.name)
            SetSelectedBackgroundCategory(nil)
            SetSelectedBackgroundName(nil)
            RefreshBackgroundsSection(self)
          end)
          b.__backgroundCategoryGroupName = row.name
          b.__bgCategoryGroupDragDropBorder = b.__bgCategoryGroupDragDropBorder or CreatePurpleOutline(b)
          b.__bgCategoryGroupDragDropBorder:Hide()
          b.__lid_customBg = {0.1804, 0.1216, 0.2863, 0.98}
          b.__lid_customHover = {0.72, 0.86, 1.00, 0.24}
          local isSelectedGroup = (selectedBackgroundCategoryGroup == row.name)
          local groupTextColor
          if isSelectedGroup or row.containsSelected then
            groupTextColor = {0.6, 0.9, 1.0, 1.0}
          else
            groupTextColor = {0.92, 0.82, 1.0, 1.0}
          end
          b.__lid_customText = groupTextColor
          b.__onToggleBackgroundCategoryGroup = function()
            ToggleBackgroundCategoryGroupCollapsed(row.name)
            SetSelectedBackgroundCategoryGroup(row.name)
            SetSelectedBackgroundCategory(nil)
            SetSelectedBackgroundName(nil)
            RefreshBackgroundsSection(self)
          end
          ConfigureBackgroundCategoryGroupButton(b, label, row.collapsed)
          if b.__bgCategoryGroupArrow then
            b.__bgCategoryGroupArrow:SetTextColor(groupTextColor[1], groupTextColor[2], groupTextColor[3], groupTextColor[4] or 1)
          end
          if b.__glow then b.__glow:SetShown(isSelectedGroup or (row.containsSelected and row.collapsed)) end
          self.bgCatButtons[#self.bgCatButtons + 1] = b
          self.bgCatGroupButtons[#self.bgCatGroupButtons + 1] = b
        elseif row and row.type == "category" then
          local name = row.name
          local label = row.groupName and ("  " .. name) or name
          usedItemButtons = usedItemButtons + 1
          local b = self:AcquirePooledButton(self.bgCatItemButtonPool, usedItemButtons, function()
            return MakeSimpleListButton(self.bgCatContent, label, yc, function()
              SetSelectedBackgroundCategory(name)
              SetSelectedBackgroundCategoryGroup(nil)
              SetSelectedBackgroundName(nil)
              RefreshBackgroundsSection(self)
            end)
          end)
          self:PreparePooledListButton(b, label, yc, function()
            SetSelectedBackgroundCategory(name)
            SetSelectedBackgroundCategoryGroup(nil)
            SetSelectedBackgroundName(nil)
            RefreshBackgroundsSection(self)
          end, {
            x = row.groupName and 16 or 0,
            width = row.groupName and 184 or 200,
          })
          b.__backgroundCategoryName = name
          b.__bgDragDropBorder = b.__bgDragDropBorder or CreatePurpleOutline(b)
          b.__bgDragDropBorder:Hide()
          b.__bgCategoryGroupDragSourceBorder = b.__bgCategoryGroupDragSourceBorder or CreatePurpleOutline(b)
          b.__bgCategoryGroupDragSourceBorder:Hide()
          b:RegisterForDrag("LeftButton")
          b:SetScript("OnDragStart", function(button)
            BeginBackgroundCategoryGroupDrag(name, row.groupName, button)
          end)

          if name == selectedBackgroundCategory then
            b:GetFontString():SetTextColor(0.6, 0.9, 1.0)
          end
          if b.__glow then b.__glow:SetShown(name == selectedBackgroundCategory) end
          self.bgCatButtons[#self.bgCatButtons + 1] = b
          self.bgCatDropButtons[#self.bgCatDropButtons + 1] = b
        end
        yc = yc - 22
      end
      for i = usedGroupButtons + 1, #self.bgCatGroupButtonPool do
        self.bgCatGroupButtonPool[i]:Hide()
      end
      for i = usedItemButtons + 1, #self.bgCatItemButtonPool do
        self.bgCatItemButtonPool[i]:Hide()
      end
      if self.bgCatContent then
        self.bgCatContent:SetHeight(math.max(1, -yc))
      end
      if self.bgCatScroll and self.bgCatScroll.UpdateScrollChildRect then
        self.bgCatScroll:UpdateScrollChildRect()
      end
      if self.bgCatScroll then self.bgCatScroll:SetVerticalScroll(0) end
      UpdatePagerControls(self.bgCatPrevBtn, self.bgCatNextBtn, self.bgCatPageText, self.bgCatPage, bgCatTotalPages)
      if self.bgDeleteCatBtn then
        self.bgDeleteCatBtn:SetEnabled(selectedBackgroundCategory ~= nil and selectedBackgroundCategory ~= "")
      end
      self.bgSelectedCategoryGroupName = selectedBackgroundCategoryGroup
    end

    if not self.__skipBackgroundListRebuild and self.bgListButtons then
      for _, b in ipairs(self.bgListButtons) do b:Hide() end
      wipe(self.bgListButtons)
      local list = ListBackgrounds and ListBackgrounds(selectedBackgroundCategory) or {}
      local sortedList = GetSortedBackgroundNames(list)
      local bgListTotalPages = math.max(1, math.ceil(#sortedList / LISTS_PER_PAGE))
      local selectedBgPage = GetListPageForValue(sortedList, selectedBackgroundName, LISTS_PER_PAGE)
      if selectedBackgroundName ~= self.__lastPagedBackgroundName and selectedBgPage then
        self.bgListPage = selectedBgPage
      end
      self.bgListPage = ClampListPage(self.bgListPage or 1, bgListTotalPages)
      self.__lastPagedBackgroundName = selectedBackgroundName
      local bgListStartIndex = ((self.bgListPage - 1) * LISTS_PER_PAGE) + 1
      local bgListEndIndex = math.min(#sortedList, bgListStartIndex + LISTS_PER_PAGE - 1)
      local yb = 0
      for i = bgListStartIndex, bgListEndIndex do
        local name = sortedList[i]
        local b = MakeSimpleListButton(self.bgListContent, name, yb, function() SelectBackground(name) end)
        b.__backgroundItemName = name
        b.__bgDragSourceBorder = b.__bgDragSourceBorder or CreatePurpleOutline(b)
        b.__bgDragSourceBorder:Hide()
        b:RegisterForDrag("LeftButton")
        b:SetScript("OnDragStart", function(button)
          BeginBackgroundCategoryDrag(name, selectedBackgroundCategory, button)
        end)
        if name == selectedBackgroundName then
          b:GetFontString():SetTextColor(0.6, 0.9, 1.0)
        end
        if b.__glow then b.__glow:SetShown(name == selectedBackgroundName) end
        self.bgListButtons[#self.bgListButtons + 1] = b
        yb = yb - 22
      end
      if self.bgListContent then
        self.bgListContent:SetHeight(math.max(1, -yb))
      end
      if self.bgListScroll and self.bgListScroll.UpdateScrollChildRect then
        self.bgListScroll:UpdateScrollChildRect()
      end
      if self.bgListScroll then self.bgListScroll:SetVerticalScroll(0) end
      UpdatePagerControls(self.bgPrevBtn, self.bgNextBtn, self.bgPageText, self.bgListPage, bgListTotalPages)
      if self.bgDeleteListBtn then
        self.bgDeleteListBtn:SetEnabled(selectedBackgroundName ~= nil and selectedBackgroundName ~= "")
      end
    end

    if self.__skipBackgroundDetailsRebuild then
      return
    end

    if self.bgSelLabel then
      if selectedBackgroundName then
        self.bgSelLabel:SetText("Selected: " .. selectedBackgroundName)
        if self.bgSourceText then
          self.bgSourceText:SetText("Source:\nAddOns/LidCouncil_UserMedia/Your_Backgrounds/" .. selectedBackgroundName .. ".tga")
          self.bgSourceText:Show()
        end
        if self.bgPreviewTex then
          local path = (GetBackgroundPath and GetBackgroundPath(selectedBackgroundName))
            or ("Interface\\AddOns\\LidCouncil_UserMedia\\Your_Backgrounds\\" .. selectedBackgroundName .. ".tga")
          local hasTex = true
          if C_Texture and C_Texture.GetFileIDFromPath then
            hasTex = (C_Texture.GetFileIDFromPath(path) ~= nil)
          end
          if hasTex then
            self.bgPreviewTex:SetTexture(path)
            self.bgPreviewTex.__path = path
          else
            self.bgPreviewTex:SetTexture(nil)
            self.bgPreviewTex.__path = nil
          end
        end
        if self.bgSourceText then self.bgSourceText:Show() end
        if self.bgReloadNote then self.bgReloadNote:Show() end
        if self.bgPreviewFrame then self.bgPreviewFrame:Show() end
        if self.bgEmptyText then self.bgEmptyText:Hide() end
        if self.bgRenameBtn then self.bgRenameBtn:Show(); self.bgRenameBtn:Enable() end
        if self.bgDeleteBtn then self.bgDeleteBtn:Show(); self.bgDeleteBtn:Enable() end
        if self.bgRenameCatBtn then self.bgRenameCatBtn:Hide(); self.bgRenameCatBtn:Disable() end
        if self.bgDeleteCatPropsBtn then self.bgDeleteCatPropsBtn:Hide(); self.bgDeleteCatPropsBtn:Disable() end
        if self.bgRenameCatGroupBtn then self.bgRenameCatGroupBtn:Hide(); self.bgRenameCatGroupBtn:Disable() end
        if self.bgDeleteCatGroupPropsBtn then self.bgDeleteCatGroupPropsBtn:Hide(); self.bgDeleteCatGroupPropsBtn:Disable() end
        if self.UpdateBgPreviewLayout then self:UpdateBgPreviewLayout() end
        if self.UpdateBgPropsLayout then self:UpdateBgPropsLayout() end
      elseif selectedBackgroundCategoryGroup then
        self.bgSelLabel:SetText("Selected Group: " .. selectedBackgroundCategoryGroup)
        if self.bgSourceText then self.bgSourceText:SetText("Source:\nAddOns/LidCouncil_UserMedia/Your_Backgrounds/"); self.bgSourceText:Hide() end
        if self.bgReloadNote then self.bgReloadNote:Hide() end
        if self.bgPreviewTex then self.bgPreviewTex:SetTexture(nil) end
        if self.bgPreviewTex then self.bgPreviewTex.__path = nil end
        if self.bgPreviewFrame then self.bgPreviewFrame:Hide() end
        if self.bgEmptyText then
          self.bgEmptyText:SetText("Open the group or drag categories into it.\nDeleting the group keeps all categories and makes them groupless.")
          self.bgEmptyText:Show()
        end
        if self.bgRenameBtn then self.bgRenameBtn:Hide(); self.bgRenameBtn:Disable() end
        if self.bgDeleteBtn then self.bgDeleteBtn:Hide(); self.bgDeleteBtn:Disable() end
        if self.bgRenameCatBtn then self.bgRenameCatBtn:Hide(); self.bgRenameCatBtn:Disable() end
        if self.bgDeleteCatPropsBtn then self.bgDeleteCatPropsBtn:Hide(); self.bgDeleteCatPropsBtn:Disable() end
        if self.bgRenameCatGroupBtn then self.bgRenameCatGroupBtn:Show(); self.bgRenameCatGroupBtn:Enable() end
        if self.bgDeleteCatGroupPropsBtn then self.bgDeleteCatGroupPropsBtn:Show(); self.bgDeleteCatGroupPropsBtn:Enable() end
        if self.UpdateBgPropsLayout then self:UpdateBgPropsLayout() end
      elseif selectedBackgroundCategory then
        self.bgSelLabel:SetText("Selected Category: " .. selectedBackgroundCategory)
        if self.bgSourceText then self.bgSourceText:SetText("Source:\nAddOns/LidCouncil_UserMedia/Your_Backgrounds/"); self.bgSourceText:Hide() end
        if self.bgReloadNote then self.bgReloadNote:Hide() end
        if self.bgPreviewTex then self.bgPreviewTex:SetTexture(nil) end
        if self.bgPreviewTex then self.bgPreviewTex.__path = nil end
        if self.bgPreviewFrame then self.bgPreviewFrame:Hide() end
        if self.bgEmptyText then
          self.bgEmptyText:SetText("Add or select a background.")
          self.bgEmptyText:Show()
        end
        if self.bgRenameBtn then self.bgRenameBtn:Hide(); self.bgRenameBtn:Disable() end
        if self.bgDeleteBtn then self.bgDeleteBtn:Hide(); self.bgDeleteBtn:Disable() end
        if self.bgRenameCatBtn then self.bgRenameCatBtn:Show(); self.bgRenameCatBtn:Enable() end
        if self.bgDeleteCatPropsBtn then self.bgDeleteCatPropsBtn:Show(); self.bgDeleteCatPropsBtn:Enable() end
        if self.bgRenameCatGroupBtn then self.bgRenameCatGroupBtn:Hide(); self.bgRenameCatGroupBtn:Disable() end
        if self.bgDeleteCatGroupPropsBtn then self.bgDeleteCatGroupPropsBtn:Hide(); self.bgDeleteCatGroupPropsBtn:Disable() end
        if self.UpdateBgPropsLayout then self:UpdateBgPropsLayout() end
      else
        self.bgSelLabel:SetText("Selected: (none)")
        if self.bgSourceText then self.bgSourceText:SetText("Source:\nAddOns/LidCouncil_UserMedia/Your_Backgrounds/"); self.bgSourceText:Hide() end
        if self.bgReloadNote then self.bgReloadNote:Hide() end
        if self.bgPreviewTex then self.bgPreviewTex:SetTexture(nil) end
        if self.bgPreviewTex then self.bgPreviewTex.__path = nil end
        if self.bgPreviewFrame then self.bgPreviewFrame:Hide() end
        if self.bgEmptyText then
          self.bgEmptyText:SetText("Select or create a category first.")
          self.bgEmptyText:Show()
        end
        if self.bgRenameBtn then self.bgRenameBtn:Hide(); self.bgRenameBtn:Disable() end
        if self.bgDeleteBtn then self.bgDeleteBtn:Hide(); self.bgDeleteBtn:Disable() end
        if self.bgRenameCatBtn then self.bgRenameCatBtn:Hide(); self.bgRenameCatBtn:Disable() end
        if self.bgDeleteCatPropsBtn then self.bgDeleteCatPropsBtn:Hide(); self.bgDeleteCatPropsBtn:Disable() end
        if self.bgRenameCatGroupBtn then self.bgRenameCatGroupBtn:Hide(); self.bgRenameCatGroupBtn:Disable() end
        if self.bgDeleteCatGroupPropsBtn then self.bgDeleteCatGroupPropsBtn:Hide(); self.bgDeleteCatGroupPropsBtn:Disable() end
        if self.UpdateBgPropsLayout then self:UpdateBgPropsLayout() end
      end
    end
  end

  RefreshBackgroundsSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      self.__refreshing = true
      RefreshBackgroundsSectionState(self)
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
    end, function(err)
      ReportEditorRuntimeError("Editor Backgrounds Refresh", err)
    end)
    if not ok then
      self.__refreshing = false
    end
  end

  RefreshBackgroundListDetailsSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      self.__refreshing = true
      self.__skipBackgroundCategoryListRebuild = true
      RefreshBackgroundsSectionState(self)
      self.__skipBackgroundCategoryListRebuild = nil
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
    end, function(err)
      ReportEditorRuntimeError("Editor Background List/Details Refresh", err)
    end)
    if not ok then
      self.__skipBackgroundCategoryListRebuild = nil
      self.__refreshing = false
    end
  end

  editor.RefreshBackgroundCategoryListSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      self.__refreshing = true
      self.__skipBackgroundListRebuild = true
      self.__skipBackgroundDetailsRebuild = true
      RefreshBackgroundsSectionState(self)
      self.__skipBackgroundListRebuild = nil
      self.__skipBackgroundDetailsRebuild = nil
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
    end, function(err)
      ReportEditorRuntimeError("Editor Background Category List Refresh", err)
    end)
    if not ok then
      self.__skipBackgroundListRebuild = nil
      self.__skipBackgroundDetailsRebuild = nil
      self.__refreshing = false
    end
  end

  editor.RefreshBackgroundListSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      self.__refreshing = true
      self.__skipBackgroundCategoryListRebuild = true
      self.__skipBackgroundDetailsRebuild = true
      RefreshBackgroundsSectionState(self)
      self.__skipBackgroundCategoryListRebuild = nil
      self.__skipBackgroundDetailsRebuild = nil
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
    end, function(err)
      ReportEditorRuntimeError("Editor Background List Refresh", err)
    end)
    if not ok then
      self.__skipBackgroundCategoryListRebuild = nil
      self.__skipBackgroundDetailsRebuild = nil
      self.__refreshing = false
    end
  end

  local function SetBannerDiagnostic(fontString, text, color)
    if not fontString then
      return
    end
    fontString:SetText(text)
    if fontString.SetTextColor and type(color) == "table" then
      fontString.__lid_customTextColor = color
      fontString:SetTextColor(color[1], color[2], color[3], color[4] or 1)
    end
  end

  local function SetBannerSyncActivity(self, active, value, color, showStatic, determinate)
    local bar = self and self.syncActivityBar
    if not bar then
      return
    end
    local monitoringEnabled = type(self.IsHeaderMonitoringEnabled) ~= "function"
      or self:IsHeaderMonitoringEnabled()
    bar.__lidSyncActive = active == true
    bar.__lidSyncVisible = active == true or showStatic == true
    bar.__lidSyncDeterminate = determinate == true
    bar.__lidSyncPulseElapsed = 0
    if (active or showStatic) and monitoringEnabled then
      if bar.SetStatusBarColor and type(color) == "table" then
        bar:SetStatusBarColor(color[1], color[2], color[3], color[4] or 0.90)
      end
      if bar.SetValue then
        local maximumValue = determinate == true and 1.00 or 0.95
        local minimumValue = determinate == true and 0 or 0.05
        local targetValue = math.max(minimumValue, math.min(maximumValue, tonumber(value) or 0.35))
        bar.__lidSyncTargetValue = targetValue
        if determinate == true then
          if bar.__lidSyncDisplayedValue == nil or self.__lidSyncProgressReset == true then
            bar.__lidSyncDisplayedValue = 0
            bar:SetValue(0)
          end
          self.__lidSyncProgressReset = nil
        else
          bar.__lidSyncDisplayedValue = targetValue
          bar:SetValue(targetValue)
        end
      end
      bar:Show()
    else
      if bar.SetValue then
        bar:SetValue(0)
      end
      bar.__lidSyncVisible = false
      bar.__lidSyncDeterminate = false
      bar.__lidSyncTargetValue = 0
      bar.__lidSyncDisplayedValue = 0
      bar:Hide()
    end
  end

  local function GetBannerTransferProgress(self, direction, peer, completedChunks, totalChunks)
    local completed = math.max(0, tonumber(completedChunks) or 0)
    local total = math.max(0, tonumber(totalChunks) or 0)
    if total <= 0 then
      return nil, nil
    end

    local now = type(GetTime) == "function" and GetTime() or 0
    local key = tostring(direction or "") .. "\31" .. tostring(peer or "") .. "\31" .. tostring(total)
    local tracker = type(self.__lidSyncTransferProgress) == "table" and self.__lidSyncTransferProgress or {}
    self.__lidSyncTransferProgress = tracker
    local state = type(tracker[key]) == "table" and tracker[key] or nil
    if not state or completed < (tonumber(state.completed) or 0) then
      state = {
        completed = completed,
        observedAt = now,
        rate = 0,
      }
      tracker[key] = state
      self.__lidSyncProgressReset = true
    elseif completed > (tonumber(state.completed) or 0) then
      local elapsed = math.max(0.01, now - (tonumber(state.observedAt) or now))
      local sampleRate = (completed - (tonumber(state.completed) or 0)) / elapsed
      local previousRate = math.max(0, tonumber(state.rate) or 0)
      state.rate = previousRate > 0 and ((previousRate * 0.65) + (sampleRate * 0.35)) or sampleRate
      state.completed = completed
      state.observedAt = now
    end

    local progress = math.min(1, completed / total)
    local etaSeconds
    local rate = math.max(0, tonumber(state.rate) or 0)
    if completed > 0 and completed < total and rate >= 0.25 then
      etaSeconds = math.max(1, math.ceil((total - completed) / rate))
    end
    return progress, etaSeconds
  end

  -- Most communication endpoints are private registry APIs; older core helpers remain global.
  local function ResolveBannerApiFunction(name)
    local registryFunction = type(API) == "table" and API[name] or nil
    if type(registryFunction) == "function" then
      return registryFunction
    end
    local globalFunction = rawget(_G, name)
    if type(globalFunction) == "function" then
      return globalFunction
    end
    return nil
  end

  local function ResolveBannerLocalAutoPassState(self)
    local getLocalAutoPassEnabled = ResolveBannerApiFunction("LIDCOUNCIL_GetLocalAutoPassEnabled")
    local localAutoPassEnabled = false
    local stateSource = "saved local setting"
    if type(getLocalAutoPassEnabled) == "function" then
      local ok, result = pcall(getLocalAutoPassEnabled)
      if ok then
        localAutoPassEnabled = result and true or false
      else
        stateSource = "local setting unavailable"
      end
    else
      stateSource = "local setting unavailable"
    end

    local getActiveLootSession = ResolveBannerApiFunction("LIDCOUNCIL_GetActiveLootSession")
    local activeSession = nil
    if type(getActiveLootSession) == "function" then
      local ok, session = pcall(getActiveLootSession)
      if ok and type(session) == "table" then
        activeSession = session
      end
    end

    local sessionId = tostring(type(activeSession) == "table" and activeSession.id or "")
    if self.__lidHeaderAutoPassSessionId ~= sessionId then
      self.__lidHeaderAutoPassSessionId = sessionId
      self.__lidHeaderAutoPassOverride = nil
    end

    -- The session member record is updated with the same confirmed preference
    -- shown in Current Session, so it is the best source while a session is open.
    if type(activeSession) == "table" and type(activeSession.members) == "table" then
      local selfKey = ""
      if type(GetCharacterKey) == "function" then
        selfKey = tostring(GetCharacterKey() or "")
      end
      if selfKey == "" then
        local getCharacterKey = ResolveBannerApiFunction("LIDCOUNCIL_GetCharacterKey")
        if type(getCharacterKey) == "function" then
          local ok, result = pcall(getCharacterKey)
          if ok then
            selfKey = tostring(result or "")
          end
        end
      end
      if selfKey ~= "" then
        local selfMember = activeSession.members[selfKey]
        if type(selfMember) ~= "table" then
          local wantedKey = string.lower(selfKey)
          for memberKey, member in pairs(activeSession.members) do
            if string.lower(tostring(memberKey or "")) == wantedKey
              or string.lower(tostring(type(member) == "table" and member.name or "")) == wantedKey
            then
              selfMember = member
              break
            end
          end
        end
        if type(selfMember) == "table" and selfMember.localAutoPassEnabled ~= nil then
          localAutoPassEnabled = selfMember.localAutoPassEnabled and true or false
          stateSource = "current session member"
        end
      end
    end

    if type(self.__lidHeaderAutoPassOverride) == "boolean" then
      localAutoPassEnabled = self.__lidHeaderAutoPassOverride
      stateSource = "confirmed local change"
    end
    self.__lidHeaderAutoPassDetail = string.format(
      "Personal Auto Pass: %s (%s).",
      localAutoPassEnabled and "ON" or "OFF",
      stateSource
    )
    return localAutoPassEnabled
  end

  local function FormatBannerElapsed(seconds)
    local elapsed = math.max(0, math.floor(tonumber(seconds) or 0))
    local hours = math.floor(elapsed / 3600)
    local minutes = math.floor((elapsed % 3600) / 60)
    local remainingSeconds = elapsed % 60
    if hours > 0 then
      return string.format("%d:%02d:%02d", hours, minutes, remainingSeconds)
    end
    return string.format("%02d:%02d", minutes, remainingSeconds)
  end

  local function RefreshSessionPresentation(self)
    if not self then
      return
    end
    local getSessionStatus = ResolveBannerApiFunction("LIDCOUNCIL_GetSessionPresentationStatus")
    local status = {}
    if type(getSessionStatus) == "function" then
      local ok, result = pcall(getSessionStatus)
      if ok and type(result) == "table" then
        status = result
      end
    end

    local state = tostring(status.state or "inactive")
    local label = tostring(status.label or "NO SESSION")
    local collapsed = self.__collapsed == true
    local displayLabel = collapsed and label:gsub("%s+SESSION$", "") or label
    if state == "inactive" and collapsed then
      displayLabel = "IDLE"
    end
    local participating = status.participating == true
    local preparing = state == "preparing"
    local accentColor = participating
      and { 0.28, 0.92, 0.42, 1.00 }
      or (preparing and { 1.00, 0.72, 0.22, 1.00 } or { 0.55, 0.64, 0.72, 1.00 })
    local badgeBgColor = participating
      and { 0.06, 0.24, 0.12, 0.96 }
      or (preparing and { 0.28, 0.18, 0.04, 0.96 } or { 0.12, 0.15, 0.18, 0.96 })

    self.__lidSessionParticipating = participating
    self.__lidSessionPresentationState = state
    local localAutoPassEnabled = ResolveBannerLocalAutoPassState(self)
    local displayedAutoPassEnabled = state ~= "inactive" and localAutoPassEnabled
    if state == "inactive" then
      self.__lidHeaderAutoPassDetail = "Personal Auto Pass: OFF (no active session)."
    end
    if type(self.SetHeaderAutoPassIcon) == "function" then
      -- Always expose the local roll preference. With no session the icon is
      -- intentionally the OFF dice, rather than disappearing altogether.
      self:SetHeaderAutoPassIcon(displayedAutoPassEnabled, true)
    end
    local pendingRollCount = 0
    local getPendingRollChoiceStatus = INTERNAL_API.LIDCOUNCIL_GetPendingRollChoiceStatus_Impl
    if type(getPendingRollChoiceStatus) == "function" then
      local ok, result = pcall(getPendingRollChoiceStatus)
      if ok and type(result) == "table" then
        pendingRollCount = math.max(0, math.floor(tonumber(result.pendingCount) or 0))
      end
    end
    if type(self.SetHeaderRollPendingIndicator) == "function" then
      self:SetHeaderRollPendingIndicator(pendingRollCount)
    end
    if self.sessionStatusText then
      SetBannerDiagnostic(self.sessionStatusText, displayLabel, accentColor)
    end
    if self.sessionStatusBadge then
      local textWidth = self.sessionStatusText
        and self.sessionStatusText.GetStringWidth
        and self.sessionStatusText:GetStringWidth()
        or 90
      self.sessionStatusBadge:SetWidth(math.max(92, math.min(178, math.ceil(textWidth + 18))))
      if self.sessionStatusBadge.SetBackdropColor then
        self.sessionStatusBadge:SetBackdropColor(
          badgeBgColor[1],
          badgeBgColor[2],
          badgeBgColor[3],
          badgeBgColor[4]
        )
      end
      if self.sessionStatusBadge.SetBackdropBorderColor then
        self.sessionStatusBadge:SetBackdropBorderColor(
          accentColor[1],
          accentColor[2],
          accentColor[3],
          0.90
        )
      end
    end
    if type(self.ApplySessionInfoCardBackground) == "function" then
      self:ApplySessionInfoCardBackground(collapsed and 0.68 or 0.70)
    end

    local metaParts = {}
    local host = tostring(status.host or "")
    local memberCount = math.max(0, tonumber(status.memberCount) or 0)
    local profileName = tostring(status.profileName or "")
    if profileName == "" and type(GetEnabledLootProfileName) == "function" then
      local profileOk, enabledProfileName = pcall(GetEnabledLootProfileName)
      if profileOk then
        profileName = tostring(enabledProfileName or "")
      end
    end
    metaParts[#metaParts + 1] = profileName ~= "" and ("Profile: " .. profileName) or "Profile: none"
    if state ~= "inactive" then
      if not collapsed and host ~= "" then
        metaParts[#metaParts + 1] = "Host: " .. host
      end
      if not collapsed then
        metaParts[#metaParts + 1] = tostring(memberCount) .. (memberCount == 1 and " member" or " members")
      end
      if math.max(0, tonumber(status.startedAt) or 0) > 0 then
        metaParts[#metaParts + 1] = FormatBannerElapsed(status.elapsedSeconds)
      end
    end
    local metaText = table.concat(metaParts, " | ")
    if self.sessionStatusMeta then
      SetBannerDiagnostic(self.sessionStatusMeta, metaText, { 0.78, 0.84, 0.90, 1.00 })
    end
    if self.sessionInfoCard and self.sessionInfoCard.GetWidth then
      local contentWidth = math.max(120, self.sessionInfoCard:GetWidth() - 28)
      if self.localDataText then
        self.localDataText:SetWidth(contentWidth)
      end
      if self.syncActivityText then
        self.syncActivityText:SetWidth(math.max(96, contentWidth - 24))
      end
      if self.syncActivityBar then
        self.syncActivityBar:SetWidth(math.max(96, contentWidth - 24))
      end
    end

    self.__lidSessionTooltipDetail = label .. (metaText ~= "" and ": " .. metaText or "")

    if self.activeSessionLogo then
      if participating
        and self.__collapsed ~= true
        and self.logo
        and self.logo:IsShown()
      then
        self.activeSessionLogo:Show()
      else
        self.activeSessionLogo:Hide()
      end
    end
    if self.sessionFrameAccent then
      if (participating or preparing) and self.__collapsed ~= true then
        self.sessionFrameAccent:SetBackdropBorderColor(
          accentColor[1],
          accentColor[2],
          accentColor[3],
          0.62
        )
        self.sessionFrameAccent:Show()
      else
        self.sessionFrameAccent:Hide()
      end
    end
  end

  local function SetLocalDataBanner(self, visible, text, color, detail)
    local localDataText = self and self.localDataText
    local syncActivityText = self and self.syncActivityText
    if not localDataText or not syncActivityText then
      return
    end
    self.__lidLocalDataDetail = tostring(detail or "No data to push.")
    local collapsed = self.__collapsed == true
    local monitoringEnabled = type(self.IsHeaderMonitoringEnabled) ~= "function"
      or self:IsHeaderMonitoringEnabled()
    self.__lidLocalDataVisible = visible == true
    localDataText:SetShown(visible == true and not collapsed and monitoringEnabled)
    if visible and not collapsed and monitoringEnabled then
      SetBannerDiagnostic(localDataText, text, color)
    end
    syncActivityText:ClearAllPoints()
    if visible and not collapsed then
      syncActivityText:SetPoint("TOPLEFT", localDataText, "BOTTOMLEFT", 24, -1)
    elseif self.sessionStatusBadge then
      syncActivityText:SetPoint("TOPLEFT", self.sessionStatusBadge, "BOTTOMLEFT", 24, -3)
    end
    if self.sessionInfoButton then
      self.sessionInfoButton:ClearAllPoints()
      if visible and not collapsed then
        self.sessionInfoButton:SetPoint("TOPLEFT", localDataText, "BOTTOMLEFT", 0, -3)
      elseif self.sessionStatusBadge then
        self.sessionInfoButton:SetPoint("TOPLEFT", self.sessionStatusBadge, "BOTTOMLEFT", 0, -3)
      end
    end
  end

  local function SameBannerCharacterKey(left, right)
    return string.lower(tostring(left or "")) == string.lower(tostring(right or ""))
  end

  local function GetBannerSyncDestination(queueStatus, activeSession, selfKey)
    local targets = {}
    local seenTargets = {}
    for _, targetValue in ipairs(type(queueStatus) == "table" and queueStatus.targets or {}) do
      local target = tostring(targetValue or "")
      local targetKey = string.lower(target)
      if target ~= "" and not SameBannerCharacterKey(target, selfKey) and not seenTargets[targetKey] then
        seenTargets[targetKey] = true
        targets[#targets + 1] = target
      end
    end
    for _, item in ipairs(type(queueStatus) == "table" and queueStatus.queuedItems or {}) do
      local target = tostring(type(item) == "table" and (item.target or item.memberKey) or "")
      local targetKey = string.lower(target)
      if target ~= "" and not SameBannerCharacterKey(target, selfKey) and not seenTargets[targetKey] then
        seenTargets[targetKey] = true
        targets[#targets + 1] = target
      end
    end
    if #targets > 0 then
      -- Queue diagnostics can be refreshed while their target snapshot is being
      -- rebuilt. Filter it before rendering so a temporary hole cannot crash
      -- the collapsed banner.
      local previewParts = {}
      local targetCount = 0
      for _, target in pairs(targets) do
        if type(target) == "string" and target ~= "" then
          targetCount = targetCount + 1
          if #previewParts < 2 then
            previewParts[#previewParts + 1] = target
          end
        end
      end
      local preview = previewParts[1] or ""
      if previewParts[2] then
        preview = preview .. ", " .. previewParts[2]
      end
      if preview == "" then
        return ""
      end
      if type(queueStatus) == "table" and queueStatus.targetsTruncated == true then
        preview = preview .. " +more"
      elseif targetCount > 2 then
        preview = preview .. " +" .. tostring(targetCount - 2)
      end
      return preview
    end
    if type(activeSession) == "table" then
      if selfKey ~= "" and SameBannerCharacterKey(activeSession.host, selfKey) then
        return "joined members"
      end
      if tostring(activeSession.host or "") ~= "" then
        return "host"
      end
    end
    return ""
  end

  local function GetBannerOutboundPriorityLabel(outboundStatus)
    outboundStatus = type(outboundStatus) == "table" and outboundStatus or {}
    local nextItem = type(outboundStatus.nextItem) == "table" and outboundStatus.nextItem or {}
    local activeTransfer = type(outboundStatus.activeTransfer) == "table" and outboundStatus.activeTransfer or {}
    local priority = math.max(0, tonumber(nextItem.priority or outboundStatus.nextPriority) or 0)
    local kind = tostring(nextItem.kind or activeTransfer.kind or "")
    local reason = string.lower(tostring(nextItem.reason or activeTransfer.reason or ""))
    local lane = string.lower(tostring(nextItem.lane or activeTransfer.lane or ""))

    if kind == "" and reason ~= "" then
      kind = "PSYNC"
    end

    -- Lower native queue numbers are more urgent. Present the technical queue as
    -- the three operational bands described in the Sync Priority tutorial.
    if priority <= 5 then
      return "P1 Live"
    end
    if kind:find("PSYNC", 1, true) and reason:find("bootstrap", 1, true) and lane ~= "archive" and lane ~= "stats" then
      return "P1 Roll-ready"
    end
    if lane == "archive" then
      return "P3 History"
    end
    if lane == "stats" then
      return "P3 Statistics"
    end
    if priority > 0 then
      return "P2 Session/Profile"
    end
    return ""
  end

  local function GetBannerInboundPriorityLabel(inboundStatus)
    local transfer = type(inboundStatus) == "table" and type(inboundStatus.transfer) == "table"
      and inboundStatus.transfer or {}
    local mode = string.upper(tostring(transfer.mode or ""))
    local lane = string.lower(tostring(transfer.lane or ""))
    if lane == "archive" then
      return "P3 History"
    elseif lane == "stats" then
      return "P3 Statistics"
    elseif mode == "BACKGROUND" then
      return "P2 Session/Profile"
    elseif lane == "live" or transfer.liveSync == true then
      return "P1 Roll-ready"
    elseif lane ~= "" then
      return "P2 Session/Profile"
    end
    return ""
  end

  local function GetBannerSyncSource(inboundStatus, activeSession, selfKey)
    local sources = {}
    local seenSources = {}
    for _, sourceValue in ipairs(type(inboundStatus) == "table" and inboundStatus.sources or {}) do
      local source = tostring(sourceValue or "")
      local sourceKey = string.lower(source)
      if source ~= "" and not SameBannerCharacterKey(source, selfKey) and not seenSources[sourceKey] then
        seenSources[sourceKey] = true
        sources[#sources + 1] = source
      end
    end
    local lastSource = tostring(type(inboundStatus) == "table" and inboundStatus.lastDispatchedSender or "")
    local lastSourceKey = string.lower(lastSource)
    if lastSource ~= "" and not SameBannerCharacterKey(lastSource, selfKey) and not seenSources[lastSourceKey] then
      seenSources[lastSourceKey] = true
      sources[#sources + 1] = lastSource
    end
    local transferSource = tostring(type(inboundStatus) == "table" and type(inboundStatus.transfer) == "table"
      and inboundStatus.transfer.sender or "")
    local transferSourceKey = string.lower(transferSource)
    if transferSource ~= "" and not SameBannerCharacterKey(transferSource, selfKey) and not seenSources[transferSourceKey] then
      seenSources[transferSourceKey] = true
      sources[#sources + 1] = transferSource
    end
    if #sources > 0 then
      -- Inbound status may be assembled from partial runtime data. Avoid
      -- table.concat on a sparse source list while the editor is collapsing.
      local previewParts = {}
      local sourceCount = 0
      for _, source in pairs(sources) do
        if type(source) == "string" and source ~= "" then
          sourceCount = sourceCount + 1
          if #previewParts < 2 then
            previewParts[#previewParts + 1] = source
          end
        end
      end
      local preview = previewParts[1] or ""
      if previewParts[2] then
        preview = preview .. ", " .. previewParts[2]
      end
      if preview == "" then
        return ""
      end
      if type(inboundStatus) == "table" and inboundStatus.sourcesTruncated == true then
        preview = preview .. " +more"
      elseif sourceCount > 2 then
        preview = preview .. " +" .. tostring(sourceCount - 2)
      end
      return preview
    end
    if type(activeSession) == "table" then
      if selfKey ~= "" and SameBannerCharacterKey(activeSession.host, selfKey) then
        return "joined members"
      end
      if tostring(activeSession.host or "") ~= "" then
        return "host"
      end
    end
    return ""
  end

  local function RefreshSyncActivityBanner(self, enabledProfileName)
    if type(InCombatLockdown) == "function" and InCombatLockdown() then
      self.__lidBannerDiagnosticsDeferred = true
      return
    end
    RefreshSessionPresentation(self)
    local syncActivityText = self and self.syncActivityText
    if not syncActivityText then
      return
    end
    local getActiveLootSession = ResolveBannerApiFunction("LIDCOUNCIL_GetActiveLootSession")
    local activeSession
    if type(getActiveLootSession) == "function" then
      local ok, session = pcall(getActiveLootSession)
      if ok then
        activeSession = session
      end
    end
    local getCharacterKey = ResolveBannerApiFunction("LIDCOUNCIL_GetCharacterKey")
    local selfKey = ""
    if type(getCharacterKey) == "function" then
      local ok, characterKey = pcall(getCharacterKey)
      if ok then
        selfKey = tostring(characterKey or "")
      end
    end
    local getActivityStatus = ResolveBannerApiFunction("LIDCOUNCIL_GetCommHeaderActivityStatus")
    local activityStatus = {}
    if type(getActivityStatus) == "function" then
      local ok, result = pcall(getActivityStatus)
      if ok and type(result) == "table" then
        activityStatus = result
      end
    end

    local outboundStatus = type(activityStatus.outbound) == "table" and activityStatus.outbound or {}
    local inboundStatus = type(activityStatus.inbound) == "table" and activityStatus.inbound or {}
    local snapshotBatch = type(activityStatus.profileSnapshotBatch) == "table" and activityStatus.profileSnapshotBatch or {}
    local activeProfileTransfers = math.max(0, tonumber(outboundStatus.activeProfileTransfers) or 0)
    local awaitingProfileAck = math.max(0, tonumber(outboundStatus.awaitingProfileAck) or 0)
    local awaitingProfileApply = math.max(0, tonumber(outboundStatus.awaitingProfileApply) or 0)
    local uploadActive = outboundStatus.active == true or activeProfileTransfers > 0
    local downloadActive = inboundStatus.active == true
    if not uploadActive and not downloadActive then
      local activeSessionId = tostring(type(activeSession) == "table" and activeSession.id or "")
      local heldSessionId = tostring(snapshotBatch.heldSessionId or "")
      local pendingBootstrapTargets = math.max(0, tonumber(snapshotBatch.pendingTargets) or 0)
      local holdRemaining = math.max(0, tonumber(snapshotBatch.holdRemaining) or 0)
      local batchMatchesActiveSession = activeSessionId ~= "" and heldSessionId == activeSessionId
      if pendingBootstrapTargets > 0 and batchMatchesActiveSession then
        local packagePriority = math.max(0, tonumber(snapshotBatch.nextPriority) or 0)
        local packageTransferKind = string.upper(tostring(snapshotBatch.nextTransferKind or ""))
        local target = tostring(snapshotBatch.nextTarget or "")
        if holdRemaining > 0 then
          packagePriority = math.max(0, tonumber(snapshotBatch.firstQueuedPriority) or packagePriority)
          packageTransferKind = string.upper(tostring(snapshotBatch.firstQueuedTransferKind or packageTransferKind))
          target = tostring(snapshotBatch.firstQueuedTarget or target)
        end
        local priorityLabel = packagePriority <= 15 and "P1" or (packagePriority <= 30 and "P2" or "P3")
        local packageLabel = "profile"
        local packageDetail = "profile update"
        if packageTransferKind == "FULL" then
          packageLabel = "full"
          packageDetail = "full profile"
        elseif packageTransferKind == "DOMAINS" then
          packageLabel = "domains"
          packageDetail = "profile domains"
        elseif packageTransferKind == "SUBDOMAINS" then
          packageLabel = "targeted"
          packageDetail = "targeted profile data"
        elseif packageTransferKind == "PATCHES" or packageTransferKind == "ROWPATCHES" then
          packageLabel = "patch"
          packageDetail = "profile patch"
        end
        local queuedLabel = priorityLabel .. " " .. packageLabel
        if holdRemaining > 0 then
          local waitSeconds = math.max(1, math.ceil(holdRemaining))
          local syncText = "Sync: Roster settling | " .. queuedLabel .. " in " .. tostring(waitSeconds) .. "s"
          local holdReason = tostring(snapshotBatch.holdReason or "")
          local holdReasonLabel = "roster update"
          if holdReason == "prepare_session" then
            holdReasonLabel = "session preparation"
          elseif holdReason == "manual_invite" then
            holdReasonLabel = "session invite"
          elseif string.sub(holdReason, 1, 5) == "join:" then
            holdReasonLabel = "member joined"
          end
          SetBannerDiagnostic(syncActivityText, syncText, { 1.00, 0.76, 0.28, 1.00 })
          self.__lidSyncDetail = syncText
            .. " | Reason: " .. holdReasonLabel
            .. " | First package: " .. priorityLabel .. " " .. packageDetail
            .. (target ~= "" and " -> " .. target or "")
          SetBannerSyncActivity(self, false, 0.22, { 1.00, 0.76, 0.28, 1.00 }, true, true)
          return
        end
        local syncText = "Sync: " .. queuedLabel .. " queued"
          .. (target ~= "" and " -> " .. target or "")
        SetBannerDiagnostic(syncActivityText, syncText, { 0.38, 0.72, 1.00, 1.00 })
        self.__lidSyncDetail = syncText
        SetBannerSyncActivity(self, false, 0.28, { 0.38, 0.72, 1.00, 1.00 }, true, true)
        return
      end
      SetBannerDiagnostic(syncActivityText, "Sync: Idle", { 0.72, 0.78, 0.84, 1.00 })
      self.__lidSyncDetail = "Sync is idle. No upload or download is active."
      self.__lidSyncTransferProgress = nil
      self.__lidSyncProgressReset = nil
      SetBannerSyncActivity(self, false)
      return
    end

    local parts = {}
    local pendingMessages = math.max(0, tonumber(outboundStatus.pending) or 0)
    local uploadRate = math.max(0, tonumber(outboundStatus.messageRatePerSecond) or 0)
    local downloadRate = math.max(0, tonumber(inboundStatus.messageRatePerSecond) or 0)
    local uploadProgress
    local downloadProgress
    if uploadActive then
      local destination = GetBannerSyncDestination(outboundStatus, activeSession, selfKey)
      local uploadLabel
      if pendingMessages <= 0 and awaitingProfileApply > 0 then
        uploadLabel = "Apply pending"
      elseif pendingMessages <= 0 and awaitingProfileAck > 0 then
        uploadLabel = "Awaiting receipt"
      elseif pendingMessages <= 0 and activeProfileTransfers > 0 then
        uploadLabel = "Transfer pending"
      else
        uploadLabel = (outboundStatus.running == true or uploadRate > 0) and "Upload" or "Upload queued"
      end
      local priorityLabel = GetBannerOutboundPriorityLabel(outboundStatus)
      local uploadProgressText = ""
      local uploadEtaText = ""
      local outboundTransfer = type(outboundStatus.activeTransfer) == "table" and outboundStatus.activeTransfer or {}
      local sentChunks = math.max(0, tonumber(outboundTransfer.sentChunks) or 0)
      local totalChunks = math.max(0, tonumber(outboundTransfer.totalChunks) or 0)
      if totalChunks > 0 then
        local uploadEta
        uploadProgress, uploadEta = GetBannerTransferProgress(self, "upload", destination, sentChunks, totalChunks)
        uploadProgressText = " " .. tostring(math.floor(uploadProgress * 100 + 0.5)) .. "%"
        if uploadEta then
          uploadEtaText = " ~" .. tostring(uploadEta) .. "s"
        end
      end
      parts[#parts + 1] = uploadLabel .. uploadProgressText
        .. (priorityLabel ~= "" and " " .. priorityLabel or "")
        .. (destination ~= "" and " -> " .. destination or "")
        .. uploadEtaText
      if activeProfileTransfers > 1 then
        parts[#parts + 1] = tostring(activeProfileTransfers) .. " transfers"
      end
    end
    if downloadActive then
      local source = GetBannerSyncSource(inboundStatus, activeSession, selfKey)
      local priorityLabel = GetBannerInboundPriorityLabel(inboundStatus)
      local downloadProgressText = ""
      local downloadEtaText = ""
      local inboundTransfer = type(inboundStatus.transfer) == "table" and inboundStatus.transfer or {}
      local receivedChunks = math.max(0, tonumber(inboundTransfer.receivedChunkCount) or 0)
      local totalChunks = math.max(0, tonumber(inboundTransfer.chunkCount) or 0)
      if totalChunks > 0 then
        local downloadEta
        downloadProgress, downloadEta = GetBannerTransferProgress(self, "download", source, receivedChunks, totalChunks)
        downloadProgressText = " " .. tostring(math.floor(downloadProgress * 100 + 0.5)) .. "%"
        if downloadEta then
          downloadEtaText = " ~" .. tostring(downloadEta) .. "s"
        end
      end
      parts[#parts + 1] = "Download" .. downloadProgressText
        .. (priorityLabel ~= "" and " " .. priorityLabel or "")
        .. (source ~= "" and " <- " .. source or "")
        .. downloadEtaText
    end
    if pendingMessages > 0 then
      parts[#parts + 1] = tostring(pendingMessages) .. " queued"
    end
    if uploadRate > 0 and downloadRate > 0 then
      parts[#parts + 1] = string.format("U %.1f / D %.1f msg/s", uploadRate, downloadRate)
    elseif uploadRate > 0 then
      parts[#parts + 1] = string.format("%.1f msg/s", uploadRate)
    elseif downloadRate > 0 then
      parts[#parts + 1] = string.format("%.1f msg/s", downloadRate)
    end

    local waitingForReceiptOnly = uploadActive
      and not downloadActive
      and pendingMessages <= 0
      and uploadRate <= 0
      and awaitingProfileAck > 0
      and awaitingProfileApply <= 0
    local syncColor = waitingForReceiptOnly and { 1.00, 0.76, 0.28, 1.00 }
      or (downloadActive and uploadActive
      and { 0.34, 0.82, 0.92, 1.00 }
      or (downloadActive and { 0.30, 0.88, 0.72, 1.00 } or { 0.38, 0.72, 1.00, 1.00 }))
    local activityRate = math.max(uploadRate, downloadRate)
    local activityValue = 0.30 + (math.max(pendingMessages, activityRate) / (math.max(pendingMessages, activityRate) + 12)) * 0.45
    local determinateProgress = downloadProgress or uploadProgress
    if determinateProgress ~= nil then
      activityValue = determinateProgress
    end
    local syncText = "Sync:"
    for _, part in pairs(parts) do
      if type(part) == "string" and part ~= "" then
        syncText = syncText .. (syncText == "Sync:" and " " or " | ") .. part
      end
    end
    if syncText == "Sync:" then
      syncText = "Sync: Active"
    end
    SetBannerDiagnostic(syncActivityText, syncText, syncColor)
    self.__lidSyncDetail = syncText
    if downloadActive then
      local inboundTransfer = type(inboundStatus.transfer) == "table" and inboundStatus.transfer or {}
      local receivedChunks = math.max(0, tonumber(inboundTransfer.receivedChunkCount) or 0)
      local totalChunks = math.max(0, tonumber(inboundTransfer.chunkCount) or 0)
      if totalChunks > 0 then
        self.__lidSyncDetail = self.__lidSyncDetail
          .. " | Incoming profile snapshot: " .. tostring(math.min(receivedChunks, totalChunks))
          .. " of " .. tostring(totalChunks) .. " chunks received."
      end
    end
    SetBannerSyncActivity(
      self,
      not waitingForReceiptOnly and determinateProgress == nil,
      activityValue,
      syncColor,
      waitingForReceiptOnly or determinateProgress ~= nil,
      determinateProgress ~= nil
    )
  end

  local function RefreshLocalSyncBannerDiagnostics(self, enabledProfileName)
    if type(InCombatLockdown) == "function" and InCombatLockdown() then
      self.__lidBannerDiagnosticsDeferred = true
      return
    end
    self.__lidBannerDiagnosticsDeferred = nil

    local localDataText = self and self.localDataText
    local syncActivityText = self and self.syncActivityText
    if not localDataText or not syncActivityText then
      return
    end

    if enabledProfileName == "" then
      SetLocalDataBanner(
        self,
        false,
        "",
        nil,
        "Local data cannot be queued because no profile is active."
      )
      RefreshSyncActivityBanner(self, enabledProfileName)
      return
    end

    local getSyncStatus = ResolveBannerApiFunction("LIDCOUNCIL_GetActiveProfileSyncStatus")
    local syncStatus = {}
    if type(getSyncStatus) == "function" then
      local ok, result = pcall(getSyncStatus)
      if ok and type(result) == "table" then
        syncStatus = result
      end
    end
    local localDomains = type(syncStatus.localDomains) == "table" and syncStatus.localDomains or {}
    local localDomainNames = {}
    for index, domain in ipairs(localDomains) do
      if index > 3 then
        break
      end
      local text = tostring(domain or "")
      if text ~= "" then
        localDomainNames[#localDomainNames + 1] = text
      end
    end
    if #localDomainNames > 0 then
      local pendingText = table.concat(localDomainNames, ", ")
      if #localDomains > #localDomainNames then
        pendingText = pendingText .. " +" .. tostring(#localDomains - #localDomainNames)
      end
      SetLocalDataBanner(
        self,
        true,
        "To push: " .. pendingText,
        { 1.00, 0.82, 0.28, 1.00 },
        "Local data queued to push: " .. pendingText .. "."
      )
    else
      SetLocalDataBanner(self, false, "", nil, "Local data: No data to push.")
    end

    RefreshSyncActivityBanner(self, enabledProfileName)
  end

  editor.RefreshSyncActivityBanner = function(targetEditor)
    local enabledProfileName = type(GetEnabledLootProfileName) == "function"
      and tostring(GetEnabledLootProfileName() or "")
      or ""
    RefreshSyncActivityBanner(targetEditor, enabledProfileName)
  end

  local function RefreshProfileLabelsState(self, root)
    local enabledProfileName = type(GetEnabledLootProfileName) == "function" and tostring(GetEnabledLootProfileName() or "") or ""
    -- The profile header represents the enabled profile, never the last profile selected for editing.
    local currentProfileName = enabledProfileName
    local hasActiveProfile = currentProfileName ~= ""
    local curProfLabel = hasActiveProfile and DisplayProfileName(currentProfileName) or "No profile activated :("
    local profileText = "Current Profile: " .. curProfLabel
    local inactiveColor = { 1.00, 0.35, 0.25, 1.00 }
    local function SetCurrentLabel(fontString, useInactiveState)
      if not fontString then
        return
      end
      fontString:SetText(profileText)
      fontString.__lid_customTextColor = useInactiveState and inactiveColor or nil
      if fontString.SetTextColor then
        if useInactiveState then
          fontString:SetTextColor(inactiveColor[1], inactiveColor[2], inactiveColor[3], inactiveColor[4])
        else
          fontString:SetTextColor(0.92, 0.96, 1.00, 1.00)
        end
      end
    end

    if self.profileBarText then
      SetCurrentLabel(self.profileBarText, not hasActiveProfile)
    end
    if self.profileLabel then
      SetCurrentLabel(self.profileLabel, not hasActiveProfile)
    end
    if self.panelsCurrentLabel then
      SetCurrentLabel(self.panelsCurrentLabel, not hasActiveProfile)
    end
    if self.uiCurrentLabel then
      SetCurrentLabel(self.uiCurrentLabel, false)
    end
    if self.liveBoardCurrentLabel then
      SetCurrentLabel(self.liveBoardCurrentLabel, not hasActiveProfile)
    end
    if self.lootPresetsCurrentLabel then
      SetCurrentLabel(self.lootPresetsCurrentLabel, not hasActiveProfile)
    end
    if self.tutorialCurrentLabel then
      SetCurrentLabel(self.tutorialCurrentLabel, false)
    end
    if self.aboutCurrentLabel then
      SetCurrentLabel(self.aboutCurrentLabel, false)
    end
    RefreshLocalSyncBannerDiagnostics(self, enabledProfileName)
  end

  local function RefreshProfilesSectionState(self, root)
    if self.selectedProfileName == "Default" then
      self.selectedProfileName = nil
    end
    if not self.selectedProfileName and not self.selectedProfileGroupName and root.currentProfile and root.currentProfile ~= "Default" then
      self.selectedProfileName = root.currentProfile
    end
    if self.selectedProfileGroupName and not FindProfileGroupIndex(EnsureProfileGroups(), self.selectedProfileGroupName) then
      self.selectedProfileGroupName = nil
    end

    if not self.profContent or not self.profScroll then
      if type(self.profGroupButtonPool) == "table" then
        for _, button in ipairs(self.profGroupButtonPool) do
          if button and button.Hide then button:Hide() end
        end
      end
      if type(self.profItemButtonPool) == "table" then
        for _, button in ipairs(self.profItemButtonPool) do
          if button and button.Hide then button:Hide() end
        end
      end
      if type(self.profButtons) == "table" then wipe(self.profButtons) end
      if type(self.profGroupButtons) == "table" then wipe(self.profGroupButtons) end
      return
    end

    if not self.__skipProfileListRebuild then
      self.profGroupButtonPool = self.profGroupButtonPool or {}
      self.profItemButtonPool = self.profItemButtonPool or {}
      self.profButtons = self.profButtons or {}
      self.profGroupButtons = self.profGroupButtons or {}
      for _, b in ipairs(self.profGroupButtonPool) do b:Hide() end
      for _, b in ipairs(self.profItemButtonPool) do b:Hide() end
      wipe(self.profButtons)
      wipe(self.profGroupButtons)

      local profilePageFocus = GetProfileFocusKey(self.selectedProfileName, self.selectedProfileGroupName)
      if not profilePageFocus and root.currentProfile and root.currentProfile ~= "Default" then
        profilePageFocus = GetProfileFocusKey(root.currentProfile)
      end
      local profileRows = BuildVisibleProfileRows(root, self.selectedProfileName)
      local selectedProfilePage = GetListPageForValue(profileRows, profilePageFocus, LISTS_PER_PAGE, function(row)
        return row and row.key
      end)
      local profTotalPages = math.max(1, math.ceil(#profileRows / LISTS_PER_PAGE))
      if profilePageFocus ~= self.__lastPagedSelectedProfileKey and selectedProfilePage then
        self.profListPage = selectedProfilePage
      end
      self.profListPage = ClampListPage(self.profListPage or 1, profTotalPages)
      self.__lastPagedSelectedProfileKey = profilePageFocus
      local profStartIndex = ((self.profListPage - 1) * LISTS_PER_PAGE) + 1
      local profEndIndex = math.min(#profileRows, profStartIndex + LISTS_PER_PAGE - 1)
      local py = 0
      local usedProfileGroupButtons = 0
      local usedProfileButtons = 0
      for i = profStartIndex, profEndIndex do
        local row = profileRows[i]
        if row and row.type == "group" then
          local label = string.format("%s (%d)", row.name or "Group", row.childCount or 0)
          usedProfileGroupButtons = usedProfileGroupButtons + 1
          local b = self:AcquirePooledButton(self.profGroupButtonPool, usedProfileGroupButtons, function()
            return MakeSimpleListButton(self.profContent, label, py, function()
              self.selectedProfileGroupName = row.name
              self.selectedProfileName = nil
              RefreshProfilesSection(self)
            end)
          end)
          self:PreparePooledListButton(b, label, py, function()
            self.selectedProfileGroupName = row.name
            self.selectedProfileName = nil
            RefreshProfilesSection(self)
          end)
          b.__profileGroupName = row.name
          b.__profileDragDropBorder = b.__profileDragDropBorder or CreatePurpleOutline(b)
          b.__profileDragDropBorder:Hide()
          b.__lid_customBg = {0.1804, 0.1216, 0.2863, 0.98}
          b.__lid_customHover = {0.72, 0.86, 1.00, 0.24}
          local isSelectedGroup = (self.selectedProfileGroupName == row.name)
          local groupTextColor
          if isSelectedGroup then
            groupTextColor = {0.6, 0.9, 1.0, 1.0}
          elseif row.containsCurrent then
            groupTextColor = {0.7, 1.0, 0.7, 1.0}
          elseif row.containsSelected then
            groupTextColor = {0.6, 0.9, 1.0, 1.0}
          else
            groupTextColor = {0.92, 0.82, 1.0, 1.0}
          end
          b.__lid_customText = groupTextColor
          b.__onToggleProfileGroup = function()
            ToggleProfileGroupCollapsed(row.name)
            self.selectedProfileGroupName = row.name
            self.selectedProfileName = nil
            RefreshProfilesSection(self)
          end
          ConfigureProfileGroupButton(b, label, row.collapsed)
          if b.__groupArrow then
            b.__groupArrow:SetTextColor(groupTextColor[1], groupTextColor[2], groupTextColor[3], groupTextColor[4] or 1)
          end
          if b.__glow then b.__glow:SetShown(isSelectedGroup or (row.containsSelected and row.collapsed)) end
          self.profButtons[#self.profButtons + 1] = b
          self.profGroupButtons[#self.profGroupButtons + 1] = b
        elseif row and row.type == "profile" then
          local pname = row.name
          local label = row.groupName and ("  " .. pname) or pname
          usedProfileButtons = usedProfileButtons + 1
          local b = self:AcquirePooledButton(self.profItemButtonPool, usedProfileButtons, function()
            return MakeProfileButton(self.profContent, label, py, function()
              self.selectedProfileName = pname
              self.selectedProfileGroupName = nil
              RefreshProfilesSection(self)
            end)
          end)
          self:PreparePooledListButton(b, label, py, function()
            self.selectedProfileName = pname
            self.selectedProfileGroupName = nil
            RefreshProfilesSection(self)
          end, {
            x = row.groupName and 16 or 0,
            width = row.groupName and 184 or 200,
          })
          b.__profileDragSourceBorder = b.__profileDragSourceBorder or CreatePurpleOutline(b)
          b.__profileDragSourceBorder:Hide()
          b:RegisterForDrag("LeftButton")
          b:SetScript("OnDragStart", function(button)
            BeginProfileGroupDrag(pname, row.groupName, button)
          end)

          if pname == root.currentProfile then
            b:GetFontString():SetTextColor(0.7, 1.0, 0.7)
          elseif pname == self.selectedProfileName then
            b:GetFontString():SetTextColor(0.6, 0.9, 1.0)
          end
          if b.__glow then b.__glow:SetShown(pname == self.selectedProfileName) end
          if b.__activeIcon then b.__activeIcon:SetShown(pname == root.currentProfile) end
          if b.__activeBorder then b.__activeBorder:SetShown(pname == root.currentProfile) end
          self.profButtons[#self.profButtons + 1] = b
        end
        py = py - 22
      end
      for i = usedProfileGroupButtons + 1, #self.profGroupButtonPool do
        self.profGroupButtonPool[i]:Hide()
      end
      for i = usedProfileButtons + 1, #self.profItemButtonPool do
        self.profItemButtonPool[i]:Hide()
      end
      self.profContent:SetHeight(math.max(1, -py))
      if self.profScroll and self.profScroll.UpdateScrollChildRect then
        self.profScroll:UpdateScrollChildRect()
      end
      if self.profScroll then self.profScroll:SetVerticalScroll(0) end
      UpdatePagerControls(self.profPrevBtn, self.profNextBtn, self.profPageText, self.profListPage, profTotalPages)
    end

    if self.__skipProfileDetailsRebuild then
      return
    end

    self.selProfLabel:SetText("Selected: " .. (self.selectedProfileName or "(none)"))
    if self.selProfGroupLabel then
      self.selProfGroupLabel:SetText("Selected Group: " .. (self.selectedProfileGroupName or "(none)"))
    end
    if self.activateBtn then
      self.activateBtn:SetEnabled(self.selectedProfileName ~= nil and self.selectedProfileName ~= "")
    end
    if self.deactivateBtn then
      self.deactivateBtn:SetEnabled(self.selectedProfileName ~= nil and self.selectedProfileName == root.currentProfile and root.currentProfile ~= "Default")
    end
    if self.renameProfGroupBtn then
      self.renameProfGroupBtn:SetEnabled(self.selectedProfileGroupName ~= nil and self.selectedProfileGroupName ~= "")
    end
    if self.delProfGroupBtn then
      self.delProfGroupBtn:SetEnabled(self.selectedProfileGroupName ~= nil and self.selectedProfileGroupName ~= "")
    end
    if self.profGroupInfoLabel then
      local groupInfoText = "Deleting a group will only ungroup its profiles."
      if self.selectedProfileGroupName then
        local groups = EnsureProfileGroups()
        local idx = FindProfileGroupIndex(groups, self.selectedProfileGroupName)
        local childCount = (idx and groups[idx] and #(groups[idx].profiles or {})) or 0
        groupInfoText = string.format("Profiles in group: %d\nDeleting this group will keep all profiles and make them groupless.", childCount)
      end
      self.profGroupInfoLabel:SetText(groupInfoText)
    end

    if self.selectedProfileGroupName then
      if self.profRightScroll then self.profRightScroll:Hide() end
      if self.profGroupScroll then self.profGroupScroll:Show() end
      if self.profWelcomeScroll then self.profWelcomeScroll:Hide() end
      if self.profWelcomeFrame then self.profWelcomeFrame:Hide() end
    elseif self.selectedProfileName and root.profiles[self.selectedProfileName] then
      if self.profRightScroll then self.profRightScroll:Show() end
      if self.profGroupScroll then self.profGroupScroll:Hide() end
      if self.profWelcomeScroll then self.profWelcomeScroll:Hide() end
      if self.profWelcomeFrame then self.profWelcomeFrame:Hide() end
    else
      if self.profRightScroll then self.profRightScroll:Hide() end
      if self.profGroupScroll then self.profGroupScroll:Hide() end
      if self.profWelcomeScroll then self.profWelcomeScroll:Show() end
      if self.profWelcomeFrame then self.profWelcomeFrame:Show() end
      if self.LayoutProfileWelcome then self:LayoutProfileWelcome() end
    end

    if self.specAutoCheck then
      local prof = self.selectedProfileName and root.profiles[self.selectedProfileName]
      local ps = prof and prof.profileSpec
      local autoMode = (ps and ps.mode == "CHARACTER") and "CHARACTER" or "SPEC"
      local autoModeText = (autoMode == "CHARACTER") and "Change by Character" or "Change by Specialization"
      if ps and ps.enabled == nil then
        ps.enabled = false
      end
      if ps and ps.mode ~= "CHARACTER" then
        ps.mode = "SPEC"
      end
      self.specAutoCheck:SetChecked(ps and ps.enabled)
      local specAutoText = _G[self.specAutoCheck:GetName() .. "Text"]
      if specAutoText then
        specAutoText:SetText("Activate automatic profile change")
      end
      if self.profileModeDrop then
        SetDropdownSelection(self.profileModeDrop, autoMode, autoModeText)
      end
      if self.specDesc then
        if autoMode == "CHARACTER" then
          self.specDesc:SetText("If this option is activated, this profile will automatically activate when you log onto the selected character. The specialization setting is ignored in this mode.")
        else
          self.specDesc:SetText("If this option is activated, this profile will automatically activate when you change to the selected specialization. You can optionally limit this behavior to a specific character.")
        end
      end
      if self.charLabel then
        if autoMode == "CHARACTER" then
          self.charLabel:SetText("Choose Character")
        else
          self.charLabel:SetText("Optional Character Filter")
        end
      end

      local specID = ps and ps.specID
      local specText = "Unbound"
      if specID then
        if type(GetSpecializationInfoByID) == "function" then
          local _, name, _, _, role, class = GetSpecializationInfoByID(specID)
          if name then
            specText = class and (class .. "_" .. name) or name
            if role then specText = specText .. " (" .. role .. ")" end
          else
            specText = tostring(specID)
          end
        else
          specText = "SpecID: " .. tostring(specID)
        end
      end
      SetDropdownSelection(self.specDrop, specID or "NONE", specText)
      if self.specLabel then
        self.specLabel:SetShown(autoMode == "SPEC")
      end
      if self.specDrop then
        if autoMode == "SPEC" then
          self.specDrop:Show()
        else
          if self.specDrop.CloseMenu then
            self.specDrop:CloseMenu()
          end
          self.specDrop:Hide()
        end
      end

      if self.charDrop then
        local charVal = ps and ps.character
        local charText = charVal or ((autoMode == "CHARACTER") and "Choose character" or "Any character")
        local charValue = charVal
        if charValue == nil and autoMode ~= "CHARACTER" then
          charValue = "ANY"
        end
        SetDropdownSelection(self.charDrop, charValue, charText)
      end
    end
  end

  RefreshProfilesSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      local root = RootDB()
      self.__refreshing = true
      RefreshProfileLabelsState(self, root)
      RefreshProfilesSectionState(self, root)
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
    end, function(err)
      ReportEditorRuntimeError("Editor Profiles Refresh", err)
    end)
    if not ok then
      self.__refreshing = false
    end
  end

  RefreshProfileDetailsSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      local root = RootDB()
      self.__refreshing = true
      self.__skipProfileListRebuild = true
      RefreshProfileLabelsState(self, root)
      RefreshProfilesSectionState(self, root)
      self.__skipProfileListRebuild = nil
      self.__refreshing = false
    end, function(err)
      ReportEditorRuntimeError("Editor Profile Details Refresh", err)
    end)
    if not ok then
      self.__skipProfileListRebuild = nil
      self.__refreshing = false
    end
  end

  editor.RefreshProfilesListSection = function(self)
    if not self or self.__refreshing then return end
    local ok = xpcall(function()
      local root = RootDB()
      self.__refreshing = true
      self.__skipProfileDetailsRebuild = true
      RefreshProfilesSectionState(self, root)
      self.__skipProfileDetailsRebuild = nil
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
    end, function(err)
      ReportEditorRuntimeError("Editor Profile List Refresh", err)
    end)
    if not ok then
      self.__skipProfileDetailsRebuild = nil
      self.__refreshing = false
    end
  end

  editor.RefreshCurrentProfileSections = function(self)
    if not self or self.__refreshing then return end
    if RequestFullWorkflowRefresh(self, {
      refreshHiddenPages = true,
      refreshAbout = true,
    }) then
      return
    end
    local ok = xpcall(function()
      local root = RootDB()
      self.__refreshing = true
      RefreshProfileLabelsState(self, root)
      if self.RefreshLootProfilePage then self:RefreshLootProfilePage() end
      if self.RefreshLootSessionPage then self:RefreshLootSessionPage() end
      if self.RefreshLootLiveBoardPage then self:RefreshLootLiveBoardPage() end
      if self.RefreshLootListPage then self:RefreshLootListPage() end
      if self.RefreshLootPresetsPage then self:RefreshLootPresetsPage() end
      self.__refreshing = false
      if self.ApplyTheme then self:ApplyTheme() end
      if self.RefreshAbout then self:RefreshAbout() end
    end, function(err)
      ReportEditorRuntimeError("Editor Current Profile Refresh", err)
    end)
    if not ok then
      self.__refreshing = false
    end
  end

  editor.RefreshLootWorkflowSections = function(self, opts)
    if not self or self.__refreshing then
      return
    end
    opts = type(opts) == "table" and opts or {}
    local ok = xpcall(function()
      local root = RootDB()
      local currentPage = tostring(self.currentPage or "Profiles")
      local refreshAllLoot = opts.refreshAllLoot == true
      local refreshPages = type(opts.refreshPages) == "table" and opts.refreshPages or nil
      local refreshAbout = opts.refreshAbout == true or currentPage == "About" or (refreshPages and refreshPages.About == true)
      local refreshedLootPage = false
      local refreshHiddenPages = opts.refreshHiddenPages == true

      local function ShouldRefreshPage(pageName)
        if refreshAllLoot then
          return true
        end
        if refreshPages then
          return refreshPages[pageName] == true
        end
        return currentPage == pageName
      end

      local function ShouldRunPageRefresh(pageName)
        if not ShouldRefreshPage(pageName) then
          return false
        end
        if refreshAllLoot or refreshPages == nil or refreshHiddenPages then
          return true
        end
        return currentPage == pageName
      end

      self.__refreshing = true
      RefreshProfileLabelsState(self, root)

      if ShouldRunPageRefresh("Profiles") then
        if self.RefreshLootProfilePage then self:RefreshLootProfilePage() end
        refreshedLootPage = true
      end
      if ShouldRunPageRefresh("Panels") then
        if self.RefreshLootSessionPage then self:RefreshLootSessionPage(opts) end
        refreshedLootPage = true
      end
      if ShouldRunPageRefresh("LiveBoard") then
        if self.RefreshLootLiveBoardPage then self:RefreshLootLiveBoardPage() end
        refreshedLootPage = true
      end
      if ShouldRunPageRefresh("Backgrounds") then
        if self.RefreshLootListPage then self:RefreshLootListPage() end
        refreshedLootPage = true
      end
      if ShouldRunPageRefresh("LootPresets") then
        if self.RefreshLootPresetsPage then self:RefreshLootPresetsPage() end
        refreshedLootPage = true
      end

      self.__refreshing = false
      if self.ApplyTheme and refreshedLootPage then
        self:ApplyTheme()
      end
      if refreshAbout and (currentPage == "About" or refreshHiddenPages or refreshAllLoot) and self.RefreshAbout then
        self:RefreshAbout()
      end
    end, function(err)
      ReportEditorRuntimeError("Editor Loot Workflow Refresh", err)
    end)
    if not ok then
      self.__refreshing = false
    end
  end

  editor.InvalidateSortedNameCaches = function()
    InvalidateSortedProfileNames()
    InvalidateSortedPanelNames()
  end

  editor.RefreshPanelsSection = RefreshPanelsSection
  editor.RefreshPanelPropertiesSection = RefreshPanelPropertiesSection
  editor.RefreshBackgroundsSection = RefreshBackgroundsSection
  editor.RefreshBackgroundListDetailsSection = RefreshBackgroundListDetailsSection
  editor.RefreshProfilesSection = RefreshProfilesSection
  editor.RefreshProfileDetailsSection = RefreshProfileDetailsSection

  function editor:Refresh()
    local ok = xpcall(function()
      local root = RootDB()

      self.__refreshing = true
      self.title:SetText("LidCouncil Version " .. ADDON_VERSION)
      if self.versionText then
        self.versionText:SetText("v" .. WOW_CLIENT_VERSION)
      end
      RefreshProfileLabelsState(self, root)

      if self.minimapSizeSlider then
        local size = (root.ui.minimap and root.ui.minimap.size) or 28
        self.minimapSizeSlider:SetValueSilent(size)
      end
      if self.minimapAlphaSlider then
        local alpha = (root.ui.minimap and root.ui.minimap.alpha) or 1
        self.minimapAlphaSlider:SetValueSilent(alpha * 100)
      end
      if self.minimapHoverCheck then
        local hov = (root.ui.minimap and root.ui.minimap.onlyOnHover) and true or false
        self.minimapHoverCheck:SetChecked(hov)
      end

      if self.RefreshLootProfilePage then self:RefreshLootProfilePage() end
      if self.RefreshLootSessionPage then self:RefreshLootSessionPage() end
      if self.RefreshLootLiveBoardPage then self:RefreshLootLiveBoardPage() end
      if self.RefreshLootListPage then self:RefreshLootListPage() end
      if self.RefreshLootPresetsPage then self:RefreshLootPresetsPage() end
      if self.UpdateUILayout then self:UpdateUILayout() end

      self.__refreshing = false
      self:ApplyTheme()
      -- Theme and page refreshes can make header regions visible again. Apply
      -- the saved monitoring preference after all of those operations finish.
      if self.ApplyHeaderMonitoringVisibility then self:ApplyHeaderMonitoringVisibility() end
      if self.RefreshAbout then self:RefreshAbout() end
      self.__applyingTheme = false
    end, function(err)
      ReportEditorRuntimeError("Editor Refresh", err)
    end)
    if not ok then
      self.__refreshing = false
    end
  end
end
