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_CreateLootListDetailsHelpers_Impl = function(deps)
  deps = type(deps) == "table" and deps or {}

  local ApplyFrameSkin = deps.ApplyFrameSkin
  local TrimText = deps.TrimText
  local FormatDateTime = deps.FormatDateTime
  local ClampLayoutValue = deps.ClampLayoutValue
  local SetControlWidth = deps.SetControlWidth
  local GetFontStringWidth = deps.GetFontStringWidth
  local FlowLayoutControls = deps.FlowLayoutControls

  local function EnsureLootLedgerHistoryRows(rows, parent, count)
    rows = rows or {}
    while #rows < count do
      local row = CreateFrame("Frame", nil, parent)
      row:SetSize(552, 24)
      row:EnableMouse(true)

      local bg = row:CreateTexture(nil, "BACKGROUND")
      bg:SetAllPoints(true)
      bg:SetTexture("Interface\\Buttons\\WHITE8x8")
      bg:SetVertexColor(0.08, 0.05, 0.06, 0.72)
      row.__bg = bg

      local icon = row:CreateTexture(nil, "ARTWORK")
      icon:SetSize(18, 18)
      icon:SetPoint("LEFT", 4, 0)
      icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
      row.__icon = icon

      local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      nameText:SetPoint("LEFT", icon, "RIGHT", 8, 0)
      nameText:SetWidth(286)
      nameText:SetJustifyH("LEFT")
      row.__nameText = nameText

      local receivedText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      receivedText:SetPoint("LEFT", 300, 0)
      receivedText:SetWidth(160)
      receivedText:SetJustifyH("LEFT")
      row.__receivedText = receivedText

      local pointsText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      pointsText:SetPoint("LEFT", 468, 0)
      pointsText:SetWidth(60)
      pointsText:SetJustifyH("LEFT")
      row.__pointsText = pointsText

      rows[#rows + 1] = row
    end
    return rows
  end

  local function EnsureLootLedgerMemberRows(rows, parent, count)
    rows = rows or {}
    while #rows < count do
      local row = CreateFrame("Frame", nil, parent, "BackdropTemplate")
      row:SetSize(552, 52)
      ApplyFrameSkin(row)

      local bg = row:CreateTexture(nil, "BACKGROUND")
      bg:SetAllPoints(true)
      bg:SetTexture("Interface\\Buttons\\WHITE8x8")
      bg:SetVertexColor(0.08, 0.05, 0.06, 0.42)
      row.__bg = bg

      local accent = row:CreateTexture(nil, "BORDER")
      accent:SetTexture("Interface\\Buttons\\WHITE8x8")
      accent:SetPoint("TOPLEFT", 1, -1)
      accent:SetPoint("BOTTOMLEFT", 1, 1)
      accent:SetWidth(4)
      row.__accent = accent

      local icon = row:CreateTexture(nil, "ARTWORK")
      icon:SetSize(20, 20)
      icon:SetPoint("TOPLEFT", 12, -10)
      icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
      row.__icon = icon

      local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
      nameText:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, 0)
      nameText:SetJustifyH("LEFT")
      if nameText.SetMaxLines then
        nameText:SetMaxLines(1)
      end
      row.__nameText = nameText

      local roleText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      roleText:SetPoint("TOPRIGHT", -10, -10)
      roleText:SetJustifyH("RIGHT")
      if roleText.SetMaxLines then
        roleText:SetMaxLines(1)
      end
      row.__roleText = roleText

      local specText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      specText:SetPoint("TOPLEFT", nameText, "BOTTOMLEFT", 0, -4)
      specText:SetJustifyH("LEFT")
      if specText.SetMaxLines then
        specText:SetMaxLines(1)
      end
      row.__specText = specText

      local statsText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      statsText:SetPoint("TOPLEFT", specText, "BOTTOMLEFT", 0, -4)
      statsText:SetPoint("RIGHT", -10, 0)
      statsText:SetJustifyH("LEFT")
      if statsText.SetMaxLines then
        statsText:SetMaxLines(1)
      end
      row.__statsText = statsText

      rows[#rows + 1] = row
    end
    return rows
  end

  local function ResolveLootLedgerEntryIcon(item)
    if type(item) ~= "table" then
      return "Interface\\Icons\\INV_Misc_QuestionMark"
    end
    if item.itemIcon then
      return item.itemIcon
    end
    local infoSource = item.itemLink or item.itemID
    if infoSource and type(GetItemInfo) == "function" then
      local _, _, _, _, _, _, _, _, _, itemIcon = GetItemInfo(infoSource)
      if itemIcon then
        return itemIcon
      end
    end
    return "Interface\\Icons\\INV_Misc_QuestionMark"
  end

  local function NormalizeLootLedgerRollColor(color)
    if type(color) ~= "table" then
      return nil
    end
    local r = tonumber(color[1])
    local g = tonumber(color[2])
    local b = tonumber(color[3])
    if not r or not g or not b then
      return nil
    end
    return { r, g, b }
  end

  local function ResolveLootLedgerRollOption(profile, item)
    local itemTable = type(item) == "table" and item or {}
    local optionKey = string.lower(TrimText(itemTable.rollOptionKey))
    local optionLabel = string.lower(TrimText(itemTable.rollOptionLabel))
    local resolvedLabel = TrimText(itemTable.rollOptionLabel)
    if resolvedLabel == "" then
      resolvedLabel = TrimText(itemTable.rollOptionKey)
    end
    local resolvedColor = NormalizeLootLedgerRollColor(itemTable.rollOptionColor)
    local options = type(profile) == "table" and profile.settings and profile.settings.rollOptions or nil
    for _, option in ipairs(type(options) == "table" and options or {}) do
      if type(option) == "table" then
        local candidateKey = string.lower(TrimText(option.key))
        local candidateLabel = string.lower(TrimText(option.label))
        if (optionKey ~= "" and optionKey == candidateKey) or (optionLabel ~= "" and optionLabel == candidateLabel) then
          local label = TrimText(option.label)
          if label ~= "" then
            resolvedLabel = label
          end
          resolvedColor = NormalizeLootLedgerRollColor(option.color) or resolvedColor
          break
        end
      end
    end
    return resolvedLabel, resolvedColor
  end

  local function BuildLootLedgerHistoryEntries(profile, selectedRow)
    local entries = {}
    if not selectedRow or selectedRow.type ~= "character" then
      return entries
    end

    local members = type(selectedRow.summaryMembers) == "table" and selectedRow.summaryMembers or { selectedRow.character }
    for _, memberKey in ipairs(members) do
      local ledger = profile.lootLedger and profile.lootLedger[memberKey] or nil
      for _, item in ipairs(ledger and ledger.items or {}) do
        local rollOptionLabel, rollOptionColor = ResolveLootLedgerRollOption(profile, item)
        entries[#entries + 1] = {
          owner = memberKey,
          entryId = item.entryId,
          itemName = item.itemName,
          itemLevel = item.itemLevel,
          points = item.points,
          note = item.note,
          awardedAt = item.awardedAt,
          itemLink = item.itemLink,
          itemID = item.itemID,
          itemIcon = item.itemIcon,
          itemQuality = item.itemQuality,
          pointBehavior = item.pointBehavior,
          rollOptionKey = item.rollOptionKey,
          rollOptionLabel = rollOptionLabel,
          rollOptionColor = rollOptionColor,
          rollValue = item.rollValue,
          grantedBy = item.grantedBy,
          isManual = item.isManual == true,
        }
      end
    end

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

  local function NormalizeLootLedgerPointBehavior(value)
    local text = string.upper(TrimText(value))
    if text == "BIS" or text == "OFFSPEC" or text == "TRANSMOG" or text == "DISENCHANT" or text == "NONE" or text == "ITEMLEVEL" then
      return text
    end
    return ""
  end

  local function FormatLootLedgerPointBehaviorLabel(value)
    local resolved = NormalizeLootLedgerPointBehavior(value)
    if resolved == "BIS" then
      return "BiS"
    end
    if resolved == "OFFSPEC" then
      return "Off-Spec"
    end
    if resolved == "TRANSMOG" then
      return "Transmog"
    end
    if resolved == "DISENCHANT" then
      return "Disenchant"
    end
    if resolved == "NONE" then
      return "No Points"
    end
    return ""
  end

  local function BuildLootLedgerAwardInsight(profile, selectedRow)
    local counters = {
      bis = 0,
      offSpec = 0,
      transmog = 0,
      disenchant = 0,
    }
    local bisItems = {}
    if type(profile) ~= "table" or type(selectedRow) ~= "table" or selectedRow.type ~= "character" then
      return counters, bisItems
    end

    local members = type(selectedRow.summaryMembers) == "table" and selectedRow.summaryMembers or { selectedRow.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 behavior = NormalizeLootLedgerPointBehavior(type(item) == "table" and item.pointBehavior or "")
        if behavior == "BIS" then
          counters.bis = counters.bis + 1
          local itemLabel = TrimText(type(item) == "table" and (item.itemLink or item.itemName) or "")
          if itemLabel ~= "" then
            bisItems[#bisItems + 1] = itemLabel
          end
        elseif behavior == "OFFSPEC" then
          counters.offSpec = counters.offSpec + 1
        elseif behavior == "TRANSMOG" then
          counters.transmog = counters.transmog + 1
        elseif behavior == "DISENCHANT" then
          counters.disenchant = counters.disenchant + 1
        end
      end
    end

    return counters, bisItems
  end

  local function BuildLootLedgerNeedDiceInsight(profile, selectedRow)
    local rolls = {}
    if type(profile) ~= "table" or type(selectedRow) ~= "table" or selectedRow.type ~= "character" then
      return {
        count = 0,
        highest = 0,
        average = 0,
        recent = {},
      }
    end

    local members = type(selectedRow.summaryMembers) == "table" and selectedRow.summaryMembers or { selectedRow.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 = rollValue,
            awardedAt = tonumber(type(item) == "table" and item.awardedAt or 0) or 0,
          }
        end
      end
    end

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

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

    return {
      count = #rolls,
      highest = highest,
      average = #rolls > 0 and (total / #rolls) or 0,
      recent = recent,
    }
  end

  local function BuildNeedDiceSummaryText(diceInsight)
    if type(diceInsight) ~= "table" or tonumber(diceInsight.count) == 0 then
      return "Need Dice: None yet"
    end

    local recentParts = {}
    for _, value in ipairs(diceInsight.recent or {}) do
      recentParts[#recentParts + 1] = tostring(value)
    end
    return string.format(
      "Need Dice: %d roll(s), avg %.1f, high %d, recent [%s]",
      tonumber(diceInsight.count) or 0,
      tonumber(diceInsight.average) or 0,
      tonumber(diceInsight.highest) or 0,
      #recentParts > 0 and table.concat(recentParts, ", ") or "-"
    )
  end

  local function BuildBisItemSummaryText(items)
    if type(items) ~= "table" or #items == 0 then
      return "None"
    end
    if #items <= 4 then
      return table.concat(items, ", ")
    end
    local preview = {}
    for index = 1, 4 do
      preview[#preview + 1] = tostring(items[index])
    end
    return table.concat(preview, ", ") .. string.format(" +%d more", #items - 4)
  end

  local function ApplyLootLedgerTextureIcon(texture, iconData)
    if not texture then
      return
    end

    local resolvedTexture = type(iconData) == "table" and iconData.texture or nil
    if not resolvedTexture or resolvedTexture == "" then
      texture:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
      texture:SetTexCoord(0, 1, 0, 1)
      return
    end

    texture:SetTexture(resolvedTexture)
    if type(iconData.left) == "number" and type(iconData.right) == "number" and type(iconData.top) == "number" and type(iconData.bottom) == "number" then
      texture:SetTexCoord(iconData.left, iconData.right, iconData.top, iconData.bottom)
    else
      texture:SetTexCoord(0, 1, 0, 1)
    end
  end

  local function UpdateLootLedgerDetailLayout(self)
    if not self or not self.lootLedgerRightContent then
      return
    end

    local contentWidth = math.max(540, math.floor((self.lootListRightBg and self.lootListRightBg:GetWidth() or 620) - 40))
    local rightWidth = math.max(320, contentWidth - 18)
    local heroWidth = rightWidth - 8
    if self.lootLedgerSummaryText then
      self.lootLedgerSummaryText:SetWidth(heroWidth - 70)
    end
    if self.lootLedgerMembersText then
      self.lootLedgerMembersText:SetWidth(rightWidth)
    end
    if self.lootLedgerHistoryText then
      self.lootLedgerHistoryText:SetWidth(rightWidth)
    end
    local summaryHeight = math.max(34, math.ceil((self.lootLedgerSummaryText and self.lootLedgerSummaryText:GetStringHeight() or 0)))
    local membersInfoHeight = math.max(0, math.ceil((self.lootLedgerMembersText and self.lootLedgerMembersText:IsShown() and self.lootLedgerMembersText:GetStringHeight() or 0)))
    local membersRowsHeight = math.max(0, tonumber(self.lootLedgerMembersRowsHeight) or 0)
    local historyInfoHeight = math.max(24, math.ceil((self.lootLedgerHistoryText and self.lootLedgerHistoryText:GetStringHeight() or 0)))
    local historyRowsHeight = math.max(1, tonumber(self.lootLedgerHistoryRowsHeight) or 1)
    local heroHeight = math.max(88, 62 + summaryHeight)
    local roleLabelWidth = GetFontStringWidth(self.lootLedgerRoleLabel, 32)
    local noteLabelWidth = GetFontStringWidth(self.lootLedgerCharacterNoteLabel, 32)
    local mainLabelWidth = GetFontStringWidth(self.lootLedgerMainLabel, 36)
    local roleDropdownWidth = 120
    local noteEditWidth = math.max(180, rightWidth - noteLabelWidth - 8)
    local mainDropdownWidth = math.max(170, rightWidth - mainLabelWidth - 8)
    local fieldGap = 10
    local pointsEditWidth = 82
    local descriptionWidth = math.max(140, rightWidth - pointsEditWidth - fieldGap)
    local showMainAssignment = self.__lootLedgerShowMainAssignment == true
    local roleRowTop
    local roleControlsHeight
    local noteRowTop
    local noteControlsHeight
    local mainRowTop
    local mainControlsHeight
    local membersTop
    local membersBlockHeight

    if self.lootLedgerHeroFrame then
      self.lootLedgerHeroFrame:SetWidth(heroWidth)
      self.lootLedgerHeroFrame:SetHeight(heroHeight)
    end
    if self.lootLedgerSelectedLabel then
      self.lootLedgerSelectedLabel:SetWidth(heroWidth - 70)
    end
    if self.lootLedgerTotalLabel then
      self.lootLedgerTotalLabel:SetWidth(heroWidth - 70)
    end
    if self.lootLedgerSummaryText then
      self.lootLedgerSummaryText:SetWidth(heroWidth - 70)
    end

    local function LayoutDivider(divider, top)
      if not divider then
        return
      end
      divider:ClearAllPoints()
      divider:SetPoint("TOPLEFT", 18, top)
      divider:SetSize(rightWidth, 1)
      divider:Show()
    end

    local primaryCardWidth = math.floor((heroWidth - 24) / 4)
    local secondaryCardWidth = math.floor((heroWidth - 24) / 4)
    local primaryCardTop = -46 - heroHeight - 10
    local secondaryCardTop = primaryCardTop - 62
    local cardIndex = 0
    for _, key in ipairs({ "sessions", "items", "points", "total" }) do
      local card = self.lootLedgerStatCards and self.lootLedgerStatCards[key] or nil
      if card and card.frame then
        cardIndex = cardIndex + 1
        card.frame:ClearAllPoints()
        card.frame:SetPoint("TOPLEFT", 18 + ((cardIndex - 1) * (primaryCardWidth + 8)), primaryCardTop)
        card.frame:SetSize(primaryCardWidth, 54)
      end
    end
    cardIndex = 0
    for _, key in ipairs({ "bis", "offspec", "transmog", "disenchant" }) do
      local card = self.lootLedgerStatCards and self.lootLedgerStatCards[key] or nil
      if card and card.frame then
        cardIndex = cardIndex + 1
        card.frame:ClearAllPoints()
        card.frame:SetPoint("TOPLEFT", 18 + ((cardIndex - 1) * (secondaryCardWidth + 8)), secondaryCardTop)
        card.frame:SetSize(secondaryCardWidth, 54)
      end
    end

    local summaryHeaderTop = secondaryCardTop - 74
    LayoutDivider(self.lootLedgerMembersDivider, summaryHeaderTop + 12)
    self.lootLedgerMembersHeader:ClearAllPoints()
    self.lootLedgerMembersHeader:SetPoint("TOPLEFT", 18, summaryHeaderTop)

    roleRowTop = summaryHeaderTop - 28
    if self.lootLedgerRoleLabel then
      self.lootLedgerRoleLabel:ClearAllPoints()
      self.lootLedgerRoleLabel:SetPoint("TOPLEFT", 18, roleRowTop)
    end
    if self.lootLedgerRoleDropdown then
      SetControlWidth(self.lootLedgerRoleDropdown, roleDropdownWidth)
    end
    if self.lootLedgerMainLabel then
      self.lootLedgerMainLabel:SetShown(showMainAssignment)
    end
    if self.lootLedgerSummaryDropdown then
      self.lootLedgerSummaryDropdown:SetShown(showMainAssignment)
    end
    if self.lootLedgerSummaryClearBtn then
      self.lootLedgerSummaryClearBtn:SetShown(showMainAssignment)
    end
    roleControlsHeight = FlowLayoutControls({
      self.lootLedgerRoleDropdown,
      self.lootLedgerSummaryLinkBtn,
      self.lootLedgerSummaryClearBtn,
    }, 18 + roleLabelWidth + 8, roleRowTop, math.max(180, rightWidth - roleLabelWidth - 8), 10, 8)

    noteRowTop = roleRowTop - roleControlsHeight - 10
    if self.lootLedgerCharacterNoteLabel then
      self.lootLedgerCharacterNoteLabel:ClearAllPoints()
      self.lootLedgerCharacterNoteLabel:SetPoint("TOPLEFT", 18, noteRowTop)
    end
    if self.lootLedgerCharacterNoteEdit then
      SetControlWidth(self.lootLedgerCharacterNoteEdit, noteEditWidth)
    end
    noteControlsHeight = FlowLayoutControls({
      self.lootLedgerCharacterNoteEdit,
    }, 18 + noteLabelWidth + 8, noteRowTop, math.max(180, rightWidth - noteLabelWidth - 8), 10, 8)

    if showMainAssignment then
      mainRowTop = noteRowTop - noteControlsHeight - 10
      if self.lootLedgerMainLabel then
        self.lootLedgerMainLabel:ClearAllPoints()
        self.lootLedgerMainLabel:SetPoint("TOPLEFT", 18, mainRowTop)
      end
      if self.lootLedgerSummaryDropdown then
        SetControlWidth(self.lootLedgerSummaryDropdown, mainDropdownWidth)
      end
      mainControlsHeight = FlowLayoutControls({
        self.lootLedgerSummaryDropdown,
      }, 18 + mainLabelWidth + 8, mainRowTop, math.max(180, rightWidth - mainLabelWidth - 8), 10, 8)
      membersTop = mainRowTop - mainControlsHeight - 10
    else
      mainRowTop = noteRowTop - noteControlsHeight
      mainControlsHeight = 0
      membersTop = noteRowTop - noteControlsHeight - 10
    end

    if self.lootLedgerMembersRowsFrame then
      self.lootLedgerMembersRowsFrame:ClearAllPoints()
      self.lootLedgerMembersRowsFrame:SetPoint("TOPLEFT", 18, membersTop)
      self.lootLedgerMembersRowsFrame:SetSize(rightWidth, math.max(1, membersRowsHeight))
      self.lootLedgerMembersRowsFrame:SetShown(membersRowsHeight > 0)
    end

    if self.lootLedgerMembersText then
      self.lootLedgerMembersText:ClearAllPoints()
      if membersRowsHeight > 0 and self.lootLedgerMembersRowsFrame then
        self.lootLedgerMembersText:SetPoint("TOPLEFT", self.lootLedgerMembersRowsFrame, "BOTTOMLEFT", 0, -8)
      else
        self.lootLedgerMembersText:SetPoint("TOPLEFT", 18, membersTop)
      end
      self.lootLedgerMembersText:SetWidth(rightWidth)
    end

    membersBlockHeight = membersRowsHeight
    if membersInfoHeight > 0 then
      if membersBlockHeight > 0 then
        membersBlockHeight = membersBlockHeight + 8
      end
      membersBlockHeight = membersBlockHeight + membersInfoHeight
    end
    if membersBlockHeight <= 0 then
      membersBlockHeight = 20
    end

    local historyHeaderTop = membersTop - membersBlockHeight - 26
    LayoutDivider(self.lootLedgerHistoryDivider, historyHeaderTop + 12)
    self.lootLedgerHistoryHeader:ClearAllPoints()
    self.lootLedgerHistoryHeader:SetPoint("TOPLEFT", 18, historyHeaderTop)

    if self.lootLedgerHistoryColumns then
      self.lootLedgerHistoryColumns:ClearAllPoints()
      self.lootLedgerHistoryColumns:SetPoint("TOPLEFT", 18, historyHeaderTop - 24)
      self.lootLedgerHistoryColumns:SetWidth(rightWidth)
    end

    if self.lootLedgerHistoryItemHeader then
      self.lootLedgerHistoryItemHeader:SetWidth(math.max(180, rightWidth - 266))
    end
    if self.lootLedgerHistoryReceivedHeader then
      self.lootLedgerHistoryReceivedHeader:ClearAllPoints()
      self.lootLedgerHistoryReceivedHeader:SetPoint("TOPLEFT", math.max(190, rightWidth - 250), 0)
    end
    if self.lootLedgerHistoryPointsHeader then
      self.lootLedgerHistoryPointsHeader:ClearAllPoints()
      self.lootLedgerHistoryPointsHeader:SetPoint("TOPLEFT", math.max(360, rightWidth - 82), 0)
    end

    if self.lootLedgerHistoryRowsFrame then
      self.lootLedgerHistoryRowsFrame:ClearAllPoints()
      self.lootLedgerHistoryRowsFrame:SetPoint("TOPLEFT", 18, historyHeaderTop - 46)
      self.lootLedgerHistoryRowsFrame:SetSize(rightWidth, historyRowsHeight)
    end

    self.lootLedgerHistoryText:ClearAllPoints()
    self.lootLedgerHistoryText:SetPoint("TOPLEFT", self.lootLedgerHistoryRowsFrame, "BOTTOMLEFT", 0, -8)
    self.lootLedgerHistoryText:SetWidth(rightWidth)

    local addHeaderTop = historyHeaderTop - 54 - historyRowsHeight - historyInfoHeight
    LayoutDivider(self.lootLedgerManualDivider, addHeaderTop + 12)
    self.lootLedgerHeader:ClearAllPoints()
    self.lootLedgerHeader:SetPoint("TOPLEFT", 18, addHeaderTop)

    local firstLabelTop = addHeaderTop - 24
    local firstInputTop = firstLabelTop - 18
    descriptionWidth = math.max(140, rightWidth - pointsEditWidth - fieldGap)
    local pointsX = 18 + descriptionWidth + fieldGap

    if self.lootLedgerCharacterEditLabel then
      self.lootLedgerCharacterEditLabel:Hide()
    end
    if self.lootLedgerItemEditLabel then
      self.lootLedgerItemEditLabel:ClearAllPoints()
      self.lootLedgerItemEditLabel:SetPoint("TOPLEFT", 18, firstLabelTop)
      self.lootLedgerItemEditLabel:SetWidth(descriptionWidth)
    end
    if self.lootLedgerPointsEditLabel then
      self.lootLedgerPointsEditLabel:ClearAllPoints()
      self.lootLedgerPointsEditLabel:SetPoint("TOPLEFT", pointsX, firstLabelTop)
      self.lootLedgerPointsEditLabel:SetWidth(pointsEditWidth)
    end
    if self.lootLedgerCharacterEdit then
      self.lootLedgerCharacterEdit:Hide()
    end
    if self.lootLedgerItemEdit then
      SetControlWidth(self.lootLedgerItemEdit, descriptionWidth)
      self.lootLedgerItemEdit:ClearAllPoints()
      self.lootLedgerItemEdit:SetPoint("TOPLEFT", 18, firstInputTop)
    end
    if self.lootLedgerPointsEdit then
      SetControlWidth(self.lootLedgerPointsEdit, pointsEditWidth)
      self.lootLedgerPointsEdit:ClearAllPoints()
      self.lootLedgerPointsEdit:SetPoint("TOPLEFT", pointsX, firstInputTop)
    end

    self.lootLedgerAddEntryBtn:ClearAllPoints()
    self.lootLedgerAddEntryBtn:SetPoint("TOPLEFT", 18, firstInputTop - 32)
    if self.lootLedgerDeleteEntryBtn then
      self.lootLedgerDeleteEntryBtn:ClearAllPoints()
      self.lootLedgerDeleteEntryBtn:SetPoint("LEFT", self.lootLedgerAddEntryBtn, "RIGHT", 10, 0)
    end
    if self.lootLedgerDeleteCharacterBtn then
      self.lootLedgerDeleteCharacterBtn:ClearAllPoints()
      self.lootLedgerDeleteCharacterBtn:SetPoint("TOPLEFT", self.lootLedgerAddEntryBtn, "BOTTOMLEFT", 0, -34)
      self.lootLedgerDeleteCharacterBtn:Show()
    end
    self.lootLedgerFeedback:ClearAllPoints()
    if self.lootLedgerDeleteCharacterBtn then
      self.lootLedgerFeedback:SetPoint("TOPLEFT", self.lootLedgerDeleteCharacterBtn, "BOTTOMLEFT", 0, -8)
    else
      self.lootLedgerFeedback:SetPoint("TOPLEFT", self.lootLedgerAddEntryBtn, "BOTTOMLEFT", 0, -8)
    end
    self.lootLedgerFeedback:SetWidth(rightWidth)

    local contentHeight = math.abs(firstInputTop - 32) + 154
    self.lootLedgerRightContent:SetSize(contentWidth + 14, math.max(1, contentHeight))
    if self.lootLedgerRightScroll and self.lootLedgerRightScroll.UpdateScrollChildRect then
      self.lootLedgerRightScroll:UpdateScrollChildRect()
    end
  end

  return {
    EnsureLootLedgerHistoryRows = EnsureLootLedgerHistoryRows,
    EnsureLootLedgerMemberRows = EnsureLootLedgerMemberRows,
    ResolveLootLedgerEntryIcon = ResolveLootLedgerEntryIcon,
    BuildLootLedgerHistoryEntries = BuildLootLedgerHistoryEntries,
    FormatLootLedgerPointBehaviorLabel = FormatLootLedgerPointBehaviorLabel,
    BuildLootLedgerAwardInsight = BuildLootLedgerAwardInsight,
    BuildLootLedgerNeedDiceInsight = BuildLootLedgerNeedDiceInsight,
    BuildNeedDiceSummaryText = BuildNeedDiceSummaryText,
    BuildBisItemSummaryText = BuildBisItemSummaryText,
    ApplyLootLedgerTextureIcon = ApplyLootLedgerTextureIcon,
    UpdateLootLedgerDetailLayout = UpdateLootLedgerDetailLayout,
  }
end
