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

local shared = INTERNAL_API.LIDCOUNCIL_LootEditorShared_Impl
if type(shared) ~= "table" then
  error("LidCouncil missing loot editor shared helpers.")
end

local RequireApi = shared.RequireApi
local BindMouseWheelScroll = shared.BindMouseWheelScroll
local RootDB = shared.RootDB
local ApplyFrameSkin = shared.ApplyFrameSkin
local MakeYellowSubHeader = shared.MakeYellowSubHeader
local GetCharacterKey = shared.GetCharacterKey
local GetEnabledLootProfileName = shared.GetEnabledLootProfileName or RequireApi("LIDCOUNCIL_GetEnabledLootProfileName", _G.LIDCOUNCIL_GetEnabledLootProfileName)
local GetLootProfile = shared.GetLootProfile
local GetPrimaryLootProfileName = shared.GetPrimaryLootProfileName
local GetLootProfileNames = shared.GetLootProfileNames
local IsLootProfileVisibleInEditor = shared.IsLootProfileVisibleInEditor or function(_, profileName)
  return profileName ~= nil and profileName ~= ""
end
local GetLootProfileAccessInfo = shared.GetLootProfileAccessInfo
local SetCurrentLootProfile = shared.SetCurrentLootProfile
local CreateLootProfile = shared.CreateLootProfile
local DeleteLootProfile = shared.DeleteLootProfile
local CopyLootProfile = shared.CopyLootProfile
local SetLootProfileEnabled = shared.SetLootProfileEnabled
local SetLootProfileRaid = shared.SetLootProfileRaid
local AddLootRollOption = shared.AddLootRollOption
local UpdateLootRollOption = shared.UpdateLootRollOption
local RemoveLootRollOption = shared.RemoveLootRollOption
local UpdateLootPointMode = shared.UpdateLootPointMode
local UpdateLootLidTableRow = shared.UpdateLootLidTableRow
local AddLootPointRule = shared.AddLootPointRule
local RemoveLootPointRule = shared.RemoveLootPointRule
local StartLootSession = shared.StartLootSession
local StopLootSession = shared.StopLootSession
local GetActiveLootSession = shared.GetActiveLootSession
local AddLootLedgerEntry = shared.AddLootLedgerEntry
local RemoveLootLedgerEntry = shared.RemoveLootLedgerEntry
local GetLootLedgerCharacters = shared.GetLootLedgerCharacters
local ResolveOptionalApiFunction = shared.ResolveOptionalApiFunction
local CreateCustomDropdownControl = shared.CreateCustomDropdownControl
local TrimText = shared.TrimText
local FormatDateTime = shared.FormatDateTime
local FormatDuration = shared.FormatDuration
local CreateSimpleEditBox = shared.CreateSimpleEditBox
local SetListButtonSelected = shared.SetListButtonSelected
local SetTextListButtonTextColor = shared.SetTextListButtonTextColor
local EnsureTextListButtons = shared.EnsureTextListButtons
local CreateSectionShell = shared.CreateSectionShell
local ClampLayoutValue = shared.ClampLayoutValue
local GetResponsiveSectionLayout = shared.GetResponsiveSectionLayout
local SetControlWidth = shared.SetControlWidth
local GetFontStringWidth = shared.GetFontStringWidth
local CreateSectionDivider = shared.CreateSectionDivider
local FlowLayoutControls = shared.FlowLayoutControls
local LayoutRollOptionRow = shared.LayoutRollOptionRow
local LayoutLidTableEditorRow = shared.LayoutLidTableEditorRow
local BuildRollOptionsText = shared.BuildRollOptionsText
local DEFAULT_ROLL_OPTION_ICON_CHOICES = shared.DEFAULT_ROLL_OPTION_ICON_CHOICES
local ResolveTextureAsset = shared.ResolveTextureAsset
local ResolveDefaultRollOptionIcon = shared.ResolveDefaultRollOptionIcon
local ResolveRollOptionIcon = shared.ResolveRollOptionIcon
local GetRollOptionIconLabel = shared.GetRollOptionIconLabel
local EnsureRollOptionRows = shared.EnsureRollOptionRows
local EnsureLidTableEditorRows = shared.EnsureLidTableEditorRows
local ExtractItemID = shared.ExtractItemID
local ExtractItemLinkName = shared.ExtractItemLinkName
local FormatPointRuleItemLabel = shared.FormatPointRuleItemLabel
local FormatRaidLootItemLabel = shared.FormatRaidLootItemLabel
local LOOT_ITEM_MENU_PAGE_SIZE = shared.LOOT_ITEM_MENU_PAGE_SIZE
local NormalizeInstanceTuple = shared.NormalizeInstanceTuple
local NormalizeEncounterTuple = shared.NormalizeEncounterTuple
local NormalizeLootInfo = shared.NormalizeLootInfo
local EnsureEncounterJournalLoaded = shared.EnsureEncounterJournalLoaded
local CollectRaidEntries = shared.CollectRaidEntries
local CollectRaidEncounterEntries = shared.CollectRaidEncounterEntries
local CollectRaidLootEntries = shared.CollectRaidLootEntries
local FindRaidEntryByProfile = shared.FindRaidEntryByProfile
local FindRaidLootEntry = shared.FindRaidLootEntry
local RAID_LOOT_DIFFICULTIES = shared.RAID_LOOT_DIFFICULTIES
local EnsureLootLiveBoardMode = shared.EnsureLootLiveBoardMode
local ClampMenuPage = shared.ClampMenuPage
local GetSelectedValuePage = shared.GetSelectedValuePage

local ACTIVATE_PROFILE_REQUIRED_TEXT = "Please go to Profile and activate one Profile first"
local NO_ACTIVE_PROFILE_CURRENT_TEXT = "Current Profile: No profile activated :("
local NO_ACTIVE_PROFILE_TEXT_COLOR = { 1.00, 0.35, 0.25, 1.00 }

local function SetCurrentProfileFont(fontString, text, color)
  if not fontString then
    return
  end
  fontString:SetText(text)
  fontString.__lid_customTextColor = color
  if color and fontString.SetTextColor then
    fontString:SetTextColor(color[1], color[2], color[3], color[4] or 1)
  elseif fontString.SetTextColor then
    fontString:SetTextColor(0.92, 0.96, 1.00, 1.00)
  end
end
local EMPTY_LOOT_LIST_ACCESS = {
  canEditLootList = false,
  canEditManualPoints = false,
  canEditMainAlt = false,
  canEditNotes = false,
}

local function GetLootListContextProfileName()
  local root = RootDB()
  local enabledProfileName = TrimText(GetEnabledLootProfileName() or "")
  if enabledProfileName ~= "" and IsLootProfileVisibleInEditor(root, enabledProfileName) then
    return enabledProfileName
  end
  return nil
end
local GetEditorThemePalette = type(INTERNAL_API.LIDCOUNCIL_GetEditorThemePalette_Impl) == "function" and INTERNAL_API.LIDCOUNCIL_GetEditorThemePalette_Impl or nil
local EnsureLootProfileGroups = shared.EnsureLootProfileGroups
local FindLootProfileGroupIndex = shared.FindLootProfileGroupIndex
local RemoveLootProfileFromGroups = shared.RemoveLootProfileFromGroups
local ToggleLootProfileGroupCollapsed = shared.ToggleLootProfileGroupCollapsed
local GetLootProfileGroupName = shared.GetLootProfileGroupName
local BuildLootProfileNavRows = shared.BuildLootProfileNavRows
local BuildPointRulesText = shared.BuildPointRulesText
local ResolvePointMode = shared.ResolvePointMode
local GetLidTableRows = shared.GetLidTableRows
local BuildLidTableSummaryText = shared.BuildLidTableSummaryText
local POINT_MODE_CHOICES = shared.POINT_MODE_CHOICES
local GetSelectedProfileName = shared.GetSelectedProfileName
local RequestEditorPageRefresh = shared.RequestEditorPageRefresh
local lootListData = INTERNAL_API.LIDCOUNCIL_LootListData_Impl
if type(lootListData) ~= "table" then
  error("LidCouncil missing loot list data helpers.")
end

local lootListRendering = INTERNAL_API.LIDCOUNCIL_LootListRendering_Impl
if type(lootListRendering) ~= "table" then
  error("LidCouncil missing loot list rendering helpers.")
end

local MAX_LOOTLIST_GROUP_NAME = tonumber(lootListData.MAX_LOOTLIST_GROUP_NAME) or 32
local EnsureLootLedgerMetaLocal = lootListData.EnsureLootLedgerMetaLocal
local NormalizeLootLedgerRoleLocal = lootListData.NormalizeLootLedgerRoleLocal
local SetLootLedgerMainAltRole = lootListData.SetLootLedgerMainAltRole
local SetLootLedgerCharacterNote = lootListData.SetLootLedgerCharacterNote
local SetLootLedgerMainCharacter = lootListData.SetLootLedgerMainCharacter
local ClearLootLedgerMainCharacter = lootListData.ClearLootLedgerMainCharacter
local DeleteLootLedgerCharacter = lootListData.DeleteLootLedgerCharacter
local SeedLootLedgerTestRoster = lootListData.SeedLootLedgerTestRoster
local CreateLootListGroup = lootListData.CreateLootListGroup
local DeleteLootListGroup = lootListData.DeleteLootListGroup
local BuildLootLedgerNavRows = lootListData.BuildLootLedgerNavRows
local RefreshLootListPageImpl = lootListRendering.RefreshLootListPage
local BuildLootLedgerHistoryEntries = lootListRendering.BuildLootLedgerHistoryEntries
local GetClassColorForRow = lootListRendering.GetClassColorForRow
local BuildSpecLabel = lootListRendering.BuildSpecLabel
local GetLootLedgerHeaderIconData = lootListRendering.GetLootLedgerHeaderIconData
local ApplyLootLedgerTextureIcon = lootListRendering.ApplyLootLedgerTextureIcon

local NUMERIC_LOOT_LEDGER_SORT_KEYS = {
  sessions = true,
  items = true,
  points = true,
  total = true,
  bis = true,
  offspec = true,
  transmog = true,
  disenchant = true,
}

local VALID_LOOT_LEDGER_SORT_KEYS = {
  spec = true,
  name = true,
  note = true,
  sessions = true,
  items = true,
  points = true,
  total = true,
  bis = true,
  offspec = true,
  transmog = true,
  disenchant = true,
}

local function IsCombatLocked()
  return type(InCombatLockdown) == "function" and InCombatLockdown() and true or false
end

local function RejectCombatLockedLootListAction(feedback, actionLabel)
  if not IsCombatLocked() then
    return false
  end
  local message = tostring(actionLabel or "This Loot List action") .. " is unavailable during combat."
  if feedback and feedback.SetText then
    feedback:SetText(message)
  end
  return true, message
end

local function GetLootListViewMode()
  local root = RootDB()
  root.ui = root.ui or {}
  local mode = TrimText(root.ui.lootLedgerViewMode)
  if mode ~= "DETAILS" then
    mode = "LIST_ONLY"
  end
  root.ui.lootLedgerViewMode = mode
  return mode
end

local function StripButtonTemplateTextures(button)
  if not button then
    return
  end
  if button.__lootListTemplateTexturesStripped then
    return
  end
  button.__lootListTemplateTexturesStripped = true

  if button.SetNormalTexture then pcall(button.SetNormalTexture, button, "") end
  if button.SetHighlightTexture then pcall(button.SetHighlightTexture, button, "") end
  if button.SetPushedTexture then pcall(button.SetPushedTexture, button, "") end
  if button.SetDisabledTexture then pcall(button.SetDisabledTexture, button, "") end

  if type(button.GetRegions) == "function" and type(button.GetNumRegions) == "function" then
    for i = 1, button:GetNumRegions() do
      local region = select(i, button:GetRegions())
      if region and region.GetObjectType and region:GetObjectType() == "Texture" then
        region:SetTexture(nil)
        region:Hide()
      end
    end
  end

  local name = button.GetName and button:GetName() or nil
  if name then
    for _, suffix in ipairs({
      "Left", "Middle", "Right",
      "LeftDisabled", "MiddleDisabled", "RightDisabled",
      "LeftHighlight", "MiddleHighlight", "RightHighlight",
    }) do
      local region = _G[name .. suffix]
      if region and region.Hide then
        region:SetTexture(nil)
        region:Hide()
      end
    end
  end
end

local function ApplyLootListToolbarButtonStyle(button)
  if not button then
    return
  end

  StripButtonTemplateTextures(button)
  if type(button.SetBackdrop) == "function" then
    ApplyFrameSkin(button)
  end

  local palette = GetEditorThemePalette and GetEditorThemePalette() or nil
  local normal = type(palette) == "table" and type(palette.normal) == "table" and palette.normal or { 0.10, 0.10, 0.13, 0.98 }
  local hover = type(palette) == "table" and type(palette.hover) == "table" and palette.hover or { 0.14, 0.14, 0.18, 0.98 }
  local active = type(palette) == "table" and type(palette.active) == "table" and palette.active or { 0.74, 0.22, 0.30, 0.92 }
  local border = type(palette) == "table" and type(palette.border) == "table" and palette.border or { 0.74, 0.22, 0.30, 0.95 }

  button.__lootListToolbarNormal = normal
  button.__lootListToolbarHover = hover
  button.__lootListToolbarActive = active
  if button.SetBackdropBorderColor then
    button:SetBackdropBorderColor(border[1] or 0.74, border[2] or 0.22, border[3] or 0.30, border[4] or 0.95)
  end

  local label = button.GetFontString and button:GetFontString() or nil
  if label and label.SetTextColor then
    label:SetTextColor(0.92, 0.92, 0.96, 1)
  end

  button:SetScript("OnEnter", function(self)
    local color = self.__lootListToolbarSelected and self.__lootListToolbarActive or self.__lootListToolbarHover
    if self.SetBackdropColor then
      self:SetBackdropColor(color[1] or 0.14, color[2] or 0.14, color[3] or 0.18, color[4] or 0.98)
    end
  end)
  button:SetScript("OnLeave", function(self)
    local color = self.__lootListToolbarSelected and self.__lootListToolbarActive or self.__lootListToolbarNormal
    if self.SetBackdropColor then
      self:SetBackdropColor(color[1] or 0.10, color[2] or 0.10, color[3] or 0.13, color[4] or 0.98)
    end
  end)
end

local function SetLootListToolbarButtonSelected(button, selected)
  if not button then
    return
  end
  button.__lootListToolbarSelected = selected and true or false
  local color = button.__lootListToolbarSelected and button.__lootListToolbarActive or button.__lootListToolbarNormal
  if button.SetBackdropColor then
    button:SetBackdropColor(
      color and color[1] or 0.10,
      color and color[2] or 0.10,
      color and color[3] or 0.13,
      color and color[4] or 0.98
    )
  end
end

local function ApplyLootListDetailActionButtonStyle(button, danger)
  if not button then
    return
  end

  StripButtonTemplateTextures(button)
  if type(button.SetBackdrop) == "function" then
    ApplyFrameSkin(button)
  end

  local normal = danger and { 0.19, 0.045, 0.04, 0.98 } or { 0.07, 0.17, 0.31, 1.00 }
  local hover = danger and { 0.32, 0.07, 0.055, 1.00 } or { 0.10, 0.26, 0.46, 1.00 }
  local border = danger and { 0.96, 0.34, 0.28, 1.00 } or { 0.48, 0.75, 1.00, 1.00 }
  button.__lootListDetailNormal = normal
  button.__lootListDetailHover = hover
  if button.SetBackdropColor then
    button:SetBackdropColor(normal[1], normal[2], normal[3], normal[4])
  end
  if button.SetBackdropBorderColor then
    button:SetBackdropBorderColor(border[1], border[2], border[3], border[4])
  end
  local label = button.GetFontString and button:GetFontString() or nil
  if label and label.SetTextColor then
    if danger then
      label:SetTextColor(1.00, 0.62, 0.56, 1)
    else
      label:SetTextColor(0.92, 0.96, 1.00, 1)
    end
  end
  if label and label.ClearAllPoints and label.SetPoint then
    label:ClearAllPoints()
    label:SetPoint("LEFT", button, "LEFT", 14, 0)
    label:SetPoint("RIGHT", button, "RIGHT", -10, 0)
    label:SetJustifyH("LEFT")
  end
  if not button.__lootListDetailAccent then
    local accent = button:CreateTexture(nil, "ARTWORK")
    accent:SetTexture("Interface\\Buttons\\WHITE8x8")
    accent:SetPoint("TOPLEFT", 3, -3)
    accent:SetPoint("BOTTOMLEFT", 3, 3)
    accent:SetWidth(3)
    button.__lootListDetailAccent = accent
  end
  if button.__lootListDetailAccent then
    if danger then
      button.__lootListDetailAccent:SetVertexColor(1.00, 0.36, 0.30, 1)
    else
      button.__lootListDetailAccent:SetVertexColor(0.48, 0.78, 1.00, 1)
    end
  end
  button:SetScript("OnEnter", function(self)
    local color = self.__lootListDetailHover
    if color and self.SetBackdropColor then
      self:SetBackdropColor(color[1], color[2], color[3], color[4])
    end
  end)
  button:SetScript("OnLeave", function(self)
    local color = self.__lootListDetailNormal
    if color and self.SetBackdropColor then
      self:SetBackdropColor(color[1], color[2], color[3], color[4])
    end
  end)
end

local function BuildNeedDicePopupSummary(profile, row)
  if type(profile) ~= "table" or type(row) ~= "table" then
    return "Need Dice: None yet"
  end

  local rolls = {}
  local members = type(row.summaryMembers) == "table" and row.summaryMembers or { row.character }
  for _, memberKey in ipairs(members) do
    local ledger = type(profile.lootLedger) == "table" and profile.lootLedger[memberKey] or nil
    for _, item in ipairs(type(ledger) == "table" and ledger.items or {}) do
      local optionKey = string.lower(TrimText(type(item) == "table" and (item.rollOptionKey or item.rollOptionLabel) or ""))
      local rollValue = tonumber(type(item) == "table" and item.rollValue or nil)
      if optionKey == "need" and rollValue then
        rolls[#rolls + 1] = rollValue
      end
    end
  end

  if #rolls == 0 then
    return "Need Dice: None yet"
  end

  table.sort(rolls, function(a, b)
    return (tonumber(a) or 0) > (tonumber(b) or 0)
  end)

  local highest = tonumber(rolls[1]) or 0
  local total = 0
  local recent = {}
  for index, value in ipairs(rolls) do
    total = total + (tonumber(value) or 0)
    if index <= 8 then
      recent[#recent + 1] = tostring(value)
    end
  end

  return string.format(
    "Need Dice: %d roll(s) | avg %.1f | high %d | recent [%s]",
    #rolls,
    total / #rolls,
    highest,
    table.concat(recent, ", ")
  )
end

local CreateLootListDetailsPopupHelpers = INTERNAL_API.LIDCOUNCIL_CreateLootListDetailsPopupHelpers_Impl
if type(CreateLootListDetailsPopupHelpers) ~= "function" then
  error("LidCouncil missing loot list details popup helpers.")
end

local lootListDetailsPopupHelpers = CreateLootListDetailsPopupHelpers({
  ApplyFrameSkin = ApplyFrameSkin,
  ApplyLootListToolbarButtonStyle = ApplyLootListToolbarButtonStyle,
  ApplyLootListDetailActionButtonStyle = ApplyLootListDetailActionButtonStyle,
  CreateSimpleEditBox = CreateSimpleEditBox,
  GetLootListViewMode = GetLootListViewMode,
})

local CreateLootListAwardsViewHelpers = INTERNAL_API.LIDCOUNCIL_CreateLootListAwardsViewHelpers_Impl
if type(CreateLootListAwardsViewHelpers) ~= "function" then
  error("LidCouncil missing loot list awards view helpers.")
end

local lootListAwardsViewHelpers = CreateLootListAwardsViewHelpers({
  ApplyFrameSkin = ApplyFrameSkin,
  ApplyLootListToolbarButtonStyle = ApplyLootListToolbarButtonStyle,
  BindMouseWheelScroll = BindMouseWheelScroll,
  TrimText = TrimText,
  ResolveOptionalApiFunction = ResolveOptionalApiFunction,
  FormatDateTime = FormatDateTime,
})

local CreateLootListRecommendationsViewHelpers = INTERNAL_API.LIDCOUNCIL_CreateLootListRecommendationsViewHelpers_Impl
if type(CreateLootListRecommendationsViewHelpers) ~= "function" then
  error("LidCouncil missing loot list recommendations view helpers.")
end

local lootListRecommendationsViewHelpers = CreateLootListRecommendationsViewHelpers({
  ApplyFrameSkin = ApplyFrameSkin,
  ApplyLootListToolbarButtonStyle = ApplyLootListToolbarButtonStyle,
  BindMouseWheelScroll = BindMouseWheelScroll,
  CreateCustomDropdownControl = CreateCustomDropdownControl,
  CollectRaidEntries = CollectRaidEntries,
  CollectRaidEncounterEntries = CollectRaidEncounterEntries,
  FindRaidEntryByProfile = FindRaidEntryByProfile,
  CollectRaidLootEntries = CollectRaidLootEntries,
  RAID_LOOT_DIFFICULTIES = RAID_LOOT_DIFFICULTIES,
  FormatRaidLootItemLabel = FormatRaidLootItemLabel,
  ResolveRollOptionIcon = ResolveRollOptionIcon,
  GetLootProfile = GetLootProfile,
  GetCharacterKey = GetCharacterKey,
  TrimText = TrimText,
  ResolveOptionalApiFunction = ResolveOptionalApiFunction,
})

local function BuildLootListPage(editor, page)
  if not editor or not page then return end

  local function RequestLootListPageRefresh(owner)
    if type(owner) ~= "table" then
      return false
    end
    -- Loot List selections must refresh their own detail pane. Routing this
    -- through Backgrounds left the Split view showing the previous character.
    if owner.RefreshLootListPage then
      owner:RefreshLootListPage()
      return true
    end
    if type(RequestEditorPageRefresh) == "function" then
      local handled = RequestEditorPageRefresh(owner, "Backgrounds", "RefreshLootListPage")
      if handled then
        return true
      end
    end
    if owner.Refresh then
      owner:Refresh()
      return true
    end
    return false
  end

  editor.RequestLootListPageRefresh = function(self)
    return RequestLootListPageRefresh(type(self) == "table" and self or editor)
  end

  local function RefreshLootListPageView()
    return RequestLootListPageRefresh(editor)
  end

  local navBg, rightBg = CreateSectionShell(page, 432)
  editor.lootListNavBg = navBg
  editor.lootListRightBg = rightBg

  local modeTabsBg = CreateFrame("Frame", nil, page, "BackdropTemplate")
  modeTabsBg:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
  modeTabsBg:SetPoint("TOPRIGHT", page, "TOPRIGHT", 0, 0)
  modeTabsBg:SetHeight(30)
  ApplyFrameSkin(modeTabsBg)
  if modeTabsBg.SetBackdropColor then
    modeTabsBg:SetBackdropColor(0.025, 0.03, 0.045, 0.96)
  end
  editor.lootLedgerModeTabsBg = modeTabsBg

  navBg:ClearAllPoints()
  navBg:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -36)
  navBg:SetPoint("BOTTOMLEFT", page, "BOTTOMLEFT", 0, 0)
  rightBg:ClearAllPoints()
  rightBg:SetPoint("TOPLEFT", navBg, "TOPRIGHT", 12, 0)
  rightBg:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)

  local inactiveOverlay = CreateFrame("Frame", nil, page, "BackdropTemplate")
  ApplyFrameSkin(inactiveOverlay)
  inactiveOverlay:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
  inactiveOverlay:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
  inactiveOverlay:EnableMouse(true)
  inactiveOverlay:SetFrameStrata("HIGH")
  inactiveOverlay:SetFrameLevel((page.GetFrameLevel and page:GetFrameLevel() or 0) + 20)
  inactiveOverlay:Hide()
  editor.lootListInactiveOverlay = inactiveOverlay

  local inactiveText = inactiveOverlay:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
  inactiveText:SetPoint("CENTER", 0, 0)
  inactiveText:SetWidth(520)
  inactiveText:SetJustifyH("CENTER")
  inactiveText:SetJustifyV("MIDDLE")
  inactiveText:SetText(ACTIVATE_PROFILE_REQUIRED_TEXT)
  editor.lootListInactiveText = inactiveText

  local navTitle = navBg:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
  navTitle:SetPoint("TOPLEFT", 8, -8)
  navTitle:SetText("Loot List")
  navTitle:Hide()
  editor.lootLedgerNavTitle = navTitle

  local lootListBtn = CreateFrame("Button", nil, modeTabsBg, "UIPanelButtonTemplate,BackdropTemplate")
  lootListBtn:SetSize(96, 24)
  lootListBtn:SetPoint("LEFT", modeTabsBg, "LEFT", 8, 0)
  lootListBtn:SetText("Loot List")
  ApplyLootListToolbarButtonStyle(lootListBtn)
  editor.lootLedgerModeLootListBtn = lootListBtn

  local itemHistoryBtn = CreateFrame("Button", nil, modeTabsBg, "UIPanelButtonTemplate,BackdropTemplate")
  itemHistoryBtn:SetSize(108, 22)
  itemHistoryBtn:SetPoint("LEFT", lootListBtn, "RIGHT", 6, 0)
  itemHistoryBtn:SetText("Item History")
  ApplyLootListToolbarButtonStyle(itemHistoryBtn)
  editor.lootLedgerItemHistoryBtn = itemHistoryBtn

  local modeAwardsBtn = CreateFrame("Button", nil, modeTabsBg, "UIPanelButtonTemplate,BackdropTemplate")
  modeAwardsBtn:SetSize(92, 22)
  modeAwardsBtn:SetPoint("LEFT", itemHistoryBtn, "RIGHT", 6, 0)
  modeAwardsBtn:SetText("Fun Awards")
  ApplyLootListToolbarButtonStyle(modeAwardsBtn)
  editor.lootLedgerModeAwardsBtn = modeAwardsBtn

  local itemHistoryView = CreateFrame("Frame", nil, page, "BackdropTemplate")
  itemHistoryView:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -36)
  itemHistoryView:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
  ApplyFrameSkin(itemHistoryView)
  if itemHistoryView.SetBackdropColor then
    itemHistoryView:SetBackdropColor(0.025, 0.03, 0.045, 0.98)
  end
  itemHistoryView:Hide()
  editor.lootLedgerItemHistoryView = itemHistoryView

  local itemHistoryTitle = itemHistoryView:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
  itemHistoryTitle:SetPoint("TOPLEFT", 16, -14)
  itemHistoryTitle:SetText("Item History")
  itemHistoryTitle:SetTextColor(1.00, 0.82, 0.28, 1.00)

  local itemHistorySummary = itemHistoryView:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  itemHistorySummary:SetPoint("TOPLEFT", 16, -38)
  itemHistorySummary:SetPoint("TOPRIGHT", -130, -38)
  itemHistorySummary:SetJustifyH("LEFT")
  itemHistorySummary:SetText("Items, selections, votes, assignments, and trade status from saved sessions.")
  itemHistoryView.__summary = itemHistorySummary

  local itemHistoryHolder = CreateFrame("Frame", nil, itemHistoryView)
  itemHistoryHolder:SetPoint("TOPLEFT", 10, -64)
  itemHistoryHolder:SetPoint("BOTTOMRIGHT", -10, 10)
  editor.lootLedgerItemHistoryHolder = itemHistoryHolder

  local awardsHost = CreateFrame("Frame", nil, page)
  awardsHost:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -36)
  awardsHost:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
  awardsHost:Hide()
  editor.lootLedgerAwardsHost = awardsHost

  local seedTestBtn = CreateFrame("Button", nil, navBg, "UIPanelButtonTemplate,BackdropTemplate")
  seedTestBtn:SetSize(126, 22)
  seedTestBtn:SetPoint("TOPRIGHT", -8, -30)
  seedTestBtn:SetText("Seed Test Raiders")
  ApplyLootListToolbarButtonStyle(seedTestBtn)
  seedTestBtn:Hide()
  editor.lootLedgerSeedTestBtn = seedTestBtn

  local viewModeDropdown = nil
  if type(CreateCustomDropdownControl) == "function" then
    viewModeDropdown = CreateCustomDropdownControl(editor, navBg, {
      width = 134,
      buttonHeight = 22,
      rowHeight = 22,
      maxVisibleEntries = 2,
      text = "View: Pop Out",
    })
  else
    -- Keep one usable control on clients where the shared dropdown cannot load.
    viewModeDropdown = CreateFrame("Button", nil, navBg, "UIPanelButtonTemplate,BackdropTemplate")
    viewModeDropdown:SetSize(134, 22)
    ApplyLootListToolbarButtonStyle(viewModeDropdown)
    viewModeDropdown.__lootLedgerViewModeFallback = true
  end
  editor.lootLedgerViewModeDropdown = viewModeDropdown

  local function UpdateLootListToolbarLayout()
    if not editor then
      return
    end
    local anchor = editor.lootLedgerSeedTestBtn
    local viewMode = editor.lootLedgerViewModeDropdown
    if not viewMode or not navBg then
      return
    end

    if anchor and anchor.IsShown and anchor:IsShown() then
      viewMode:ClearAllPoints()
      viewMode:SetPoint("RIGHT", anchor, "LEFT", -8, 0)
    else
      viewMode:ClearAllPoints()
      viewMode:SetPoint("TOPRIGHT", navBg, "TOPRIGHT", -8, -30)
    end
  end
  editor.UpdateLootListToolbarLayout = UpdateLootListToolbarLayout
  UpdateLootListToolbarLayout()

  local function GetLootListContentMode()
    local root = RootDB()
    root.ui = root.ui or {}
    local mode = TrimText(root.ui.lootLedgerContentMode)
    if mode ~= "ITEM_HISTORY" and mode ~= "FUN_AWARDS" then
      mode = "LOOT_LIST"
    end
    root.ui.lootLedgerContentMode = mode
    return mode
  end

  local function SetLootListContentMode(mode)
    local root = RootDB()
    root.ui = root.ui or {}
    root.ui.lootLedgerContentMode = mode == "ITEM_HISTORY" and "ITEM_HISTORY"
      or mode == "FUN_AWARDS" and "FUN_AWARDS"
      or "LOOT_LIST"
    editor.__lootLedgerItemHistoryVisible = root.ui.lootLedgerContentMode == "ITEM_HISTORY"
    RefreshLootListPageView()
  end
  editor.ShowLootListContentMode = function(self, mode)
    SetLootListContentMode(type(self) == "table" and mode or self)
  end

  local function UpdateLootListModeButtons()
    local mode = GetLootListViewMode()
    local listOnly = mode == "LIST_ONLY"
    local contentMode = GetLootListContentMode()
    local showLootList = contentMode == "LOOT_LIST"
    local itemHistoryOpen = contentMode == "ITEM_HISTORY"
    local awardsOpen = contentMode == "FUN_AWARDS"
    if editor.lootLedgerViewModeDropdown then
      local viewText = listOnly and "View: Pop Out" or "View: Split"
      if editor.lootLedgerViewModeDropdown.SetSelectedValue then
        editor.lootLedgerViewModeDropdown:SetSelectedValue(mode)
      end
      if editor.lootLedgerViewModeDropdown.SetText then
        editor.lootLedgerViewModeDropdown:SetText(viewText)
      end
      if editor.lootLedgerViewModeDropdown.__lootLedgerViewModeFallback then
        ApplyLootListToolbarButtonStyle(editor.lootLedgerViewModeDropdown)
        SetTextListButtonTextColor(editor.lootLedgerViewModeDropdown, { 0.92, 0.92, 0.96, 1.00 })
      end
      editor.lootLedgerViewModeDropdown:SetShown(showLootList)
    end
    if editor.lootLedgerModeLootListBtn then
      ApplyLootListToolbarButtonStyle(editor.lootLedgerModeLootListBtn)
      SetTextListButtonTextColor(editor.lootLedgerModeLootListBtn, showLootList and { 1.00, 0.94, 0.62, 1.00 } or { 0.82, 0.82, 0.86, 1.00 })
      SetLootListToolbarButtonSelected(editor.lootLedgerModeLootListBtn, showLootList)
    end
    if editor.lootLedgerModeAwardsBtn then
      ApplyLootListToolbarButtonStyle(editor.lootLedgerModeAwardsBtn)
      SetTextListButtonTextColor(editor.lootLedgerModeAwardsBtn, awardsOpen and { 1.00, 0.94, 0.62, 1.00 } or { 0.82, 0.82, 0.86, 1.00 })
      SetLootListToolbarButtonSelected(editor.lootLedgerModeAwardsBtn, awardsOpen)
    end
    if editor.lootLedgerItemHistoryBtn then
      ApplyLootListToolbarButtonStyle(editor.lootLedgerItemHistoryBtn)
      SetTextListButtonTextColor(editor.lootLedgerItemHistoryBtn, itemHistoryOpen and { 1.00, 0.94, 0.62, 1.00 } or { 0.82, 0.82, 0.86, 1.00 })
      SetLootListToolbarButtonSelected(editor.lootLedgerItemHistoryBtn, itemHistoryOpen)
    end
    UpdateLootListToolbarLayout()
  end
  editor.UpdateLootListModeButtons = UpdateLootListModeButtons
  UpdateLootListModeButtons()
  editor.OpenLootLedgerDetailsPopup = function(self, row)
    lootListDetailsPopupHelpers.OpenLootListDetailsPopup(self, row)
  end
  editor.OpenLootLedgerAwardsPopup = function(self, profileName)
    SetLootListContentMode("FUN_AWARDS")
  end
  editor.OpenLootLedgerRecommendationsPopup = function(self, profileName)
    lootListRecommendationsViewHelpers.OpenLootListRecommendationsPopup(self, profileName)
  end
  editor.MountLootPresetsRecommendations = function(self, parent, profileName)
    return lootListRecommendationsViewHelpers.MountLootListRecommendations(self, parent, profileName)
  end

  editor.ShowLootListItemHistory = function(self)
    SetLootListContentMode("ITEM_HISTORY")
  end

  lootListBtn:SetScript("OnClick", function()
    SetLootListContentMode("LOOT_LIST")
  end)
  itemHistoryBtn:SetScript("OnClick", function()
    SetLootListContentMode("ITEM_HISTORY")
  end)
  modeAwardsBtn:SetScript("OnClick", function()
    SetLootListContentMode("FUN_AWARDS")
  end)

  local searchLabel = navBg:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  searchLabel:SetPoint("TOPLEFT", 8, -30)
  searchLabel:SetText("Search:")
  editor.lootLedgerSearchLabel = searchLabel

  local searchEdit = CreateSimpleEditBox(navBg, 180, 22)
  searchEdit:SetPoint("LEFT", searchLabel, "RIGHT", 8, 0)
  editor.lootLedgerSearchEdit = searchEdit

  local headerRow = CreateFrame("Frame", nil, navBg)
  headerRow:SetPoint("TOPLEFT", 8, -58)
  headerRow:SetSize(394, 18)
  editor.lootLedgerListHeader = headerRow

  local function GetLootLedgerSortState()
    local root = RootDB()
    root.ui = root.ui or {}
    local sortKey = TrimText(root.ui.lootLedgerSortKey)
    if VALID_LOOT_LEDGER_SORT_KEYS[sortKey] ~= true then
      sortKey = "name"
    end
    local descending = root.ui.lootLedgerSortDescending
    if type(descending) ~= "boolean" then
      descending = NUMERIC_LOOT_LEDGER_SORT_KEYS[sortKey] == true
    end
    root.ui.lootLedgerSortKey = sortKey
    root.ui.lootLedgerSortDescending = descending
    editor.__lootLedgerSortKey = sortKey
    editor.__lootLedgerSortDescending = descending
    return sortKey, descending
  end

  local function SetHeaderLabelColor(button, color)
    local label = button and button.__labelText or nil
    local resolved = type(color) == "table" and color or nil
    if label and label.SetTextColor then
      label:SetTextColor(
        tonumber(resolved and resolved[1]) or 0.86,
        tonumber(resolved and resolved[2]) or 0.86,
        tonumber(resolved and resolved[3]) or 0.90,
        tonumber(resolved and resolved[4]) or 1
      )
    end
  end

  local function UpdateLootLedgerSortHeaderState(self)
    local sortKey, descending = GetLootLedgerSortState()
    for _, button in ipairs((self and self.__lootLedgerSortHeaders) or {}) do
      local isSelected = button.__sortKey == sortKey
      local suffix = isSelected and (descending and " v" or " ^") or ""
      if button.__labelText then
        button.__labelText:SetText(tostring(button.__baseLabel or "") .. suffix)
      end
      if button.__baseBg then
        button.__baseBg:SetVertexColor(0.08, 0.06, 0.08, 0.82)
      end
      if button.__selectionBg then
        button.__selectionBg:SetVertexColor(0, 0, 0, 0)
      end
      if button.__selectionAccent then
        button.__selectionAccent:SetVertexColor(0, 0, 0, 0)
      end
      if button.SetButtonState then
        button:SetButtonState("NORMAL", false)
      end
      SetHeaderLabelColor(button, { 0.86, 0.86, 0.90, 1.00 })
    end
  end

  local function ApplyLootLedgerSort(sortKey)
    local currentKey, currentDescending = GetLootLedgerSortState()
    local root = RootDB()
    root.ui = root.ui or {}
    local nextDescending
    if currentKey == sortKey then
      nextDescending = not currentDescending
    else
      nextDescending = NUMERIC_LOOT_LEDGER_SORT_KEYS[sortKey] == true
    end
    root.ui.lootLedgerSortKey = sortKey
    root.ui.lootLedgerSortDescending = nextDescending
    editor.__lootLedgerSortKey = sortKey
    editor.__lootLedgerSortDescending = nextDescending
    UpdateLootLedgerSortHeaderState(editor)
    RefreshLootListPageView()
  end

  local function MakeHeaderText(parent, label, x, width, sortKey, tooltipText)
    if sortKey then
      local button = CreateFrame("Button", nil, parent)
      button:SetPoint("TOPLEFT", x, 0)
      button:SetSize(width, 18)
      button:RegisterForClicks("LeftButtonUp")

      local bg = button:CreateTexture(nil, "BACKGROUND")
      bg:SetAllPoints(true)
      bg:SetTexture("Interface\\Buttons\\WHITE8x8")
      bg:SetVertexColor(0.08, 0.06, 0.08, 0.82)
      button.__baseBg = bg

      local hover = button:CreateTexture(nil, "HIGHLIGHT")
      hover:SetAllPoints(true)
      hover:SetTexture("Interface\\Buttons\\WHITE8x8")
      do
        local themePalette = GetEditorThemePalette and GetEditorThemePalette() or nil
        local active = type(themePalette) == "table" and themePalette.active or { 1.00, 0.88, 0.32, 0.92 }
        hover:SetVertexColor(active[1] or 1.00, active[2] or 0.88, active[3] or 0.32, 0.10)
      end
      button.__hoverBg = hover

      local text = button:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
      text:SetPoint("TOPLEFT", 0, 0)
      text:SetPoint("BOTTOMRIGHT", 0, 0)
      text:SetJustifyH("LEFT")
      text:SetText(label)
      button.__labelText = text
      button.__baseLabel = label
      button.__sortKey = sortKey
      button.__tooltipText = tooltipText
      button:SetScript("OnClick", function()
        ApplyLootLedgerSort(sortKey)
      end)
      button:SetScript("OnEnter", function(self)
        local tip = type(self.__tooltipText) == "string" and self.__tooltipText or ""
        if tip == "" or type(GameTooltip) ~= "table" then
          return
        end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(tip, 1.00, 0.92, 0.35, 1.00, true)
        GameTooltip:Show()
      end)
      button:SetScript("OnLeave", function()
        if type(GameTooltip) == "table" then
          GameTooltip:Hide()
        end
      end)
      return button
    end

    local text = parent:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    text:SetPoint("TOPLEFT", x, 0)
    text:SetWidth(width)
    text:SetJustifyH("LEFT")
    text:SetText(label)
    return text
  end

  editor.lootLedgerHeaderSpec = MakeHeaderText(headerRow, "Spec", 22, 52, "spec")
  editor.lootLedgerHeaderName = MakeHeaderText(headerRow, "Character-Realm", 78, 164, "name")
  editor.lootLedgerHeaderNote = MakeHeaderText(headerRow, "Note", 246, 96, "note")
  editor.lootLedgerHeaderSessions = MakeHeaderText(headerRow, "Sess", 244, 40, "sessions", "Joined Sessions")
  editor.lootLedgerHeaderItems = MakeHeaderText(headerRow, "Items", 288, 40, "items", "Received items")
  editor.lootLedgerHeaderOffSpec = MakeHeaderText(headerRow, "OS", 332, 30, "offspec", "Off-Spec")
  editor.lootLedgerHeaderTransmog = MakeHeaderText(headerRow, "TM", 366, 30, "transmog", "Transmog")
  editor.lootLedgerHeaderDisenchant = MakeHeaderText(headerRow, "DE", 400, 30, "disenchant", "Disenchant")
  editor.lootLedgerHeaderPoints = MakeHeaderText(headerRow, "Points", 434, 42, "points", "Character Points")
  editor.lootLedgerHeaderBis = MakeHeaderText(headerRow, "BiS", 480, 30, "bis", "Best in Slot")
  editor.lootLedgerHeaderTotal = MakeHeaderText(headerRow, "Total", 514, 40, "total", "Total Points")
  editor.__lootLedgerSortHeaders = {
    editor.lootLedgerHeaderSpec,
    editor.lootLedgerHeaderName,
    editor.lootLedgerHeaderNote,
    editor.lootLedgerHeaderSessions,
    editor.lootLedgerHeaderItems,
    editor.lootLedgerHeaderOffSpec,
    editor.lootLedgerHeaderTransmog,
    editor.lootLedgerHeaderDisenchant,
    editor.lootLedgerHeaderPoints,
    editor.lootLedgerHeaderBis,
    editor.lootLedgerHeaderTotal,
  }
  editor.UpdateLootLedgerSortHeaderState = UpdateLootLedgerSortHeaderState
  UpdateLootLedgerSortHeaderState(editor)

  local charScroll = CreateFrame("ScrollFrame", nil, navBg, "UIPanelScrollFrameTemplate")
  charScroll:SetPoint("TOPLEFT", 8, -80)
  charScroll:SetPoint("BOTTOMRIGHT", -26, 30)
  BindMouseWheelScroll(charScroll, 18)
  editor.lootLedgerCharScroll = charScroll

  local charContent = CreateFrame("Frame", nil, charScroll)
  charContent:SetSize(394, 1)
  charScroll:SetScrollChild(charContent)
  editor.lootLedgerCharContent = charContent
  editor.lootLedgerCharacterRows = {}

  local addGroupBtn = CreateFrame("Button", nil, navBg, "UIPanelButtonTemplate")
  addGroupBtn:SetSize(118, 22)
  addGroupBtn:SetPoint("BOTTOMLEFT", 8, 6)
  addGroupBtn:SetText("Add Group")
  addGroupBtn:Hide()
  editor.lootLedgerAddGroupBtn = addGroupBtn

  local deleteGroupBtn = CreateFrame("Button", nil, navBg, "UIPanelButtonTemplate")
  deleteGroupBtn:SetSize(118, 22)
  deleteGroupBtn:SetPoint("LEFT", addGroupBtn, "RIGHT", 8, 0)
  deleteGroupBtn:SetText("Delete Group")
  deleteGroupBtn:Hide()
  editor.lootLedgerDeleteGroupBtn = deleteGroupBtn

  local dragHint = navBg:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  dragHint:SetPoint("BOTTOMLEFT", 8, 6)
  dragHint:SetWidth(394)
  dragHint:SetJustifyH("LEFT")
  dragHint:SetText("Select a character to review details and manage Main / Alt links.")
  editor.lootLedgerDragHint = dragHint

  local rightScroll = CreateFrame("ScrollFrame", nil, rightBg, "UIPanelScrollFrameTemplate")
  rightScroll:SetPoint("TOPLEFT", 6, -6)
  rightScroll:SetPoint("BOTTOMRIGHT", -28, 6)
  BindMouseWheelScroll(rightScroll, 18)
  editor.lootLedgerRightScroll = rightScroll

  local rightContent = CreateFrame("Frame", nil, rightScroll)
  rightContent:SetSize(560, 1)
  rightScroll:SetScrollChild(rightContent)
  editor.lootLedgerRightContent = rightContent

  local title = rightContent:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
  title:SetPoint("TOPLEFT", 18, -16)
  title:SetText("Loot List Details")
  editor.lootListTitle = title

  local heroFrame = CreateFrame("Frame", nil, rightContent, "BackdropTemplate")
  heroFrame:SetPoint("TOPLEFT", 18, -46)
  heroFrame:SetSize(552, 126)
  ApplyFrameSkin(heroFrame)
  editor.lootLedgerHeroFrame = heroFrame

  local heroTalentBackground = heroFrame:CreateTexture(nil, "ARTWORK", nil, -4)
  heroTalentBackground:SetBlendMode("BLEND")
  heroTalentBackground:Hide()
  editor.lootLedgerHeroTalentBackground = heroTalentBackground

  local heroTalentFade = heroFrame:CreateTexture(nil, "ARTWORK", nil, -3)
  heroTalentFade:SetTexture("Interface\\Buttons\\WHITE8x8")
  if heroTalentFade.SetGradientAlpha then
    heroTalentFade:SetGradientAlpha("HORIZONTAL", 0.01, 0.02, 0.04, 0.90, 0.01, 0.02, 0.04, 0.02)
  else
    heroTalentFade:SetColorTexture(0.01, 0.02, 0.04, 0.48)
  end
  heroTalentFade:Hide()
  editor.lootLedgerHeroTalentFade = heroTalentFade

  local heroIconFrame = CreateFrame("Frame", nil, heroFrame, "BackdropTemplate")
  heroIconFrame:SetSize(52, 52)
  heroIconFrame:SetPoint("TOPLEFT", 14, -14)
  ApplyFrameSkin(heroIconFrame)
  if heroIconFrame.SetBackdropColor then
    heroIconFrame:SetBackdropColor(0.01, 0.015, 0.025, 0.96)
  end
  if heroIconFrame.SetBackdropBorderColor then
    heroIconFrame:SetBackdropBorderColor(0.08, 0.10, 0.14, 1.00)
  end
  editor.lootLedgerHeroIconFrame = heroIconFrame

  local heroIcon = heroIconFrame:CreateTexture(nil, "ARTWORK")
  heroIcon:SetPoint("TOPLEFT", 2, -2)
  heroIcon:SetPoint("BOTTOMRIGHT", -2, 2)
  heroIcon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
  editor.lootLedgerHeroIcon = heroIcon

  local selectedLabel = heroFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
  selectedLabel:SetPoint("TOPLEFT", heroIconFrame, "TOPRIGHT", 16, -2)
  selectedLabel:SetPoint("RIGHT", -14, 0)
  selectedLabel:SetJustifyH("LEFT")
  if selectedLabel.SetMaxLines then
    selectedLabel:SetMaxLines(1)
  end
  selectedLabel:SetText("(no character selected)")
  editor.lootLedgerSelectedLabel = selectedLabel

  local heroRoleBadge = CreateFrame("Frame", nil, heroFrame, "BackdropTemplate")
  heroRoleBadge:SetSize(56, 20)
  ApplyFrameSkin(heroRoleBadge)
  heroRoleBadge:Hide()
  local heroRoleText = heroRoleBadge:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
  heroRoleText:SetAllPoints(heroRoleBadge)
  heroRoleText:SetJustifyH("CENTER")
  heroRoleText:SetText("MAIN")
  editor.lootLedgerHeroRoleBadge = heroRoleBadge
  editor.lootLedgerHeroRoleText = heroRoleText

  local totalLabel = heroFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
  totalLabel:SetPoint("TOPLEFT", selectedLabel, "BOTTOMLEFT", 0, -6)
  totalLabel:SetPoint("RIGHT", -14, 0)
  totalLabel:SetJustifyH("LEFT")
  if totalLabel.SetMaxLines then
    totalLabel:SetMaxLines(1)
  end
  totalLabel:SetText("Class / Spec")
  editor.lootLedgerTotalLabel = totalLabel

  local summaryText = heroFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  summaryText:SetPoint("TOPLEFT", totalLabel, "BOTTOMLEFT", 0, -8)
  summaryText:SetPoint("RIGHT", -14, 0)
  summaryText:SetJustifyH("LEFT")
  summaryText:SetText("")
  editor.lootLedgerSummaryText = summaryText

  editor.lootLedgerCharacterNoteFrame = CreateFrame("Frame", nil, rightContent, "BackdropTemplate")
  editor.lootLedgerCharacterNoteFrame:SetSize(552, 56)
  ApplyFrameSkin(editor.lootLedgerCharacterNoteFrame)
  editor.lootLedgerCharacterNoteFrame:Hide()

  editor.lootLedgerCharacterNoteDisplay = editor.lootLedgerCharacterNoteFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  editor.lootLedgerCharacterNoteDisplay:SetPoint("TOPLEFT", 10, -27)
  editor.lootLedgerCharacterNoteDisplay:SetJustifyH("LEFT")
  editor.lootLedgerCharacterNoteDisplay:SetJustifyV("TOP")
  editor.lootLedgerCharacterNoteDisplay:SetText("No personal note added.")

  editor.lootLedgerCharacterNoteToggleBtn = CreateFrame("Button", nil, editor.lootLedgerCharacterNoteFrame, "UIPanelButtonTemplate")
  editor.lootLedgerCharacterNoteToggleBtn:SetSize(88, 22)
  editor.lootLedgerCharacterNoteToggleBtn:SetText("Edit Note")
  ApplyLootListDetailActionButtonStyle(editor.lootLedgerCharacterNoteToggleBtn, false)

  editor.lootLedgerCharacterNoteSaveBtn = CreateFrame("Button", nil, editor.lootLedgerCharacterNoteFrame, "UIPanelButtonTemplate")
  editor.lootLedgerCharacterNoteSaveBtn:SetSize(92, 22)
  editor.lootLedgerCharacterNoteSaveBtn:SetText("Save Note")
  ApplyLootListDetailActionButtonStyle(editor.lootLedgerCharacterNoteSaveBtn, false)
  editor.lootLedgerCharacterNoteSaveBtn:Hide()

  editor.lootLedgerCharacterNoteCancelBtn = CreateFrame("Button", nil, editor.lootLedgerCharacterNoteFrame, "UIPanelButtonTemplate")
  editor.lootLedgerCharacterNoteCancelBtn:SetSize(76, 22)
  editor.lootLedgerCharacterNoteCancelBtn:SetText("Cancel")
  ApplyLootListDetailActionButtonStyle(editor.lootLedgerCharacterNoteCancelBtn, false)
  editor.lootLedgerCharacterNoteCancelBtn:Hide()

  local statCards = {}
  local statLabels = {
    { key = "sessions", title = "Sessions Joined" },
    { key = "items", title = "Items Received" },
    { key = "points", title = "Character Points" },
    { key = "total", title = "Linked Group Points" },
    { key = "bis", title = "BiS" },
    { key = "offspec", title = "Off-Spec" },
    { key = "transmog", title = "Transmog" },
    { key = "disenchant", title = "Disenchant" },
  }
  for index, info in ipairs(statLabels) do
    local card = CreateFrame("Frame", nil, rightContent, "BackdropTemplate")
    card:SetSize(128, 54)
    ApplyFrameSkin(card)

    local valueText = card:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    valueText:SetPoint("TOPLEFT", 10, -8)
    valueText:SetJustifyH("LEFT")
    valueText:SetText("0")

    local titleText = card:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    titleText:SetPoint("TOPLEFT", valueText, "BOTTOMLEFT", 0, -4)
    titleText:SetPoint("RIGHT", -8, 0)
    titleText:SetJustifyH("LEFT")
    titleText:SetText(info.title)

    statCards[info.key] = {
      frame = card,
      value = valueText,
      title = titleText,
    }
  end
  editor.lootLedgerStatCards = statCards

  local activityHeader = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  activityHeader:SetText("ACTIVITY")
  activityHeader:SetTextColor(0.84, 0.75, 0.42, 1)
  editor.lootLedgerActivityHeader = activityHeader

  local awardsHeader = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  awardsHeader:SetText("AWARD TYPES")
  awardsHeader:SetTextColor(0.84, 0.75, 0.42, 1)
  editor.lootLedgerAwardsHeader = awardsHeader

  local membersDivider = CreateSectionDivider(rightContent)
  editor.lootLedgerMembersDivider = membersDivider

  local membersHeader = MakeYellowSubHeader(rightContent, "Linked Characters", 18, -150)
  editor.lootLedgerMembersHeader = membersHeader

  local roleLabel = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  roleLabel:SetPoint("TOPLEFT", 18, -176)
  roleLabel:SetText("Role:")
  editor.lootLedgerRoleLabel = roleLabel

  local roleDropdown = nil
  if type(CreateCustomDropdownControl) == "function" then
    roleDropdown = CreateCustomDropdownControl(editor, rightContent, {
      width = 120,
      text = "Main",
    })
    roleDropdown:SetPoint("LEFT", roleLabel, "RIGHT", 8, 0)
  else
    roleDropdown = CreateSimpleEditBox(rightContent, 120, 22)
    roleDropdown:SetPoint("LEFT", roleLabel, "RIGHT", 8, 0)
  end
  editor.lootLedgerRoleDropdown = roleDropdown

  local noteLabel = editor.lootLedgerCharacterNoteFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
  noteLabel:SetPoint("TOPLEFT", 18, -206)
  noteLabel:SetText("CHARACTER NOTE")
  editor.lootLedgerCharacterNoteLabel = noteLabel

  local characterNoteEdit = CreateSimpleEditBox(editor.lootLedgerCharacterNoteFrame, 360, 22)
  characterNoteEdit:SetPoint("LEFT", noteLabel, "RIGHT", 8, 0)
  editor.lootLedgerCharacterNoteEdit = characterNoteEdit

  local mainLabel = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  mainLabel:SetPoint("TOPLEFT", 18, -236)
  mainLabel:SetText("Main:")
  editor.lootLedgerMainLabel = mainLabel

  local summaryDropdown = nil
  if type(CreateCustomDropdownControl) == "function" then
    summaryDropdown = CreateCustomDropdownControl(editor, rightContent, {
      width = 280,
      text = "(select main)",
    })
    summaryDropdown:SetPoint("LEFT", mainLabel, "RIGHT", 8, 0)
  else
    summaryDropdown = CreateSimpleEditBox(rightContent, 280, 22)
    summaryDropdown:SetPoint("LEFT", mainLabel, "RIGHT", 8, 0)
  end
  editor.lootLedgerSummaryDropdown = summaryDropdown

  local linkSummaryBtn = CreateFrame("Button", nil, rightContent, "UIPanelButtonTemplate")
  linkSummaryBtn:SetSize(96, 22)
  linkSummaryBtn:SetPoint("LEFT", roleDropdown, "RIGHT", 10, 0)
  linkSummaryBtn:SetText("Apply")
  editor.lootLedgerSummaryLinkBtn = linkSummaryBtn

  local clearSummaryBtn = CreateFrame("Button", nil, rightContent, "UIPanelButtonTemplate")
  clearSummaryBtn:SetSize(110, 22)
  clearSummaryBtn:SetPoint("LEFT", linkSummaryBtn, "RIGHT", 8, 0)
  clearSummaryBtn:SetText("Make Main")
  editor.lootLedgerSummaryClearBtn = clearSummaryBtn

  local membersRowsFrame = CreateFrame("Frame", nil, rightContent)
  membersRowsFrame:SetPoint("TOPLEFT", 18, -266)
  membersRowsFrame:SetSize(552, 1)
  editor.lootLedgerMembersRowsFrame = membersRowsFrame
  editor.lootLedgerMembersRows = {}

  local membersText = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  membersText:SetPoint("TOPLEFT", membersRowsFrame, "BOTTOMLEFT", 0, -8)
  membersText:SetWidth(560)
  membersText:SetJustifyH("LEFT")
  membersText:SetText("")
  editor.lootLedgerMembersText = membersText

  local manageLinksBtn = CreateFrame("Button", nil, rightContent, "UIPanelButtonTemplate")
  manageLinksBtn:SetSize(236, 32)
  manageLinksBtn:SetText("Manage Character Links")
  ApplyLootListDetailActionButtonStyle(manageLinksBtn, false)
  if manageLinksBtn.SetFrameLevel and rightContent.GetFrameLevel then
    manageLinksBtn:SetFrameLevel((rightContent:GetFrameLevel() or 1) + 20)
  end
  editor.lootLedgerManageLinksBtn = manageLinksBtn

  local manualDivider = CreateSectionDivider(rightContent)
  editor.lootLedgerManualDivider = manualDivider

  local addHeader = MakeYellowSubHeader(rightContent, "History Actions", 18, -304)
  editor.lootLedgerHeader = addHeader

  local manualToggleBtn = CreateFrame("Button", nil, rightContent, "UIPanelButtonTemplate")
  manualToggleBtn:SetSize(236, 32)
  manualToggleBtn:SetText("Add Manual Adjustment")
  ApplyLootListDetailActionButtonStyle(manualToggleBtn, false)
  editor.lootLedgerManualToggleBtn = manualToggleBtn

  local function MakeFieldLabel(parent, label)
    local text = parent:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    text:SetJustifyH("LEFT")
    text:SetText(label)
    return text
  end

  local characterEditLabel = MakeFieldLabel(rightContent, "Character-Realm")
  editor.lootLedgerCharacterEditLabel = characterEditLabel

  local characterEdit = CreateSimpleEditBox(rightContent, 180, 22)
  characterEdit:SetPoint("TOPLEFT", 18, -304)
  characterEditLabel:Hide()
  characterEdit:Hide()
  editor.lootLedgerCharacterEdit = characterEdit

  local itemEditLabel = MakeFieldLabel(rightContent, "Comment")
  editor.lootLedgerItemEditLabel = itemEditLabel

  local itemEdit = CreateSimpleEditBox(rightContent, 260, 22)
  itemEdit:SetPoint("TOPLEFT", 18, -304)
  editor.lootLedgerItemEdit = itemEdit

  local pointsEditLabel = MakeFieldLabel(rightContent, "Points")
  editor.lootLedgerPointsEditLabel = pointsEditLabel

  local pointsEdit = CreateSimpleEditBox(rightContent, 70, 22)
  pointsEdit:SetPoint("LEFT", itemEdit, "RIGHT", 10, 0)
  editor.lootLedgerPointsEdit = pointsEdit

  local function SanitizeManualPointsText(value)
    local text = tostring(value or "")
    local sign = ""
    if text:sub(1, 1) == "-" then
      sign = "-"
    end
    text = text:gsub("[^%d]", "")
    if text == "" then
      return sign
    end
    return sign .. text
  end

  pointsEdit:SetScript("OnTextChanged", function(self, userInput)
    if not userInput or self.__suppressManualSanitize then
      return
    end
    local current = self:GetText() or ""
    local sanitized = SanitizeManualPointsText(current)
    if sanitized ~= current then
      self.__suppressManualSanitize = true
      self:SetText(sanitized)
      self:SetCursorPosition(#sanitized)
      self.__suppressManualSanitize = nil
    end
  end)

  local addBtn = CreateFrame("Button", nil, rightContent, "UIPanelButtonTemplate")
  addBtn:SetSize(150, 22)
  addBtn:SetPoint("TOPLEFT", 18, -336)
  addBtn:SetText("Save Adjustment")
  ApplyLootListDetailActionButtonStyle(addBtn, false)
  editor.lootLedgerAddEntryBtn = addBtn

  local deleteManualBtn = CreateFrame("Button", nil, rightContent, "UIPanelButtonTemplate")
  deleteManualBtn:SetSize(170, 22)
  deleteManualBtn:SetPoint("LEFT", addBtn, "RIGHT", 10, 0)
  deleteManualBtn:SetText("Delete Selected Adjustment")
  ApplyLootListDetailActionButtonStyle(deleteManualBtn, true)
  deleteManualBtn:Hide()
  editor.lootLedgerDeleteEntryBtn = deleteManualBtn

  local dangerLabel = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  dangerLabel:SetText("DANGER ZONE")
  dangerLabel:SetTextColor(1.00, 0.36, 0.30, 1)
  editor.lootLedgerDangerLabel = dangerLabel

  local dangerDivider = CreateSectionDivider(rightContent)
  editor.lootLedgerDangerDivider = dangerDivider

  local deleteCharacterBtn = CreateFrame("Button", nil, rightContent, "UIPanelButtonTemplate")
  deleteCharacterBtn:SetSize(184, 26)
  deleteCharacterBtn:SetText("Delete Character Data")
  ApplyLootListDetailActionButtonStyle(deleteCharacterBtn, true)
  editor.lootLedgerDeleteCharacterBtn = deleteCharacterBtn

  local feedback = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  feedback:SetPoint("TOPLEFT", addBtn, "BOTTOMLEFT", 0, -8)
  feedback:SetWidth(560)
  feedback:SetJustifyH("LEFT")
  feedback:SetText("")
  editor.lootLedgerFeedback = feedback

  local historyDivider = CreateSectionDivider(rightContent)
  editor.lootLedgerHistoryDivider = historyDivider

  local historyHeader = MakeYellowSubHeader(rightContent, "Loot History", 18, -280)
  editor.lootLedgerHistoryHeader = historyHeader

  local historyColumns = CreateFrame("Frame", nil, rightContent)
  historyColumns:SetPoint("TOPLEFT", 18, -304)
  historyColumns:SetSize(552, 18)
  editor.lootLedgerHistoryColumns = historyColumns

  editor.lootLedgerHistoryItemHeader = MakeHeaderText(historyColumns, "Item", 0, 286)
  editor.lootLedgerHistoryReceivedHeader = MakeHeaderText(historyColumns, "Received", 300, 160)
  editor.lootLedgerHistoryPointsHeader = MakeHeaderText(historyColumns, "Points", 468, 60)

  local historyRowsFrame = CreateFrame("Frame", nil, rightContent)
  historyRowsFrame:SetPoint("TOPLEFT", 18, -326)
  historyRowsFrame:SetSize(552, 1)
  editor.lootLedgerHistoryRowsFrame = historyRowsFrame
  editor.lootLedgerHistoryRows = {}

  local historyText = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  historyText:SetPoint("TOPLEFT", historyRowsFrame, "BOTTOMLEFT", 0, -8)
  historyText:SetWidth(560)
  historyText:SetJustifyH("LEFT")
  historyText:SetText("")
  editor.lootLedgerHistoryText = historyText

  local historyPager = CreateFrame("Frame", nil, rightContent)
  historyPager:SetSize(552, 22)
  historyPager:Hide()
  editor.lootLedgerHistoryPager = historyPager

  local historyPrevBtn = CreateFrame("Button", nil, historyPager, "UIPanelButtonTemplate")
  historyPrevBtn:SetSize(86, 22)
  historyPrevBtn:SetPoint("LEFT", 0, 0)
  historyPrevBtn:SetText("Previous")
  ApplyLootListDetailActionButtonStyle(historyPrevBtn, false)
  editor.lootLedgerHistoryPrevBtn = historyPrevBtn

  local historyPageText = historyPager:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  historyPageText:SetPoint("LEFT", historyPrevBtn, "RIGHT", 12, 0)
  historyPageText:SetPoint("RIGHT", historyPager, "RIGHT", -98, 0)
  historyPageText:SetJustifyH("CENTER")
  historyPageText:SetText("Page 1 / 1")
  editor.lootLedgerHistoryPageText = historyPageText

  local historyNextBtn = CreateFrame("Button", nil, historyPager, "UIPanelButtonTemplate")
  historyNextBtn:SetSize(86, 22)
  historyNextBtn:SetPoint("RIGHT", 0, 0)
  historyNextBtn:SetText("Next")
  ApplyLootListDetailActionButtonStyle(historyNextBtn, false)
  editor.lootLedgerHistoryNextBtn = historyNextBtn

  page.__contentHeight = 1

  local function EnsureLootListStaticPopups()
    if type(StaticPopupDialogs) ~= "table" then
      return
    end

    if not StaticPopupDialogs.LIDCOUNCIL_CREATE_LEDGER_GROUP then
      StaticPopupDialogs.LIDCOUNCIL_CREATE_LEDGER_GROUP = {
        text = "Create new Loot List group:",
        button1 = ACCEPT,
        button2 = CANCEL,
        hasEditBox = 1,
        maxLetters = MAX_LOOTLIST_GROUP_NAME,
        timeout = 0,
        whileDead = 1,
        hideOnEscape = 1,
        preferredIndex = 3,
        OnShow = function(self)
          if self.editBox then
            self.editBox:SetText("")
            self.editBox:SetFocus()
          end
        end,
        EditBoxOnEnterPressed = function(self)
          local parent = self:GetParent()
          if parent and parent.button1 then
            parent.button1:Click()
          end
        end,
        OnAccept = function(self)
          local _, profileName = GetLootProfile(GetLootListContextProfileName())
          if not GetLootProfileAccessInfo(profileName).canEditLootList then
            if editor.lootLedgerDragHint then
              editor.lootLedgerDragHint:SetText("Raiders can view the Loot List, but cannot modify it.")
            end
            return
          end
          local profile = GetLootProfile(GetLootListContextProfileName())
          local ok, result = CreateLootListGroup(profile, self.editBox and self.editBox:GetText() or "")
          if ok then
            editor.selectedLootLedgerType = "group"
            editor.selectedLootLedgerKey = tostring(result or "")
            if editor.lootLedgerDragHint then
              editor.lootLedgerDragHint:SetText("Created group: " .. tostring(result or ""))
            end
            RefreshLootListPageView()
          elseif editor.lootLedgerDragHint then
            editor.lootLedgerDragHint:SetText(tostring(result or "Could not create Loot List group."))
          end
        end,
      }
    end

    if not StaticPopupDialogs.LIDCOUNCIL_DELETE_LEDGER_GROUP then
      StaticPopupDialogs.LIDCOUNCIL_DELETE_LEDGER_GROUP = {
        text = "Delete the Loot List group \"%s\"?\nCharacters inside the group will remain and become ungrouped.",
        button1 = ACCEPT,
        button2 = CANCEL,
        timeout = 0,
        whileDead = 1,
        hideOnEscape = 1,
        preferredIndex = 3,
        OnAccept = function(_, data)
          local _, profileName = GetLootProfile(GetLootListContextProfileName())
          if not GetLootProfileAccessInfo(profileName).canEditLootList then
            if editor.lootLedgerDragHint then
              editor.lootLedgerDragHint:SetText("Raiders can view the Loot List, but cannot modify it.")
            end
            return
          end
          local groupName = type(data) == "table" and data.groupName or nil
          local profile = GetLootProfile(GetLootListContextProfileName())
          local ok, result = DeleteLootListGroup(profile, groupName)
          if ok then
            editor.selectedLootLedgerType = "character"
            editor.selectedLootLedgerKey = nil
            if editor.lootLedgerDragHint then
              editor.lootLedgerDragHint:SetText("Deleted group: " .. tostring(groupName or ""))
            end
            RefreshLootListPageView()
          elseif editor.lootLedgerDragHint then
            editor.lootLedgerDragHint:SetText(tostring(result or "Could not delete Loot List group."))
          end
        end,
      }
    end

    if not StaticPopupDialogs.LIDCOUNCIL_DELETE_LOOT_LEDGER_CHARACTER then
      StaticPopupDialogs.LIDCOUNCIL_DELETE_LOOT_LEDGER_CHARACTER = {
        text = "Are you sure you want to delete this character and points?",
        button1 = "Yes",
        button2 = "No",
        timeout = 0,
        whileDead = 1,
        hideOnEscape = 1,
        preferredIndex = 3,
        OnAccept = function()
          if RejectCombatLockedLootListAction(feedback, "Delete Loot List Characters") then
            return
          end
          local profileName = GetLootListContextProfileName()
          local characterKey = TrimText(editor.selectedLootLedgerCharacter)
          if characterKey == "" then
            feedback:SetText("Select a character first.")
            return
          end

          local requestDelete = type(ResolveOptionalApiFunction) == "function"
            and ResolveOptionalApiFunction("LIDCOUNCIL_RequestLootLedgerCharacterDeleteFlow")
            or nil
          local ok, result, flowMode
          if type(requestDelete) == "function" then
            ok, result, flowMode = requestDelete(profileName, characterKey)
          else
            local profile = GetLootProfile(profileName)
            ok, result = DeleteLootLedgerCharacter(profile, characterKey)
            flowMode = "LOCAL"
          end

          if ok then
            editor.selectedLootLedgerType = "none"
            editor.selectedLootLedgerKey = nil
            editor.selectedLootLedgerCharacter = nil
            editor.selectedLootLedgerHistoryEntryId = nil
            editor.selectedLootLedgerHistoryOwner = nil
            if flowMode == "REQUEST_SENT" then
              feedback:SetText("Character delete request sent to the session host.")
            else
              feedback:SetText("Character deleted from the Loot List.")
            end
            RefreshLootListPageView()
          else
            feedback:SetText(tostring(result or "Could not delete the character."))
          end
        end,
      }
    end
  end

  local function GetSelectedLootListAccess()
    local profileName = TrimText(GetLootListContextProfileName())
    if profileName == "" then
      return EMPTY_LOOT_LIST_ACCESS
    end
    return GetLootProfileAccessInfo(profileName)
  end

  editor.OpenLootLedgerLinkManager = function(self)
    local selectedCharacter = TrimText(self.selectedLootLedgerCharacter)
    if selectedCharacter == "" then
      if self.lootLedgerDragHint then
        self.lootLedgerDragHint:SetText("Select a character first.")
      end
      return
    end

    local profileName = GetLootListContextProfileName()
    local profile = GetLootProfile(profileName)
    if type(profile) ~= "table" then
      if self.lootLedgerDragHint then
        self.lootLedgerDragHint:SetText("The selected Loot List profile is unavailable.")
      end
      return
    end

    local function BuildLinkManagerState()
      local _, rowsByCharacter = BuildLootLedgerNavRows(profile, profileName, "")
      local metaByCharacter = EnsureLootLedgerMetaLocal(profile)
      local selectedMeta = metaByCharacter[selectedCharacter] or {}
      local attachedAltCounts = {}

      for _, meta in pairs(metaByCharacter) do
        if type(meta) == "table" and NormalizeLootLedgerRoleLocal(meta.mainAltRole) == "ALT" then
          local mainCharacter = TrimText(meta.mainCharacter)
          if mainCharacter ~= "" then
            attachedAltCounts[mainCharacter] = (attachedAltCounts[mainCharacter] or 0) + 1
          end
        end
      end

      local candidates = {}
      for characterKey, row in pairs(rowsByCharacter or {}) do
        if characterKey ~= selectedCharacter then
          local meta = metaByCharacter[characterKey] or {}
          candidates[#candidates + 1] = {
            character = characterKey,
            icon = row.specIcon,
            specText = BuildSpecLabel(row),
            role = NormalizeLootLedgerRoleLocal(meta.mainAltRole),
            mainCharacter = TrimText(meta.mainCharacter),
            attachedAltCount = attachedAltCounts[characterKey] or 0,
          }
        end
      end

      return {
        selectedCharacter = selectedCharacter,
        selectedRole = NormalizeLootLedgerRoleLocal(selectedMeta.mainAltRole),
        selectedMain = TrimText(selectedMeta.mainCharacter),
        candidates = candidates,
        canEdit = GetSelectedLootListAccess().canEditMainAlt,
      }
    end

    local function ApplyLinkChange(characterKey, role, mainCharacter)
      if RejectCombatLockedLootListAction(self.lootLedgerDragHint, "Change Character Links") then
        return false, "Character links cannot change in combat."
      end
      if not GetSelectedLootListAccess().canEditMainAlt then
        return false, "Your role cannot change character links."
      end

      local requestMainAlt = type(ResolveOptionalApiFunction) == "function"
        and ResolveOptionalApiFunction("LIDCOUNCIL_RequestLootLedgerMainAltFlow")
        or nil
      if type(requestMainAlt) == "function" then
        return requestMainAlt(profileName, characterKey, role, mainCharacter)
      end

      if NormalizeLootLedgerRoleLocal(role) == "ALT" then
        local ok, result = SetLootLedgerMainAltRole(profile, characterKey, "ALT")
        if not ok then
          return false, result
        end
        local _, rowsByCharacter = BuildLootLedgerNavRows(profile, profileName, "")
        return SetLootLedgerMainCharacter(profile, characterKey, mainCharacter, rowsByCharacter)
      end
      return ClearLootLedgerMainCharacter(profile, characterKey)
    end

    lootListDetailsPopupHelpers.OpenLootListLinkManager(self, {
      GetState = BuildLinkManagerState,
      ApplyChange = ApplyLinkChange,
      Refresh = RefreshLootListPageView,
    })
  end

  addBtn:SetScript("OnClick", function()
    if RejectCombatLockedLootListAction(feedback, "Add Manual Points") then
      return
    end
    local access = GetSelectedLootListAccess()
    if not access.canEditManualPoints then
      feedback:SetText("Raiders can view the Loot List, but cannot add manual points.")
      return
    end
    local profileName = GetLootListContextProfileName()
    local selectedType = tostring(editor.selectedLootLedgerType or "")
    if selectedType ~= "character" then
      feedback:SetText("Select a character row to add manual points.")
      return
    end
    local selectedCharacter = TrimText(editor.selectedLootLedgerCharacter)
    if selectedCharacter == "" then
      feedback:SetText("Select a character row to add manual points.")
      return
    end
    local requestManualEntry = type(ResolveOptionalApiFunction) == "function"
      and ResolveOptionalApiFunction("LIDCOUNCIL_RequestManualLootLedgerEntryFlow")
      or nil
    local ok, result, flowMode
    if type(requestManualEntry) == "function" then
      ok, result, flowMode = requestManualEntry(
        profileName,
        selectedCharacter,
        itemEdit:GetText(),
        pointsEdit:GetText(),
        ""
      )
    else
      ok, result = AddLootLedgerEntry(
        profileName,
        selectedCharacter,
        itemEdit:GetText(),
        nil,
        pointsEdit:GetText(),
        "",
        nil,
        nil,
        "Interface\\Icons\\INV_Misc_Note_06",
        nil,
        {
          grantedBy = GetCharacterKey(),
          isManual = true,
        }
      )
      flowMode = "LOCAL"
    end
    if ok then
      editor.selectedLootLedgerType = "character"
      editor.selectedLootLedgerKey = selectedCharacter
      editor.selectedLootLedgerCharacter = selectedCharacter
      itemEdit:SetText("")
      pointsEdit:SetText("")
      if flowMode == "REQUEST_SENT" then
        feedback:SetText("Manual points sent to the session host.")
      else
        feedback:SetText("Manual points added.")
      end
      RefreshLootListPageView()
    else
      feedback:SetText(tostring(result or "Could not add ledger entry."))
    end
  end)

  deleteManualBtn:SetScript("OnClick", function()
    if RejectCombatLockedLootListAction(feedback, "Delete Manual Points") then
      return
    end
    local access = GetSelectedLootListAccess()
    if not access.canEditManualPoints then
      feedback:SetText("Raiders can view the Loot List, but cannot delete manual points.")
      return
    end
    local entryId = TrimText(editor.selectedLootLedgerHistoryEntryId)
    local ownerKey = TrimText(editor.selectedLootLedgerHistoryOwner)
    if entryId == "" or ownerKey == "" then
      feedback:SetText("Select a manual history entry first.")
      return
    end
    local profileName = GetLootListContextProfileName()
    local requestManualDelete = type(ResolveOptionalApiFunction) == "function"
      and ResolveOptionalApiFunction("LIDCOUNCIL_RequestManualLootLedgerDeleteFlow")
      or nil
    local ok, result, flowMode
    if type(requestManualDelete) == "function" then
      ok, result, flowMode = requestManualDelete(profileName, ownerKey, entryId)
    else
      ok, result = RemoveLootLedgerEntry(profileName, ownerKey, entryId)
      flowMode = "LOCAL"
    end
    if ok then
      editor.selectedLootLedgerHistoryEntryId = nil
      editor.selectedLootLedgerHistoryOwner = nil
      if flowMode == "REQUEST_SENT" then
        feedback:SetText("Manual point delete request sent to the session host.")
      else
        feedback:SetText("Manual points removed.")
      end
      RefreshLootListPageView()
    else
      feedback:SetText(tostring(result or "Could not remove manual entry."))
    end
  end)

  deleteCharacterBtn:SetScript("OnClick", function()
    if RejectCombatLockedLootListAction(feedback, "Delete Loot List Characters") then
      return
    end
    local access = GetSelectedLootListAccess()
    if not access.canDeleteLootListCharacter then
      feedback:SetText("Your current role cannot delete Loot List characters.")
      return
    end
    if TrimText(editor.selectedLootLedgerCharacter) == "" or tostring(editor.selectedLootLedgerType or "") ~= "character" then
      feedback:SetText("Select a character first.")
      return
    end
    StaticPopup_Show("LIDCOUNCIL_DELETE_LOOT_LEDGER_CHARACTER")
  end)

  searchEdit:SetScript("OnTextChanged", function(self)
    editor.__lootLedgerSearchText = TrimText(self:GetText())
    RefreshLootListPageView()
  end)

  local function SetLootListViewMode(mode)
    local root = RootDB()
    root.ui = root.ui or {}
    root.ui.lootLedgerViewMode = mode == "DETAILS" and "DETAILS" or "LIST_ONLY"
    UpdateLootListModeButtons()
    RefreshLootListPageView()
  end

  if viewModeDropdown and type(viewModeDropdown.SetMenuBuilder) == "function" then
    viewModeDropdown:SetMenuBuilder(function()
      local mode = GetLootListViewMode()
      return {
        width = 154,
        entries = {
          {
            text = "Pop Out",
            value = "LIST_ONLY",
            checked = mode == "LIST_ONLY",
            func = function()
              SetLootListViewMode("LIST_ONLY")
            end,
          },
          {
            text = "Split",
            value = "DETAILS",
            checked = mode == "DETAILS",
            func = function()
              SetLootListViewMode("DETAILS")
            end,
          },
        },
      }
    end)
  elseif viewModeDropdown then
    viewModeDropdown:SetScript("OnClick", function()
      SetLootListViewMode(GetLootListViewMode() == "DETAILS" and "LIST_ONLY" or "DETAILS")
    end)
  end

  addGroupBtn:SetScript("OnClick", nil)
  deleteGroupBtn:SetScript("OnClick", nil)
  navBg:SetScript("OnMouseUp", nil)
  charContent:SetScript("OnReceiveDrag", nil)

  seedTestBtn:SetScript("OnClick", function()
    if RejectCombatLockedLootListAction(editor.lootLedgerDragHint, "Seed Test Raiders") then
      return
    end
    local access = GetSelectedLootListAccess()
    if not access.canEditLootList then
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText("Raiders can view the Loot List, but cannot modify it.")
      end
      return
    end
    local profile, profileName = GetLootProfile(GetLootListContextProfileName())
    if TrimText(profileName) ~= "Simulation" then
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText("Seed Test Raiders is only available for the Simulation profile.")
      end
      return
    end
    local ok, result = SeedLootLedgerTestRoster(profile, profileName)
    if editor.lootLedgerDragHint then
      editor.lootLedgerDragHint:SetText(tostring(result or (ok and "Test raiders added." or "Could not seed test raiders.")))
    end
    if ok then
      RefreshLootListPageView()
    end
  end)

  manageLinksBtn:SetScript("OnClick", function()
    if editor.OpenLootLedgerLinkManager then
      editor:OpenLootLedgerLinkManager()
    end
  end)

  historyPrevBtn:SetScript("OnClick", function()
    editor.__lootLedgerHistoryPage = math.max(1, math.floor(tonumber(editor.__lootLedgerHistoryPage) or 1) - 1)
    RefreshLootListPageView()
  end)

  historyNextBtn:SetScript("OnClick", function()
    editor.__lootLedgerHistoryPage = math.floor(tonumber(editor.__lootLedgerHistoryPage) or 1) + 1
    RefreshLootListPageView()
  end)

  manualToggleBtn:SetScript("OnClick", function()
    local selectedCharacter = TrimText(editor.selectedLootLedgerCharacter)
    if selectedCharacter == "" then
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText("Select a character first.")
      end
      return
    end
    if editor.__lootLedgerManualAdjustmentCharacter == selectedCharacter then
      editor.__lootLedgerManualAdjustmentCharacter = nil
    else
      editor.__lootLedgerManualAdjustmentCharacter = selectedCharacter
    end
    RefreshLootListPageView()
  end)

  linkSummaryBtn:SetScript("OnClick", function()
    if RejectCombatLockedLootListAction(editor.lootLedgerDragHint, "Change Main / Alt Links") then
      return
    end
    local access = GetSelectedLootListAccess()
    if not access.canEditMainAlt then
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText("Raiders can view Main and Alt links, but cannot change them.")
      end
      return
    end
    local profileName = GetLootListContextProfileName()
    local profile = GetLootProfile(profileName)
    local selectedCharacter = TrimText(editor.selectedLootLedgerCharacter)
    if selectedCharacter == "" then
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText("Select a character first.")
      end
      return
    end

    local selectedRole = "MAIN"
    if roleDropdown and roleDropdown.GetSelectedValue then
      selectedRole = NormalizeLootLedgerRoleLocal(roleDropdown:GetSelectedValue())
    elseif roleDropdown and roleDropdown.GetText then
      selectedRole = NormalizeLootLedgerRoleLocal(roleDropdown:GetText())
    end

    local targetCharacter = ""
    if summaryDropdown and summaryDropdown.GetSelectedValue then
      targetCharacter = TrimText(summaryDropdown:GetSelectedValue())
    elseif summaryDropdown and summaryDropdown.GetText then
      targetCharacter = TrimText(summaryDropdown:GetText())
    end

    local requestMainAlt = type(ResolveOptionalApiFunction) == "function"
      and ResolveOptionalApiFunction("LIDCOUNCIL_RequestLootLedgerMainAltFlow")
      or nil
    local ok, result, flowMode
    if type(requestMainAlt) == "function" then
      ok, result, flowMode = requestMainAlt(profileName, selectedCharacter, selectedRole, selectedRole == "ALT" and targetCharacter or "")
    elseif selectedRole == "ALT" then
      ok, result = SetLootLedgerMainAltRole(profile, selectedCharacter, selectedRole)
      if ok then
        local _, allCharactersByKey = BuildLootLedgerNavRows(profile, profileName, "")
        ok, result = SetLootLedgerMainCharacter(profile, selectedCharacter, targetCharacter, allCharactersByKey)
      end
      flowMode = "LOCAL"
    else
      ok, result = ClearLootLedgerMainCharacter(profile, selectedCharacter)
      flowMode = "LOCAL"
    end

    if not ok then
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText(tostring(result or "Could not update the Main / Alt link."))
      end
      return
    end

    if flowMode == "REQUEST_SENT" then
      editor.__lootLedgerPendingRole = nil
      editor.__lootLedgerPendingRoleCharacter = nil
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText("Main / Alt update sent to the session host.")
      end
      RefreshLootListPageView()
      return
    end

    if selectedRole == "ALT" then
      editor.__lootLedgerPendingRole = nil
      editor.__lootLedgerPendingRoleCharacter = nil
      if editor.lootLedgerDragHint then
        if TrimText(result) ~= "" then
          editor.lootLedgerDragHint:SetText(tostring(selectedCharacter) .. " is now an Alt assigned to " .. tostring(result) .. ".")
        else
          editor.lootLedgerDragHint:SetText(tostring(selectedCharacter) .. " is now an Alt without a Main assigned yet.")
        end
      end
      RefreshLootListPageView()
      return
    end

    editor.__lootLedgerPendingRole = nil
    editor.__lootLedgerPendingRoleCharacter = nil
    if editor.lootLedgerDragHint then
      editor.lootLedgerDragHint:SetText(tostring(selectedCharacter) .. " is now a Main.")
    end
    RefreshLootListPageView()
  end)

  clearSummaryBtn:SetScript("OnClick", function()
    if RejectCombatLockedLootListAction(editor.lootLedgerDragHint, "Change Main / Alt Links") then
      return
    end
    local access = GetSelectedLootListAccess()
    if not access.canEditMainAlt then
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText("Raiders can view Main and Alt links, but cannot change them.")
      end
      return
    end
    local profileName = GetLootListContextProfileName()
    local profile = GetLootProfile(profileName)
    local selectedCharacter = TrimText(editor.selectedLootLedgerCharacter)
    local requestMainAlt = type(ResolveOptionalApiFunction) == "function"
      and ResolveOptionalApiFunction("LIDCOUNCIL_RequestLootLedgerMainAltFlow")
      or nil
    local ok, result, flowMode
    if type(requestMainAlt) == "function" then
      ok, result, flowMode = requestMainAlt(profileName, selectedCharacter, "MAIN", "")
    else
      ok, result = ClearLootLedgerMainCharacter(profile, selectedCharacter)
      flowMode = "LOCAL"
    end
    if ok then
      editor.__lootLedgerPendingRole = nil
      editor.__lootLedgerPendingRoleCharacter = nil
      if editor.lootLedgerDragHint then
        if flowMode == "REQUEST_SENT" then
          editor.lootLedgerDragHint:SetText("Main / Alt update sent to the session host.")
        else
          editor.lootLedgerDragHint:SetText(tostring(selectedCharacter) .. " is now a Main.")
        end
      end
      RefreshLootListPageView()
    elseif editor.lootLedgerDragHint then
      editor.lootLedgerDragHint:SetText(tostring(result or "Could not set the character as Main."))
    end
  end)

  local function CommitLootLedgerCharacterNote()
    if RejectCombatLockedLootListAction(editor.lootLedgerDragHint, "Edit Character Notes") then
      return
    end
    local access = GetSelectedLootListAccess()
    if not access.canEditNotes then
      if editor.lootLedgerDragHint then
        editor.lootLedgerDragHint:SetText("Raiders can view notes, but cannot edit them.")
      end
      return
    end
    local profile = GetLootProfile(GetLootListContextProfileName())
    local selectedCharacter = TrimText(editor.selectedLootLedgerCharacter)
    if selectedCharacter == "" or not editor.lootLedgerCharacterNoteEdit then
      return
    end

    local currentNote = TrimText(editor.lootLedgerCharacterNoteEdit:GetText())
    local meta = EnsureLootLedgerMetaLocal(profile)[selectedCharacter]
    local storedNote = TrimText(type(meta) == "table" and meta.note or "")
    if currentNote == storedNote then
      return
    end

    local requestNote = type(ResolveOptionalApiFunction) == "function"
      and ResolveOptionalApiFunction("LIDCOUNCIL_RequestLootLedgerCharacterNoteFlow")
      or nil
    local ok, result, flowMode
    if type(requestNote) == "function" then
      ok, result, flowMode = requestNote(GetLootListContextProfileName(), selectedCharacter, currentNote)
    else
      ok, result = SetLootLedgerCharacterNote(profile, selectedCharacter, currentNote)
      flowMode = "LOCAL"
    end
    if editor.lootLedgerDragHint then
      if ok then
        if flowMode == "REQUEST_SENT" then
          editor.lootLedgerDragHint:SetText("Character note update sent to the session host.")
        elseif currentNote ~= "" then
          editor.lootLedgerDragHint:SetText("Updated note for " .. tostring(selectedCharacter) .. ".")
        else
          editor.lootLedgerDragHint:SetText("Cleared note for " .. tostring(selectedCharacter) .. ".")
        end
      else
        editor.lootLedgerDragHint:SetText(tostring(result or "Could not update the character note."))
      end
    end
    if ok then
      editor.__lootLedgerNoteEditingCharacter = nil
      RefreshLootListPageView()
    end
  end

  if editor.lootLedgerCharacterNoteEdit then
    editor.lootLedgerCharacterNoteEdit:SetScript("OnEnterPressed", function(self)
      CommitLootLedgerCharacterNote()
      self:ClearFocus()
    end)
    editor.lootLedgerCharacterNoteEdit:SetScript("OnEscapePressed", function(self)
      local profile = GetLootProfile(GetLootListContextProfileName())
      local selectedCharacter = TrimText(editor.selectedLootLedgerCharacter)
      local meta = selectedCharacter ~= "" and EnsureLootLedgerMetaLocal(profile)[selectedCharacter] or nil
      self:SetText(type(meta) == "table" and TrimText(meta.note) or "")
      self:ClearFocus()
      editor.__lootLedgerNoteEditingCharacter = nil
      RefreshLootListPageView()
    end)
  end

  if editor.lootLedgerCharacterNoteToggleBtn then
    editor.lootLedgerCharacterNoteToggleBtn:SetScript("OnClick", function()
      local selectedCharacter = TrimText(editor.selectedLootLedgerCharacter)
      if selectedCharacter == "" then
        return
      end
      editor.__lootLedgerNoteEditingCharacter = selectedCharacter
      RefreshLootListPageView()
      if editor.lootLedgerCharacterNoteEdit and editor.lootLedgerCharacterNoteEdit.SetFocus then
        editor.lootLedgerCharacterNoteEdit:SetFocus()
      end
    end)
  end
  if editor.lootLedgerCharacterNoteSaveBtn then
    editor.lootLedgerCharacterNoteSaveBtn:SetScript("OnClick", function()
      CommitLootLedgerCharacterNote()
    end)
  end
  if editor.lootLedgerCharacterNoteCancelBtn then
    editor.lootLedgerCharacterNoteCancelBtn:SetScript("OnClick", function()
      local profile = GetLootProfile(GetLootListContextProfileName())
      local selectedCharacter = TrimText(editor.selectedLootLedgerCharacter)
      local meta = selectedCharacter ~= "" and EnsureLootLedgerMetaLocal(profile)[selectedCharacter] or nil
      if editor.lootLedgerCharacterNoteEdit then
        editor.lootLedgerCharacterNoteEdit:SetText(type(meta) == "table" and TrimText(meta.note) or "")
        editor.lootLedgerCharacterNoteEdit:ClearFocus()
      end
      editor.__lootLedgerNoteEditingCharacter = nil
      RefreshLootListPageView()
    end)
  end

  editor.RefreshLootListPage = function(self)
    EnsureLootListStaticPopups()
    local hasActiveProfile = TrimText(GetLootListContextProfileName()) ~= ""
    local contentMode = GetLootListContentMode()
    local showLootList = hasActiveProfile and contentMode == "LOOT_LIST"
    local showItemHistory = hasActiveProfile and contentMode == "ITEM_HISTORY"
    local showAwards = hasActiveProfile and contentMode == "FUN_AWARDS"
    if self.lootListInactiveOverlay then
      self.lootListInactiveOverlay:SetShown(not hasActiveProfile)
    end
    if self.backgroundsTitle then
      self.backgroundsTitle:SetText(hasActiveProfile and "Loot List" or "Profile")
    end
    if self.lootListNavBg then
      self.lootListNavBg:SetShown(showLootList)
    end
    if self.lootListRightBg then
      self.lootListRightBg:SetShown(showLootList)
    end
    if self.lootLedgerItemHistoryView then
      self.lootLedgerItemHistoryView:SetShown(showItemHistory)
    end
    if self.lootLedgerAwardsHost then
      self.lootLedgerAwardsHost:SetShown(showAwards)
    end
    if self.lootLedgerAwardsView then
      self.lootLedgerAwardsView:SetShown(showAwards)
    end
    if not hasActiveProfile then
      self.__lootLedgerItemHistoryVisible = false
      local root = RootDB()
      root.ui = root.ui or {}
      root.ui.lootLedgerContentMode = "LOOT_LIST"
      SetCurrentProfileFont(self.backgroundsCurrentLabel, NO_ACTIVE_PROFILE_CURRENT_TEXT, NO_ACTIVE_PROFILE_TEXT_COLOR)
      if self.UpdateScrollLayout then
        self:UpdateScrollLayout(true)
      end
      return
    end
    local profileName = TrimText(GetLootListContextProfileName())
    SetCurrentProfileFont(self.backgroundsCurrentLabel, "Current Profile: " .. profileName, nil)
    UpdateLootListModeButtons()
    if showItemHistory then
      local summary = self.lootLedgerItemHistoryView and self.lootLedgerItemHistoryView.__summary or nil
      if summary then
        summary:SetText("Items, selections, votes, assignments, and trade status from saved sessions.")
        summary:SetTextColor(0.92, 0.96, 1.00, 1.00)
      end
      local attachHistory = type(ResolveOptionalApiFunction) == "function"
        and ResolveOptionalApiFunction("LIDCOUNCIL_AttachLiveLootBoardHistory")
        or nil
      local attached, attachError = false, nil
      if type(attachHistory) == "function" and self.lootLedgerItemHistoryHolder then
        attached, attachError = pcall(attachHistory, self.lootLedgerItemHistoryHolder, profileName)
      end
      if not attached and self.lootLedgerItemHistoryView then
        if summary then
          summary:SetText("Item History is unavailable: " .. tostring(attachError or "history viewer did not load."))
          summary:SetTextColor(1.00, 0.45, 0.32, 1.00)
        end
      end
      return true
    end
    if showAwards then
      local awardsHost = self.lootLedgerAwardsHost
      local width = awardsHost and awardsHost.GetWidth and awardsHost:GetWidth() - 46 or 540
      lootListAwardsViewHelpers.RefreshLootListAwardsView(self, profileName, math.max(360, width), awardsHost)
      return true
    end
    local result = RefreshLootListPageImpl(self, page)
    local inCombat = IsCombatLocked()
    local mutableButtons = {
      self.lootLedgerSeedTestBtn,
      self.lootLedgerSummaryLinkBtn,
      self.lootLedgerSummaryClearBtn,
      self.lootLedgerCharacterNoteToggleBtn,
      self.lootLedgerCharacterNoteSaveBtn,
      self.lootLedgerAddEntryBtn,
      self.lootLedgerDeleteEntryBtn,
      self.lootLedgerDeleteCharacterBtn,
    }
    for _, control in ipairs(mutableButtons) do
      if control and control.Enable then
        if inCombat then
          control:Disable()
        end
      end
    end
    if self.lootLedgerCharacterNoteEdit then
      if inCombat and self.lootLedgerCharacterNoteEdit.Disable then
        self.lootLedgerCharacterNoteEdit:Disable()
      end
    end
    return result
  end

  editor.RefreshLootListPage = (function(refreshPage)
    return function(self)
      local pageStartedAt = INTERNAL_API.LIDCOUNCIL_BeginSyncPerformanceSample_Impl("editor_page_loot_list", 1)
      local result = refreshPage(self)
      INTERNAL_API.LIDCOUNCIL_RecordSyncPerformance_Impl("editor_page_loot_list", pageStartedAt, {
        context = tostring(self and (self.__lootListOpenSection or self.lootLedgerViewMode) or "characters"),
      })
      return result
    end
  end)(editor.RefreshLootListPage)
end

INTERNAL_API.LIDCOUNCIL_BuildLootListPage_Impl = BuildLootListPage

